@deepseek-ai/dsh-client-ui-slots 0.0.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +35 -0
- package/README.zh.md +35 -0
- package/lib/index.js +292 -0
- package/lib/invariant.js +25 -0
- package/lib/types/index.d.ts +601 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/renderer.d.ts +183 -0
- package/lib/types/store.d.ts +111 -0
- package/package.json +43 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/** React-free contracts between the slot host and an installed renderer. */
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { SlotEntryDef, SlotSpec, StoredEntry, Translate } from './index.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The locale face the render machinery consumes: namespace binding plus an
|
|
6
|
+
* observable revision (getSnapshot/subscribe pair — the same HostObservable
|
|
7
|
+
* currency as every other standard-kit source). The revision moves on every
|
|
8
|
+
* active-locale or registry change; the renderer re-derives each entry's `t`
|
|
9
|
+
* from (namespace, revision), so a locale switch hands out NEW function
|
|
10
|
+
* references and memoized components re-render naturally. Implemented by the
|
|
11
|
+
* locale plugin, installed through the runtime SlotsService (installLocale).
|
|
12
|
+
* Install before the first render that needs the seat: outlets bind their
|
|
13
|
+
* revision subscription at mount, and a face appearing later has no channel
|
|
14
|
+
* to notify already-mounted outlets (the locale plugin is immediately-tier
|
|
15
|
+
* infrastructure, so normal compositions install during boot).
|
|
16
|
+
*/
|
|
17
|
+
export interface LocaleFace extends HostObservable<{
|
|
18
|
+
revision: number;
|
|
19
|
+
}> {
|
|
20
|
+
/**
|
|
21
|
+
* Bind a namespace to a translate function reading the active locale at
|
|
22
|
+
* call time. Identity may be stable per namespace — freshness of rendered
|
|
23
|
+
* text is carried by the renderer's (ns, revision) seat derivation, not by
|
|
24
|
+
* this binding.
|
|
25
|
+
* @param ns - dictionary namespace.
|
|
26
|
+
* @returns the namespace-bound translate function.
|
|
27
|
+
*/
|
|
28
|
+
bind(ns: string): Translate;
|
|
29
|
+
}
|
|
30
|
+
/** Minimal observable surface for host-provided standard-kit data sources. */
|
|
31
|
+
export interface HostObservable<T> {
|
|
32
|
+
getSnapshot(): T;
|
|
33
|
+
subscribe(fn: () => void): () => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Type-erased store instance face at the render boundary (the typed twin is
|
|
37
|
+
* {@link StoreInstance}): a bare snapshot source plus the draft-stripped
|
|
38
|
+
* action callbacks. No React hook crosses this boundary — the render machinery
|
|
39
|
+
* binds `useStore` from the source at its own side (cached per instance);
|
|
40
|
+
* typing lands at the component boundary via {@link PropsStore}.
|
|
41
|
+
*/
|
|
42
|
+
export interface StoreInstanceLike {
|
|
43
|
+
getSnapshot(): unknown;
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to state changes (uSES subscribe side).
|
|
46
|
+
* @param fn - change callback.
|
|
47
|
+
* @returns unsubscribe.
|
|
48
|
+
*/
|
|
49
|
+
subscribe(fn: () => void): () => void;
|
|
50
|
+
readonly actions: Record<string, (...params: never[]) => void>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Per-session standard props resolved per session id (identity-stable per
|
|
54
|
+
* session scope; a recreated scope yields a new info). Plugins contribute
|
|
55
|
+
* members through the runtime `sessions.provide` contract; the render side binds
|
|
56
|
+
* every `hooks` source into a `use<Name>` selector hook (hooks never appear
|
|
57
|
+
* on the host contract) and spreads `props` verbatim. The runtime itself
|
|
58
|
+
* contributes the first entry (`'session'` → `useSession`).
|
|
59
|
+
*/
|
|
60
|
+
export interface SessionMaybeProvideInfo {
|
|
61
|
+
/** Current session id, absent while the application is in no-session mode. */
|
|
62
|
+
sessionId: string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Static hook roster. Each value is absent with the session; keys remain so
|
|
65
|
+
* session-maybe entries always receive the same hook-shaped standard kit.
|
|
66
|
+
*/
|
|
67
|
+
hooks: Record<string, HostObservable<unknown> | undefined>;
|
|
68
|
+
/** Static plain-member roster; values are undefined with the session. */
|
|
69
|
+
props: Record<string, unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Key-addressed projection value sources (the useProjection framework seat;
|
|
72
|
+
* session-projection subsystem page: docs/subsystems/session-projection.md).
|
|
73
|
+
* Unlike `hooks`, the key space is open — values
|
|
74
|
+
* arrive from host-computed push frames — so the render side binds per
|
|
75
|
+
* resolved key instead of per static roster member. Faces are always
|
|
76
|
+
* defined per key (absence is an `undefined` snapshot); the whole member is
|
|
77
|
+
* absent with the session.
|
|
78
|
+
*/
|
|
79
|
+
projections?: {
|
|
80
|
+
faceOf(key: string): HostObservable<unknown>;
|
|
81
|
+
} | undefined;
|
|
82
|
+
}
|
|
83
|
+
/** Definite per-session standard props resolved for strict session slots. */
|
|
84
|
+
export interface SessionProvideInfo extends SessionMaybeProvideInfo {
|
|
85
|
+
sessionId: string;
|
|
86
|
+
/** Bare observable sources, keyed by hook base name ('session' → useSession). */
|
|
87
|
+
hooks: Record<string, HostObservable<unknown>>;
|
|
88
|
+
}
|
|
89
|
+
/** renderSlot dispatch options at the machinery level. */
|
|
90
|
+
export interface RenderOpts {
|
|
91
|
+
entryKey?: string;
|
|
92
|
+
only?: string;
|
|
93
|
+
fallback?: ReactNode;
|
|
94
|
+
/** Opaque occurrence context consumed only by function-valued injected Hooks. */
|
|
95
|
+
hookContext?: unknown;
|
|
96
|
+
}
|
|
97
|
+
/** Host surface the runtime SlotsService presents to the installed renderer. */
|
|
98
|
+
export interface SlotRendererHost {
|
|
99
|
+
/**
|
|
100
|
+
* Subscribe to a key's registration changes (microtask-batched).
|
|
101
|
+
* @param key - slot key.
|
|
102
|
+
* @param fn - change callback.
|
|
103
|
+
* @returns unsubscribe.
|
|
104
|
+
*/
|
|
105
|
+
subscribe(key: string, fn: () => void): () => void;
|
|
106
|
+
/**
|
|
107
|
+
* Monotonic version for uSES pairing.
|
|
108
|
+
* @param key - slot key.
|
|
109
|
+
* @returns current version.
|
|
110
|
+
*/
|
|
111
|
+
getVersion(key: string): number;
|
|
112
|
+
/**
|
|
113
|
+
* Snapshot the registered entries for a key (stable reference between mutations).
|
|
114
|
+
* @param key - slot key.
|
|
115
|
+
* @returns entries in registration (list: order) sequence.
|
|
116
|
+
*/
|
|
117
|
+
entriesOf(key: string): readonly StoredEntry[];
|
|
118
|
+
/**
|
|
119
|
+
* Declared runtime spec from the declarations ledger.
|
|
120
|
+
* @param key - slot key.
|
|
121
|
+
* @returns the spec, or undefined while the key is undeclared (outlets render empty).
|
|
122
|
+
*/
|
|
123
|
+
specOf(key: string): SlotSpec<SlotEntryDef> | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* Stale-authorization check: whether the entry is still in the ledger.
|
|
126
|
+
* @param entry - a previously rendered entry.
|
|
127
|
+
* @returns false once the entry's registration was disposed.
|
|
128
|
+
*/
|
|
129
|
+
isLive(entry: StoredEntry): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Resolve (create or return cached) the store instance for an entry's
|
|
132
|
+
* declared handle under a scope key; lifecycle rides the ledger axis.
|
|
133
|
+
* @param entry - entry whose declaration carries the handle.
|
|
134
|
+
* @param scopeKey - session id for session-scope slots, undefined for root scope.
|
|
135
|
+
* @returns the instance, or undefined when the entry declares no store.
|
|
136
|
+
*/
|
|
137
|
+
storeOf(entry: StoredEntry, scopeKey: string | undefined): StoreInstanceLike | undefined;
|
|
138
|
+
/** Session-side standard-kit sources. */
|
|
139
|
+
sessions: {
|
|
140
|
+
/** Session list source backing the useSessions standard hook. */
|
|
141
|
+
list: HostObservable<unknown>;
|
|
142
|
+
/**
|
|
143
|
+
* Atomic current-session provide projection used by SessionProvider:
|
|
144
|
+
* selection changes and provider-roster changes publish through this one
|
|
145
|
+
* source, so a stable current id cannot strand mounted entries on an
|
|
146
|
+
* obsolete hook/prop schema. Carries the static roster with sessionId
|
|
147
|
+
* undefined while no current session resolves.
|
|
148
|
+
*/
|
|
149
|
+
provideInfo: HostObservable<SessionMaybeProvideInfo>;
|
|
150
|
+
};
|
|
151
|
+
/** Workspace-side standard-kit sources. */
|
|
152
|
+
workspaces: {
|
|
153
|
+
/** Workspace list source backing the useWorkspaces standard hook. */
|
|
154
|
+
list: HostObservable<unknown>;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Installed locale face backing the `t` standard seat (absent until the
|
|
158
|
+
* locale plugin installs one; rendering an entry that declared `locale:`
|
|
159
|
+
* without it is an assembly failure).
|
|
160
|
+
*/
|
|
161
|
+
locale?: LocaleFace | undefined;
|
|
162
|
+
}
|
|
163
|
+
/** The installation contract: runtime owns install()/renderSlot(); web-react implements rendering. */
|
|
164
|
+
export interface SlotRenderer {
|
|
165
|
+
/**
|
|
166
|
+
* Render the root slot tree over the host surface (the only ctx-level entry).
|
|
167
|
+
* @param host - the installing service's host surface.
|
|
168
|
+
* @param ownerProps - owner props from the shell's renderSlot('root', ...) call.
|
|
169
|
+
* @returns the rendered tree.
|
|
170
|
+
*/
|
|
171
|
+
renderRoot(host: SlotRendererHost, ownerProps: object): ReactNode;
|
|
172
|
+
}
|
|
173
|
+
/** Thrown when a retained renderSlot binding is invoked after its declaring entry was disposed. */
|
|
174
|
+
export declare class StaleAuthorizationError extends Error {
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Thrown when a renderSlot binding is invoked for a key outside its entry's
|
|
178
|
+
* children declaration (plain-JS backstop; typed callers are narrowed
|
|
179
|
+
* statically).
|
|
180
|
+
*/
|
|
181
|
+
export declare class SlotOwnershipError extends Error {
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** Framework-neutral store contracts for slot registrations and the runtime engine. */
|
|
2
|
+
/**
|
|
3
|
+
* Typed selector hook over a snapshot source. Canonical shape for the whole
|
|
4
|
+
* slot system (web-react's engine hook is structurally identical; the
|
|
5
|
+
* framework is the only party that ever constructs one).
|
|
6
|
+
*/
|
|
7
|
+
export type SnapshotSelectorHook<T> = <S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean) => S;
|
|
8
|
+
/**
|
|
9
|
+
* Selector hook over a source that follows the current session. The hook is
|
|
10
|
+
* always present, while its selected value is absent whenever no session is
|
|
11
|
+
* current. This keeps hook call sites stable across no-session/session
|
|
12
|
+
* transitions without pretending that a session snapshot exists.
|
|
13
|
+
*/
|
|
14
|
+
export type MaybeSnapshotSelectorHook<T> = <S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean) => S | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Action declaration table: pure immer-draft transforms over the store state,
|
|
17
|
+
* declared as the store's complete write set (the audit face — components can
|
|
18
|
+
* only write through these).
|
|
19
|
+
*/
|
|
20
|
+
export type ActionsDecl<T> = Record<string, (draft: T, ...params: any[]) => void>;
|
|
21
|
+
/**
|
|
22
|
+
* Draft-stripped callback form of an actions table: what components
|
|
23
|
+
* (`props.actions`) and inject factories receive — the framework bakes the
|
|
24
|
+
* draft parameter away by binding each action to the resolved instance.
|
|
25
|
+
*/
|
|
26
|
+
export type BakedActions<T, A extends ActionsDecl<T>> = {
|
|
27
|
+
[K in keyof A]: A[K] extends (draft: T, ...params: infer P) => void ? (...params: P) => void : never;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Store declaration spec: initial-state factory (a lambda so every instance
|
|
31
|
+
* gets a fresh state), optional persistence key (mechanical, framework-run),
|
|
32
|
+
* and the actions write set.
|
|
33
|
+
*/
|
|
34
|
+
export interface StoreSpec<T, A extends ActionsDecl<T>> {
|
|
35
|
+
init: () => T;
|
|
36
|
+
persist?: string;
|
|
37
|
+
actions: A;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Live engine instance: the create() product consumed by the render machinery
|
|
41
|
+
* and by tests. A bare snapshot source plus the baked write set — no React
|
|
42
|
+
* hook rides the engine product (the engine lives in the React-free runtime);
|
|
43
|
+
* the render machinery binds the `useStore` hook from this source on its own
|
|
44
|
+
* side, cached per instance. Production components and render paths never
|
|
45
|
+
* call create() themselves — instance lifecycle is the framework's.
|
|
46
|
+
*/
|
|
47
|
+
export interface StoreInstance<T, A extends ActionsDecl<T>> {
|
|
48
|
+
readonly actions: BakedActions<T, A>;
|
|
49
|
+
getSnapshot(): T;
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to state changes (uSES subscribe side).
|
|
52
|
+
* @param fn - change callback.
|
|
53
|
+
* @returns unsubscribe.
|
|
54
|
+
*/
|
|
55
|
+
subscribe(fn: () => void): () => void;
|
|
56
|
+
/**
|
|
57
|
+
* Drop this instance's persisted value (no-op for non-persist specs). The
|
|
58
|
+
* framework calls it when the owning scope dies for good — a pruned session
|
|
59
|
+
* must not leave orphaned storage keys behind.
|
|
60
|
+
*/
|
|
61
|
+
clearPersisted(): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Store handle: spec + state/actions types + shared identity + instance
|
|
65
|
+
* factory in one value. Handles are constructed in apply world (shared across
|
|
66
|
+
* registrations of one plugin) or by the framework from a registrant's
|
|
67
|
+
* factory (exclusive). Never export a handle at module level — module-cache
|
|
68
|
+
* identity is a disguised singleton across plugin reloads.
|
|
69
|
+
*/
|
|
70
|
+
export interface StoreHandle<T, A extends ActionsDecl<T>> {
|
|
71
|
+
readonly spec: StoreSpec<T, A>;
|
|
72
|
+
/**
|
|
73
|
+
* Create a live engine instance (framework machinery and tests only).
|
|
74
|
+
* @param scopeKey - session id for session-scope instances; suffixes the
|
|
75
|
+
* persist key so per-session instances persist independently (root-scope
|
|
76
|
+
* instances omit it).
|
|
77
|
+
* @returns a fresh instance seeded from `spec.init()`.
|
|
78
|
+
*/
|
|
79
|
+
create(scopeKey?: string): StoreInstance<T, A>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Exclusive-store registration form: the registrant passes the factory itself
|
|
83
|
+
* and the framework calls it per entry x scope (no shared identity exists).
|
|
84
|
+
*/
|
|
85
|
+
export type StoreFactory = () => StoreHandle<any, any>;
|
|
86
|
+
/** The register `store` option position: a shared handle or an exclusive factory. */
|
|
87
|
+
export type StoreDecl = StoreHandle<any, any> | StoreFactory;
|
|
88
|
+
/** Normalize a store declaration to its handle type (factories yield their return). */
|
|
89
|
+
export type HandleOf<H> = H extends () => infer R ? R : H;
|
|
90
|
+
/**
|
|
91
|
+
* Handle-keyed baked actions: the `actions` parameter of an inject factory
|
|
92
|
+
* whose registration declared a store — the same baked callback set the
|
|
93
|
+
* component receives via {@link PropsStore}.
|
|
94
|
+
*/
|
|
95
|
+
export type BoundActions<H> = H extends StoreHandle<infer T, infer A> ? BakedActions<T, A> : never;
|
|
96
|
+
/**
|
|
97
|
+
* The store props share, derived from the declared handle: a typed selector
|
|
98
|
+
* hook plus the baked write set. Components never see the instance itself
|
|
99
|
+
* (no update/set — reads via useStore, writes via the declared actions only).
|
|
100
|
+
*/
|
|
101
|
+
export type PropsStore<H> = H extends StoreHandle<infer T, infer A> ? {
|
|
102
|
+
useStore: SnapshotSelectorHook<T>;
|
|
103
|
+
actions: BakedActions<T, A>;
|
|
104
|
+
} : object;
|
|
105
|
+
/**
|
|
106
|
+
* The defineStore contract (implementation lives in the runtime package,
|
|
107
|
+
* bound to the snapshot-store engine): spec in, handle out, with T inferred
|
|
108
|
+
* from `init` and the actions table constrained by T.
|
|
109
|
+
*/
|
|
110
|
+
export type DefineStore = <T, A extends ActionsDecl<T>>(spec: StoreSpec<T, A>) => StoreHandle<T, A>;
|
|
111
|
+
//# sourceMappingURL=store.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepseek-ai/dsh-client-ui-slots",
|
|
3
|
+
"description": "Slot registry pure core: SlotMap declaration merging, single register composition API, four-share props types, store-seat types, renderer install seam",
|
|
4
|
+
"version": "0.0.1-rc.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "restricted"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/client/ui-slots"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./src/*": "./src/*",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"license": "BSD-3-Clause",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "~18.3.1",
|
|
31
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
32
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"lib/index.js",
|
|
36
|
+
"lib/invariant.js",
|
|
37
|
+
"lib/types/**/*.d.ts"
|
|
38
|
+
],
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
41
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1"
|
|
42
|
+
}
|
|
43
|
+
}
|