Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
/**
|
|
* Fiber-state projection vocabulary and the kernel-owned status store for the
|
|
* boot loading page. The status AppRoot renders is a projection of the real
|
|
* cordis fiber states (display the truth, not a retelling) — the boot chain
|
|
* subscribes `internal/status` and recomputes one row per loader entry.
|
|
*
|
|
* The store is hand-rolled here because of the shell self-sufficiency rule:
|
|
* the snapshot-store machinery lives in the runtime PLUGIN
|
|
* package, and the shell kernel must not value-import any plugin package —
|
|
* the loading page has to work while (and especially when) plugins fail.
|
|
* @module @deepseek-ai/dsh-client-web/src/loader-status
|
|
*/
|
|
import type { FiberState } from '@deepseek-ai/cordis'
|
|
|
|
/**
|
|
* Value mirror of cordis's `FiberState` const enum: a const enum has no
|
|
* runtime object to import (and esbuild-based pipelines cannot inline it
|
|
* across modules), so these values mirror the pinned vendored definition
|
|
* while retaining its type (same rationale as dsh-tool-cordis's mirror).
|
|
*/
|
|
export const FIBER_STATE = {
|
|
PENDING: 0 as FiberState.PENDING,
|
|
LOADING: 1 as FiberState.LOADING,
|
|
ACTIVE: 2 as FiberState.ACTIVE,
|
|
FAILED: 3 as FiberState.FAILED,
|
|
DISPOSED: 4 as FiberState.DISPOSED,
|
|
UNLOADING: 5 as FiberState.UNLOADING,
|
|
} as const
|
|
|
|
/** One entry's projected state label (lower-case face of {@link FiberState}). */
|
|
export type LoaderEntryState = 'pending' | 'loading' | 'active' | 'failed' | 'disposed' | 'unloading'
|
|
|
|
/** Label for each fiber state, keyed by member (inlining-safe — no reverse mapping). */
|
|
export const STATE_LABELS: Record<FiberState, LoaderEntryState> = {
|
|
[FIBER_STATE.PENDING]: 'pending',
|
|
[FIBER_STATE.LOADING]: 'loading',
|
|
[FIBER_STATE.ACTIVE]: 'active',
|
|
[FIBER_STATE.FAILED]: 'failed',
|
|
[FIBER_STATE.DISPOSED]: 'disposed',
|
|
[FIBER_STATE.UNLOADING]: 'unloading',
|
|
}
|
|
|
|
/** Per-entry state projection (AppRoot's status feed), keyed by entry name. */
|
|
export type LoaderStatus = Record<string, LoaderEntryState>
|
|
|
|
/** Minimal observable snapshot the kernel components consume (useSyncExternalStore shape). */
|
|
export interface KernelSignal<T> {
|
|
/** Current value (stable reference between changes). */
|
|
getSnapshot: () => T
|
|
/**
|
|
* Subscribe to changes.
|
|
* @param fn - change listener.
|
|
* @returns the unsubscribe disposer.
|
|
*/
|
|
subscribe: (fn: () => void) => () => void
|
|
}
|
|
|
|
/** Writable one-value signal (settled flag, boot failure report). */
|
|
export interface KernelValueSignal<T> extends KernelSignal<T> {
|
|
/**
|
|
* Publish a new value and notify subscribers.
|
|
* @param next - the new value.
|
|
*/
|
|
set: (next: T) => void
|
|
}
|
|
|
|
/**
|
|
* Create a writable kernel signal.
|
|
* @param init - initial value.
|
|
* @returns the signal.
|
|
*/
|
|
export function createSignal<T>(init: T): KernelValueSignal<T> {
|
|
let value = init
|
|
const listeners = new Set<() => void>()
|
|
return {
|
|
getSnapshot: () => value,
|
|
subscribe: (fn) => { listeners.add(fn); return () => { listeners.delete(fn) } },
|
|
set: (next) => {
|
|
value = next
|
|
for (const fn of [...listeners]) fn()
|
|
},
|
|
}
|
|
}
|
|
|
|
/** The boot status store: per-entry rows over a {@link KernelSignal} face. */
|
|
export interface LoaderStatusStore extends KernelSignal<LoaderStatus> {
|
|
/**
|
|
* Project one entry's state (copy-on-write so getSnapshot references only
|
|
* change on writes — useSyncExternalStore contract).
|
|
* @param id - entry name.
|
|
* @param state - projected fiber state.
|
|
*/
|
|
set: (id: string, state: LoaderEntryState) => void
|
|
}
|
|
|
|
/**
|
|
* Create the boot status store.
|
|
* @returns the store (empty until the boot chain projects rows).
|
|
*/
|
|
export function createLoaderStatusStore(): LoaderStatusStore {
|
|
let value: LoaderStatus = {}
|
|
const listeners = new Set<() => void>()
|
|
return {
|
|
getSnapshot: () => value,
|
|
subscribe: (fn) => { listeners.add(fn); return () => { listeners.delete(fn) } },
|
|
set: (id, state) => {
|
|
value = { ...value, [id]: state }
|
|
for (const fn of [...listeners]) fn()
|
|
},
|
|
}
|
|
}
|