Files
deepseek-harness/packages/client/web/src/loader-status.ts
T
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
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.
2026-08-10 22:04:13 +08:00

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()
},
}
}