mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Comment-only enrichment across cordis/src/*.ts — Context, EventsService (+ the ctx merges), Fiber, RegistryService, ReflectService, Service, logger — so the website API generator can render a complete reference and hard-error on any future undocumented member (vendor sync included). Logged as local modification 6 in vendor/README.md; retire it when upstreamed to the fork. INHERITED_SERVICES/EVENTS source pointers refreshed for the shifted lines; cordis catalogs regenerated.
337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
import { defineProperty, Dict } from 'cosmokit'
|
|
import { StandardSchemaV1 } from '@standard-schema/spec'
|
|
import { Context } from './context.ts'
|
|
import { Fiber } from './fiber.ts'
|
|
import { buildOuterStack, DisposableList, symbols, withProps } from './utils.ts'
|
|
|
|
function isApplicable(object: Plugin) {
|
|
return object && typeof object === 'object' && typeof object.apply === 'function'
|
|
}
|
|
|
|
/**
|
|
* Service dependency declaration accepted by plugins and the `@Inject`
|
|
* decorator.
|
|
*
|
|
* Array form requests services without intercept config. Object form maps each
|
|
* service name to optional intercept config for the plugin context.
|
|
*/
|
|
export type Inject<M = Dict> = (keyof M)[] | { [K in keyof M]?: M[K] }
|
|
|
|
/** Context keys that correspond to services with typed intercept config. */
|
|
export type InjectKey = keyof {
|
|
[K in keyof Context & string as Context[K] extends { [symbols.config]: any } ? K : never]: any
|
|
}
|
|
|
|
/**
|
|
* Decorator for declaring service dependencies on classes or class methods.
|
|
*
|
|
* On classes it contributes to the plugin's static `inject` map. On methods it
|
|
* delays the method call until the declared services are available.
|
|
*/
|
|
/**
|
|
* @param name — the required service name.
|
|
* @param config — optional intercept config applied for that service.
|
|
* @returns the class or method decorator.
|
|
*/
|
|
export function Inject<K extends InjectKey>(name: K, config?: Context[K] extends { [symbols.config]: infer T } ? T : never) {
|
|
return function (value: any, decorator: ClassDecoratorContext<any> | ClassMethodDecoratorContext<any>) {
|
|
if (decorator.kind === 'class') {
|
|
if (!Object.hasOwn(value, 'inject')) {
|
|
defineProperty(value, 'inject', Object.create(Object.getPrototypeOf(value).inject ?? null))
|
|
defineProperty(value.inject, symbols.checkProto, true)
|
|
}
|
|
value.inject[name] = config
|
|
} else if (decorator.kind === 'method') {
|
|
const inject = (value[symbols.metadata] ??= {}).inject ??= Object.create(null)
|
|
inject[name] = config
|
|
decorator.addInitializer(function () {
|
|
const property = this[symbols.tracker]?.property
|
|
;(this[symbols.initHooks] ??= []).push(() => {
|
|
(this.ctx as Context).inject(inject, (ctx) => {
|
|
return value.call(property ? withProps(this, { [property]: ctx }) : this)
|
|
})
|
|
})
|
|
})
|
|
} else {
|
|
throw new Error('@Inject() can only be used on class or class methods')
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Utilities for normalizing plugin dependency declarations. */
|
|
export namespace Inject {
|
|
/**
|
|
* Convert array/object/class-inherited inject metadata into a plain map.
|
|
*
|
|
* @param inject — the declaration to normalize; `null`/`undefined` add nothing.
|
|
* @param result — the map to fill (service name → intercept config or `null`).
|
|
* @returns `result`.
|
|
*/
|
|
export function resolve(inject: Inject | null | undefined, result: Dict = Object.create(null)) {
|
|
if (!inject) return result
|
|
if (Array.isArray(inject)) {
|
|
for (const name of inject) {
|
|
result[name] = null
|
|
}
|
|
} else if (Reflect.has(inject, symbols.checkProto)) {
|
|
Object.assign(result, resolve(Object.getPrototypeOf(inject)))
|
|
for (const name of Object.keys(inject)) {
|
|
result[name] = inject[name] ?? null
|
|
}
|
|
} else {
|
|
for (const name of Object.keys(inject)) {
|
|
result[name] = inject[name] ?? null
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
|
|
/** Supported plugin entrypoint shapes. */
|
|
export type Plugin<T = any> =
|
|
| Plugin.Function<T>
|
|
| Plugin.Constructor<T>
|
|
| Plugin.Object<T>
|
|
|
|
/** Types associated with plugin entrypoints and runtime records. */
|
|
export namespace Plugin {
|
|
/** Shared metadata understood by the plugin registry and related tooling. */
|
|
export interface Base<T = any> {
|
|
/** Display name used for fiber diagnostics and logger names. */
|
|
name?: string
|
|
/** Standard-schema validator applied to config before the plugin starts. */
|
|
Config?: StandardSchemaV1<any, T>
|
|
/** Services the plugin requires; it only loads while all are available. */
|
|
inject?: Inject
|
|
/** Service name(s) the plugin provides (read by `Service` and by loaders). */
|
|
provide?: string | string[]
|
|
/** Service names whose intercept config the plugin declares it consumes. */
|
|
intercept?: Dict<boolean>
|
|
}
|
|
|
|
export interface Transform<S, T> {
|
|
/** Marks the transform object as a schema/config transform. */
|
|
schema?: true
|
|
/** Convert user-facing config to runtime config. */
|
|
Config: (config: S) => T
|
|
}
|
|
|
|
/** Function plugin called with `(ctx, config)`. */
|
|
export interface Function<T = any> extends Base<T> {
|
|
(ctx: Context, config: T): any
|
|
}
|
|
|
|
/** Class plugin constructed with `(ctx, config)`. */
|
|
export interface Constructor<T = any> extends Base<T> {
|
|
new (ctx: Context, config: T): any
|
|
}
|
|
|
|
/** Object plugin with an `apply(ctx, config)` method. */
|
|
export interface Object<T = any> extends Base<T> {
|
|
apply(ctx: Context, config: T): any
|
|
}
|
|
|
|
/** Mutable registry record shared by all fibers of one plugin callback. */
|
|
export interface Runtime {
|
|
/** Display name copied from the first registered plugin shape. */
|
|
name?: string
|
|
/** Every live fiber of this plugin (one per `ctx.plugin()` call). */
|
|
fibers: DisposableList<Fiber>
|
|
/** The executable entrypoint all fibers share (registry identity key). */
|
|
callback: globalThis.Function
|
|
/** Standard-schema validator applied to each fiber's config. */
|
|
Config?: StandardSchemaV1
|
|
}
|
|
}
|
|
|
|
type Spread<T> = undefined extends T ? [config?: T] : [config: T]
|
|
|
|
type GetPluginParameters<P> =
|
|
| P extends (ctx: Context, ...args: infer R) => any
|
|
? R
|
|
: P extends new (ctx: Context, ...args: infer R) => any
|
|
? R
|
|
: P extends { apply(ctx: Context, ...args: infer R): any }
|
|
? R
|
|
: never
|
|
|
|
type GetPluginConfig<P> =
|
|
| P extends Plugin.Transform<infer S, any>
|
|
? S
|
|
: GetPluginParameters<P>[0]
|
|
|
|
declare module './context.ts' {
|
|
export interface Context {
|
|
/**
|
|
* Run a callback once the requested services are available.
|
|
*
|
|
* Shorthand for `ctx.plugin({ inject, apply: callback })`: the callback
|
|
* is unloaded and re-run whenever a required service changes.
|
|
*
|
|
* @param deps — required services, as an array or a name → config map.
|
|
* @param callback — plugin body called with `(ctx, config)`.
|
|
* @returns the fiber; awaiting it settles once loading finished.
|
|
*/
|
|
inject(deps: Inject, callback: Plugin.Function<void>): Fiber & PromiseLike<Fiber>
|
|
/**
|
|
* Load a plugin in the current context.
|
|
*
|
|
* @param plugin — a function, class, or `{ apply }` object plugin.
|
|
* @param args — the plugin config, validated against its `Config` schema.
|
|
* @returns the fiber; awaiting it settles once loading finished
|
|
* (rejecting on config or startup errors).
|
|
*/
|
|
plugin<P extends Plugin>(plugin: P, ...args: Spread<GetPluginConfig<P>>): Fiber & PromiseLike<Fiber>
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Plugin registry installed as `ctx.registry` and mixed into every context.
|
|
*
|
|
* It normalizes plugin shapes, tracks plugin runtimes, starts fibers, and
|
|
* exposes map-like inspection over active plugin callbacks.
|
|
*/
|
|
export class RegistryService {
|
|
private _counter = 0
|
|
private _internal = new Map<Function, Plugin.Runtime>()
|
|
|
|
constructor(public ctx: Context) {
|
|
defineProperty(this, symbols.tracker, {
|
|
property: 'ctx',
|
|
noShadow: true,
|
|
})
|
|
}
|
|
|
|
/** Allocate the next fiber uid (increments on every read). */
|
|
get counter() {
|
|
return ++this._counter
|
|
}
|
|
|
|
/** Number of registered plugin runtimes. */
|
|
get size() {
|
|
return this._internal.size
|
|
}
|
|
|
|
/**
|
|
* Resolve a supported plugin shape to its executable callback.
|
|
*
|
|
* @param plugin — a function, class, or `{ apply }` object plugin.
|
|
* @returns the callback identifying the plugin, or `undefined` if invalid.
|
|
*/
|
|
resolve(plugin: Plugin): Function | undefined {
|
|
// plugin.apply may throw
|
|
try {
|
|
if (typeof plugin === 'function') return plugin
|
|
if (isApplicable(plugin)) return plugin.apply
|
|
} catch {}
|
|
}
|
|
|
|
/**
|
|
* Look up the runtime record for a plugin.
|
|
*
|
|
* @param plugin — any supported plugin shape.
|
|
* @returns the runtime, or `undefined` when the plugin is not registered.
|
|
*/
|
|
get(plugin: Plugin) {
|
|
const key = this.resolve(plugin)
|
|
return key && this._internal.get(key)
|
|
}
|
|
|
|
/**
|
|
* Check whether a plugin has a registered runtime.
|
|
*
|
|
* @param plugin — any supported plugin shape.
|
|
* @returns `true` when at least one fiber of the plugin exists.
|
|
*/
|
|
has(plugin: Plugin) {
|
|
const key = this.resolve(plugin)
|
|
return !!key && this._internal.has(key)
|
|
}
|
|
|
|
/**
|
|
* Dispose every running fiber for a plugin and remove its runtime record.
|
|
*
|
|
* @param plugin — any supported plugin shape.
|
|
* @returns the removed runtime, or `undefined` when none was registered.
|
|
*/
|
|
delete(plugin: Plugin) {
|
|
const key = this.resolve(plugin)
|
|
const runtime = key && this._internal.get(key)
|
|
if (!runtime) return
|
|
this._internal.delete(key)
|
|
for (const fiber of runtime.fibers) {
|
|
fiber.dispose()
|
|
}
|
|
return runtime
|
|
}
|
|
|
|
/** Iterate the registered plugin callbacks. */
|
|
keys() {
|
|
return this._internal.keys()
|
|
}
|
|
|
|
/** Iterate the registered plugin runtimes. */
|
|
values() {
|
|
return this._internal.values()
|
|
}
|
|
|
|
/** Iterate `[callback, runtime]` pairs. */
|
|
entries() {
|
|
return this._internal.entries()
|
|
}
|
|
|
|
/**
|
|
* Visit every registered runtime.
|
|
*
|
|
* @param callback — receives each runtime and its identifying callback.
|
|
*/
|
|
forEach(callback: (value: Plugin.Runtime, key: Function) => void) {
|
|
return this._internal.forEach(callback)
|
|
}
|
|
|
|
/**
|
|
* Start a callback once the requested dependencies are available.
|
|
*
|
|
* @param inject — required services, as an array or a name → config map.
|
|
* @param callback — plugin body called with `(ctx, config)`.
|
|
* @returns the fiber; awaiting it settles once loading finished.
|
|
*/
|
|
inject(inject: Inject, callback: Plugin.Function<void>) {
|
|
return this.plugin({ inject, apply: callback, name: callback.name })
|
|
}
|
|
|
|
/**
|
|
* Start a plugin in the current context and return its fiber.
|
|
*
|
|
* Creates (or reuses) the plugin's runtime record, then starts a new fiber
|
|
* under the current context. Throws if `plugin` is not a supported shape or
|
|
* if the current fiber is already disposed.
|
|
*
|
|
* @param plugin — a function, class, or `{ apply }` object plugin.
|
|
* @param config — the plugin config, validated against its `Config` schema.
|
|
* @param getOuterStack — captures the caller stack for effect diagnostics.
|
|
* @returns the fiber; awaiting it settles once loading finished.
|
|
*/
|
|
plugin(plugin: Plugin, config?: any, getOuterStack = buildOuterStack()) {
|
|
// check if it's a valid plugin
|
|
const callback = this.resolve(plugin)
|
|
if (!callback) throw new Error('invalid plugin, expect function or object with an "apply" method, received ' + typeof plugin)
|
|
this.ctx.fiber.assertActive()
|
|
|
|
let runtime = this._internal.get(callback)
|
|
if (!runtime) {
|
|
let name = plugin.name
|
|
if (name === 'apply') name = undefined
|
|
runtime = { name, callback, fibers: new DisposableList(), Config: plugin.Config }
|
|
this._internal.set(callback, runtime)
|
|
}
|
|
|
|
const fiber = new Fiber(this.ctx, config, Inject.resolve(plugin.inject), runtime, getOuterStack)
|
|
const wrapped = Object.create(fiber) as Fiber & PromiseLike<Fiber>
|
|
wrapped.then = (onFulfilled, onRejected) => {
|
|
return fiber.await().then(onFulfilled, onRejected)
|
|
}
|
|
return wrapped
|
|
}
|
|
}
|