refactor: unwrap PluginMeta namespace + self-reexport (#22945)
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user