refactor: unwrap PluginMeta namespace + self-reexport (#22945)

This commit is contained in:
Kit Langton
2026-04-16 20:02:05 -04:00
committed by GitHub
parent fb02744460
commit 974fa1b8b1

View File

@@ -8,17 +8,16 @@ import { Flock } from "@opencode-ai/shared/util/flock"
import { parsePluginSpecifier, pluginSource } from "./shared" import { parsePluginSpecifier, pluginSource } from "./shared"
export namespace PluginMeta { type Source = "file" | "npm"
type Source = "file" | "npm"
export type Theme = { export type Theme = {
src: string src: string
dest: string dest: string
mtime?: number mtime?: number
size?: number size?: number
} }
export type Entry = { export type Entry = {
id: string id: string
source: Source source: Source
spec: string spec: string
@@ -32,56 +31,56 @@ export namespace PluginMeta {
load_count: number load_count: number
fingerprint: string fingerprint: string
themes?: Record<string, Theme> themes?: Record<string, Theme>
} }
export type State = "first" | "updated" | "same" export type State = "first" | "updated" | "same"
export type Touch = { export type Touch = {
spec: string spec: string
target: string target: string
id: string id: string
} }
type Store = Record<string, Entry> type Store = Record<string, Entry>
type Core = Omit<Entry, "first_time" | "last_time" | "time_changed" | "load_count" | "fingerprint" | "themes"> type Core = Omit<Entry, "first_time" | "last_time" | "time_changed" | "load_count" | "fingerprint" | "themes">
type Row = Touch & { core: Core } type Row = Touch & { core: Core }
function storePath() { function storePath() {
return Flag.OPENCODE_PLUGIN_META_FILE ?? path.join(Global.Path.state, "plugin-meta.json") return Flag.OPENCODE_PLUGIN_META_FILE ?? path.join(Global.Path.state, "plugin-meta.json")
} }
function lock(file: string) { function lock(file: string) {
return `plugin-meta:${file}` return `plugin-meta:${file}`
} }
function fileTarget(spec: string, target: string) { function fileTarget(spec: string, target: string) {
if (spec.startsWith("file://")) return fileURLToPath(spec) if (spec.startsWith("file://")) return fileURLToPath(spec)
if (target.startsWith("file://")) return fileURLToPath(target) if (target.startsWith("file://")) return fileURLToPath(target)
return return
} }
async function modifiedAt(file: string) { async function modifiedAt(file: string) {
const stat = await Filesystem.statAsync(file) const stat = await Filesystem.statAsync(file)
if (!stat) return if (!stat) return
const mtime = stat.mtimeMs const mtime = stat.mtimeMs
return Math.floor(typeof mtime === "bigint" ? Number(mtime) : mtime) return Math.floor(typeof mtime === "bigint" ? Number(mtime) : mtime)
} }
function resolvedTarget(target: string) { function resolvedTarget(target: string) {
if (target.startsWith("file://")) return fileURLToPath(target) if (target.startsWith("file://")) return fileURLToPath(target)
return target return target
} }
async function npmVersion(target: string) { async function npmVersion(target: string) {
const resolved = resolvedTarget(target) const resolved = resolvedTarget(target)
const stat = await Filesystem.statAsync(resolved) const stat = await Filesystem.statAsync(resolved)
const dir = stat?.isDirectory() ? resolved : path.dirname(resolved) const dir = stat?.isDirectory() ? resolved : path.dirname(resolved)
return Filesystem.readJson<{ version?: string }>(path.join(dir, "package.json")) return Filesystem.readJson<{ version?: string }>(path.join(dir, "package.json"))
.then((item) => item.version) .then((item) => item.version)
.catch(() => undefined) .catch(() => undefined)
} }
async function entryCore(item: Touch): Promise<Core> { async function entryCore(item: Touch): Promise<Core> {
const spec = item.spec const spec = item.spec
const target = item.target const target = item.target
const source = pluginSource(spec) const source = pluginSource(spec)
@@ -104,25 +103,25 @@ export namespace PluginMeta {
requested: parsePluginSpecifier(spec).version, requested: parsePluginSpecifier(spec).version,
version: await npmVersion(target), version: await npmVersion(target),
} }
} }
function fingerprint(value: Core) { function fingerprint(value: Core) {
if (value.source === "file") return [value.target, value.modified ?? ""].join("|") if (value.source === "file") return [value.target, value.modified ?? ""].join("|")
return [value.target, value.requested ?? "", value.version ?? ""].join("|") return [value.target, value.requested ?? "", value.version ?? ""].join("|")
} }
async function read(file: string): Promise<Store> { async function read(file: string): Promise<Store> {
return Filesystem.readJson<Store>(file).catch(() => ({}) as Store) return Filesystem.readJson<Store>(file).catch(() => ({}) as Store)
} }
async function row(item: Touch): Promise<Row> { async function row(item: Touch): Promise<Row> {
return { return {
...item, ...item,
core: await entryCore(item), core: await entryCore(item),
} }
} }
function next(prev: Entry | undefined, core: Core, now: number): { state: State; entry: Entry } { function next(prev: Entry | undefined, core: Core, now: number): { state: State; entry: Entry } {
const entry: Entry = { const entry: Entry = {
...core, ...core,
first_time: prev?.first_time ?? now, first_time: prev?.first_time ?? now,
@@ -138,9 +137,9 @@ export namespace PluginMeta {
state, state,
entry, entry,
} }
} }
export async function touchMany(items: Touch[]): Promise<Array<{ state: State; entry: Entry }>> { export async function touchMany(items: Touch[]): Promise<Array<{ state: State; entry: Entry }>> {
if (!items.length) return [] if (!items.length) return []
const file = storePath() const file = storePath()
const rows = await Promise.all(items.map((item) => row(item))) const rows = await Promise.all(items.map((item) => row(item)))
@@ -157,17 +156,17 @@ export namespace PluginMeta {
await Filesystem.writeJson(file, store) await Filesystem.writeJson(file, store)
return out return out
}) })
} }
export async function touch(spec: string, target: string, id: string): Promise<{ state: State; entry: Entry }> { export async function touch(spec: string, target: string, id: string): Promise<{ state: State; entry: Entry }> {
return touchMany([{ spec, target, id }]).then((item) => { return touchMany([{ spec, target, id }]).then((item) => {
const hit = item[0] const hit = item[0]
if (hit) return hit if (hit) return hit
throw new Error("Failed to touch plugin metadata.") throw new Error("Failed to touch plugin metadata.")
}) })
} }
export async function setTheme(id: string, name: string, theme: Theme): Promise<void> { export async function setTheme(id: string, name: string, theme: Theme): Promise<void> {
const file = storePath() const file = storePath()
await Flock.withLock(lock(file), async () => { await Flock.withLock(lock(file), async () => {
const store = await read(file) const store = await read(file)
@@ -179,10 +178,11 @@ export namespace PluginMeta {
} }
await Filesystem.writeJson(file, store) await Filesystem.writeJson(file, store)
}) })
} }
export async function list(): Promise<Store> { export async function list(): Promise<Store> {
const file = storePath() const file = storePath()
return Flock.withLock(lock(file), async () => read(file)) return Flock.withLock(lock(file), async () => read(file))
}
} }
export * as PluginMeta from "./meta"