vendor(cordis): document the full plugin-author surface (@param/@returns everywhere)
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.
This commit is contained in:
Vendored
+107
-10
@@ -7,6 +7,7 @@ import { StandardSchemaV1 } from '@standard-schema/spec'
|
||||
|
||||
declare module './context.ts' {
|
||||
export interface Context extends Pick<Fiber, 'effect'> {
|
||||
/** The fiber (plugin runtime instance) that owns this context. */
|
||||
fiber: Fiber
|
||||
}
|
||||
}
|
||||
@@ -17,6 +18,11 @@ const kValidationError = Symbol.for('ValidationError')
|
||||
export class ValidationError extends TypeError {
|
||||
name = 'ValidationError'
|
||||
|
||||
/**
|
||||
* Build the aggregated message from schema issues.
|
||||
*
|
||||
* @param issues — the standard-schema issues, one message line each.
|
||||
*/
|
||||
constructor(issues: readonly StandardSchemaV1.Issue[]) {
|
||||
super(`invalid config:\n` + issues.map(issue => {
|
||||
if (issue.path) {
|
||||
@@ -32,7 +38,14 @@ Object.defineProperty(ValidationError.prototype, kValidationError, {
|
||||
value: true,
|
||||
})
|
||||
|
||||
/** Validate and normalize config for a plugin runtime before it starts. */
|
||||
/**
|
||||
* Validate and normalize config for a plugin runtime before it starts.
|
||||
*
|
||||
* @param runtime — the plugin runtime whose `Config` schema to apply.
|
||||
* @param config — the raw user config.
|
||||
* @returns the validated config, or `config` unchanged if the runtime has no schema.
|
||||
* @throws {ValidationError} when validation reports issues.
|
||||
*/
|
||||
export function resolveConfig(runtime: Plugin.Runtime, config: any) {
|
||||
if (!runtime.Config) return config
|
||||
// TODO: async validation
|
||||
@@ -51,10 +64,21 @@ interface AsyncDisposable<T extends Awaitable<void> = Awaitable<void>> extends P
|
||||
(): T
|
||||
}
|
||||
|
||||
/** Function returned by an effect to release resources during disposal. */
|
||||
/**
|
||||
* Function returned by an effect to release resources during disposal.
|
||||
*
|
||||
* Disposers run in reverse registration order when the owning fiber unloads;
|
||||
* they may be async, in which case unloading awaits them.
|
||||
*/
|
||||
export type Disposable<T = any> = () => T
|
||||
|
||||
/** Effect body result accepted by `ctx.effect()` and plugin startup. */
|
||||
/**
|
||||
* Effect body result accepted by `ctx.effect()` and plugin startup.
|
||||
*
|
||||
* Either a single disposer, a promise of one, or a (possibly async) iterable
|
||||
* yielding several — generator effects register each yielded disposer as it
|
||||
* is produced.
|
||||
*/
|
||||
export type Effect<T = any> =
|
||||
| SyncEffect<T>
|
||||
| AsyncEffect<T>
|
||||
@@ -69,7 +93,9 @@ type AsyncEffect<T = any> =
|
||||
|
||||
/** Tree node used to expose nested effect labels for diagnostics. */
|
||||
export interface EffectMeta {
|
||||
/** Human-readable effect label, e.g. `ctx.on("event")` or `ctx.provide("name")`. */
|
||||
label: string
|
||||
/** Metadata of nested effects registered while this effect ran. */
|
||||
children: EffectMeta[]
|
||||
}
|
||||
|
||||
@@ -80,7 +106,14 @@ interface EffectRunner<T> {
|
||||
getOuterStack: () => string[]
|
||||
}
|
||||
|
||||
/** Lifecycle state for one plugin fiber. */
|
||||
/**
|
||||
* Lifecycle state for one plugin fiber.
|
||||
*
|
||||
* `PENDING` — waiting for required services; `LOADING` — the plugin callback
|
||||
* is running; `ACTIVE` — loaded and providing; `FAILED` — the callback or its
|
||||
* config threw; `UNLOADING` — disposers are running; `DISPOSED` — the fiber
|
||||
* was removed and cannot restart.
|
||||
*/
|
||||
export const enum FiberState {
|
||||
PENDING,
|
||||
LOADING,
|
||||
@@ -92,6 +125,10 @@ export const enum FiberState {
|
||||
|
||||
/** Framework error with a stable machine-readable code. */
|
||||
export class CordisError extends Error {
|
||||
/**
|
||||
* @param code — the stable error code; also the default message.
|
||||
* @param message — optional human-readable override.
|
||||
*/
|
||||
constructor(public code: CordisError.Code, message?: string) {
|
||||
super(message ?? CordisError.Code[code])
|
||||
}
|
||||
@@ -115,12 +152,19 @@ const INACTIVE = '__INACTIVE__'
|
||||
* cleanup for the plugin context returned by `ctx.plugin()`.
|
||||
*/
|
||||
export class Fiber {
|
||||
/** Unique id within the registry; 0 for the root fiber, `null` once disposed. */
|
||||
public uid: number | null
|
||||
/** The context this fiber's plugin runs in (extends the parent context). */
|
||||
public readonly ctx: Context
|
||||
/** The validated plugin config (updated by `update()`). */
|
||||
public config: any
|
||||
/** Current lifecycle state; transitions emit `internal/status`. */
|
||||
public state = FiberState.PENDING
|
||||
/** Dispose this fiber: unload the plugin, then settle once cleanup finished. */
|
||||
public readonly dispose: () => Promise<void>
|
||||
/** Snapshot of required service implementations while loaded; `undefined` otherwise. */
|
||||
public store: Dict<Impl> | undefined
|
||||
/** The in-flight load/unload transition, if one is currently running. */
|
||||
public inertia: Promise<void> | undefined
|
||||
|
||||
public readonly _hooks: Dict<DisposableList<Function>> = Object.create(null)
|
||||
@@ -133,6 +177,16 @@ export class Fiber {
|
||||
private _runner: EffectRunner<string>
|
||||
private _store: Dict<Impl> = Object.create(null)
|
||||
|
||||
/**
|
||||
* Create a fiber. Plugin authors normally obtain fibers from `ctx.plugin()`
|
||||
* rather than constructing them directly.
|
||||
*
|
||||
* @param parent — the context the plugin was loaded from.
|
||||
* @param config — raw config, validated against the runtime's schema.
|
||||
* @param inject — resolved dependency map (service name → intercept config).
|
||||
* @param runtime — the shared plugin runtime, or `null` for the root fiber.
|
||||
* @param getOuterStack — captures the caller stack for effect diagnostics.
|
||||
*/
|
||||
constructor(
|
||||
public parent: Context,
|
||||
config: any,
|
||||
@@ -226,6 +280,7 @@ export class Fiber {
|
||||
}
|
||||
}
|
||||
|
||||
/** The plugin's display name, inherited from the nearest named ancestor, else `'root'`. */
|
||||
get name() {
|
||||
let fiber: Fiber = this
|
||||
do {
|
||||
@@ -235,7 +290,12 @@ export class Fiber {
|
||||
return 'root'
|
||||
}
|
||||
|
||||
/** Throw if the fiber has already been disposed. */
|
||||
/**
|
||||
* Throw if the fiber has already been disposed.
|
||||
*
|
||||
* @returns nothing when the fiber is still active.
|
||||
* @throws {CordisError} `INACTIVE_EFFECT` when the fiber's uid has been cleared.
|
||||
*/
|
||||
assertActive() {
|
||||
if (this.uid !== null) return
|
||||
throw new CordisError('INACTIVE_EFFECT')
|
||||
@@ -287,8 +347,21 @@ export class Fiber {
|
||||
}, runner.getOuterStack)
|
||||
}
|
||||
|
||||
/** Register a cleanup-aware effect on this fiber. */
|
||||
/**
|
||||
* Register a cleanup-aware effect on this fiber.
|
||||
*
|
||||
* `execute` runs immediately; the disposers it produces are collected and
|
||||
* run (in reverse order) either when the returned disposer is called or
|
||||
* when the fiber unloads, whichever comes first. Calling the disposer twice
|
||||
* is a no-op. Throws `CordisError('INACTIVE_EFFECT')` if the fiber is
|
||||
* already disposed, and `TypeError` if `execute` returns an invalid shape.
|
||||
*
|
||||
* @param execute — the effect body; see {@link Effect} for accepted shapes.
|
||||
* @param label — effect label shown in `getEffects()` diagnostics.
|
||||
* @returns a disposer that tears the effect down and settles once done.
|
||||
*/
|
||||
effect(execute: () => SyncEffect, label?: string): Disposable<Promise<void>>
|
||||
/** Same as above for async effects; the disposer is also awaitable. */
|
||||
effect(execute: () => Effect, label?: string): AsyncDisposable<Promise<void>>
|
||||
effect(execute: () => Effect, label = 'anonymous'): any {
|
||||
this.assertActive()
|
||||
@@ -355,7 +428,11 @@ export class Fiber {
|
||||
return wrapper
|
||||
}
|
||||
|
||||
/** Return metadata for currently registered effects. */
|
||||
/**
|
||||
* Return metadata for currently registered effects.
|
||||
*
|
||||
* @returns one {@link EffectMeta} tree per labeled live effect.
|
||||
*/
|
||||
getEffects() {
|
||||
return [...this._disposables]
|
||||
.map<EffectMeta>(dispose => dispose[symbols.effect])
|
||||
@@ -474,7 +551,12 @@ export class Fiber {
|
||||
})
|
||||
}
|
||||
|
||||
/** Wait for current lifecycle work and rethrow startup errors. */
|
||||
/**
|
||||
* Wait for current lifecycle work and rethrow startup errors.
|
||||
*
|
||||
* @returns this fiber, once it has settled into a stable state.
|
||||
* @throws the config-validation or plugin-startup error, if any.
|
||||
*/
|
||||
async await() {
|
||||
while (this.inertia) {
|
||||
await this.inertia
|
||||
@@ -483,7 +565,12 @@ export class Fiber {
|
||||
return this
|
||||
}
|
||||
|
||||
/** Dispose and immediately reload this plugin with its current config. */
|
||||
/**
|
||||
* Dispose and immediately reload this plugin with its current config.
|
||||
*
|
||||
* @returns a promise resolving once the reload settled.
|
||||
* @throws {CordisError} `INACTIVE_EFFECT` when the fiber is already disposed.
|
||||
*/
|
||||
async restart() {
|
||||
this.assertActive()
|
||||
this._setEpoch(INACTIVE)
|
||||
@@ -491,7 +578,17 @@ export class Fiber {
|
||||
await this.await()
|
||||
}
|
||||
|
||||
/** Validate and apply new config, then restart the plugin. */
|
||||
/**
|
||||
* Validate and apply new config, then restart the plugin.
|
||||
*
|
||||
* Runs the `internal/update` waterfall first, so update hooks (and HMR)
|
||||
* can veto or replace the restart.
|
||||
*
|
||||
* @param config — the new raw config; validated before anything restarts.
|
||||
* @param noSave — hint for persistence hooks not to write the change back.
|
||||
* @returns nothing; the restart runs behind the `internal/update` waterfall.
|
||||
* @throws {ValidationError} when the new config fails validation.
|
||||
*/
|
||||
update(config: any, noSave = false) {
|
||||
this.assertActive()
|
||||
config = resolveConfig(this.runtime!, config)
|
||||
|
||||
Reference in New Issue
Block a user