@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,601 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot registry pure core. Owners declare slot
|
|
3
|
+
* contracts by merging into {@link SlotMap}; one `register` call contributes a
|
|
4
|
+
* component AND (optionally) declares child slots, a store seat, and the
|
|
5
|
+
* registrant's business face. Zero runtime dependencies (React types only).
|
|
6
|
+
*
|
|
7
|
+
* SlotMap and the standard-kit interfaces live directly in this entry module:
|
|
8
|
+
* consumer `declare module` augmentation merges with declarations lexically in
|
|
9
|
+
* the augmented module, not with re-exports.
|
|
10
|
+
*/
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
12
|
+
import type { HostObservable } from './renderer.ts';
|
|
13
|
+
import type { BoundActions, HandleOf, PropsStore, SnapshotSelectorHook, StoreDecl } from './store.ts';
|
|
14
|
+
export * from './store.ts';
|
|
15
|
+
export * from './renderer.ts';
|
|
16
|
+
/** Slot contract table. Owners extend via declaration merging; entries are {@link SlotEntryDef}. */
|
|
17
|
+
export interface SlotMap {
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Locale namespace table. Dictionary owners extend via declaration merging
|
|
21
|
+
* (exactly like {@link SlotMap}, and declared in this entry module for the
|
|
22
|
+
* same lexical-merge reason): the key is the namespace string, the value is
|
|
23
|
+
* the union of its dictionary keys. Register sites declare one of these
|
|
24
|
+
* namespaces (`locale:`), which puts the typed `t` standard seat on the
|
|
25
|
+
* component props.
|
|
26
|
+
*/
|
|
27
|
+
export interface LocaleNamespaceMap {
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Translate a dictionary key with optional `{name}` template params.
|
|
31
|
+
* `K` narrows the accepted keys to the owning namespace's dictionary union
|
|
32
|
+
* (plus the shared common vocabulary where composed).
|
|
33
|
+
*/
|
|
34
|
+
export type Translate<K extends string = string> = (key: K, params?: Record<string, unknown>) => string;
|
|
35
|
+
/**
|
|
36
|
+
* The shared `common` vocabulary keys as merged by the locale plugin;
|
|
37
|
+
* resolves to `never` in programs without the merge (this package's tests),
|
|
38
|
+
* keeping the union collapse harmless.
|
|
39
|
+
*/
|
|
40
|
+
export type CommonKeyOf = LocaleNamespaceMap extends {
|
|
41
|
+
common: infer C;
|
|
42
|
+
} ? C & string : never;
|
|
43
|
+
/**
|
|
44
|
+
* Key domain of a namespace-bound translate: the namespace's own dictionary
|
|
45
|
+
* union plus the shared common vocabulary (the lookup chain consults common
|
|
46
|
+
* after the namespace misses).
|
|
47
|
+
*/
|
|
48
|
+
export type LocaleKeysOf<N extends keyof LocaleNamespaceMap & string> = (LocaleNamespaceMap[N] & string) | CommonKeyOf;
|
|
49
|
+
/**
|
|
50
|
+
* Namespace-addressed translate — the developer-facing alias over
|
|
51
|
+
* {@link Translate}: `TranslateNS<'model'>` is the translate function of the
|
|
52
|
+
* `model` namespace (key domain = its dictionary union plus the shared
|
|
53
|
+
* common vocabulary), the exact type of the framework-injected `t` seat and
|
|
54
|
+
* of the locale service's typed `bind`.
|
|
55
|
+
*/
|
|
56
|
+
export type TranslateNS<N extends keyof LocaleNamespaceMap & string> = Translate<LocaleKeysOf<N>>;
|
|
57
|
+
/**
|
|
58
|
+
* Dictionary shape for a declared namespace: exactly the keys the namespace
|
|
59
|
+
* merged into {@link LocaleNamespaceMap} — a missing or extra key at a typed
|
|
60
|
+
* registration site is a compile error.
|
|
61
|
+
*/
|
|
62
|
+
export type LocaleDictOf<N extends keyof LocaleNamespaceMap & string> = Record<LocaleNamespaceMap[N] & string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* Locale share of the composed component props: the framework-injected `t`
|
|
65
|
+
* seat, present exactly on entries whose registration declares `locale:`.
|
|
66
|
+
*/
|
|
67
|
+
export type PropsLocale<N> = N extends keyof LocaleNamespaceMap & string ? {
|
|
68
|
+
/** Translate a dictionary key of the declared namespace (or the shared common vocabulary). */
|
|
69
|
+
t: TranslateNS<N>;
|
|
70
|
+
} : object;
|
|
71
|
+
/** Slot cardinality: single occupant, ordered list, key-dispatched, or selector-routed chain. */
|
|
72
|
+
export type SlotKind = 'single' | 'list' | 'keyed' | 'chain';
|
|
73
|
+
/** Slot data context: global, current-session-optional, or strict session-bound. */
|
|
74
|
+
export type SlotScope = 'root' | 'session-maybe' | 'session';
|
|
75
|
+
/**
|
|
76
|
+
* One SlotMap entry: kind/scope axes plus the optional owner-supplied props
|
|
77
|
+
* share (`owner` is what the parent passes at its renderSlot call site; the
|
|
78
|
+
* framework standard kit and the registrant's injected share never enter this
|
|
79
|
+
* table — full component props compose at the component as the four-share
|
|
80
|
+
* intersection, see {@link ComposedProps}).
|
|
81
|
+
*/
|
|
82
|
+
export interface SlotEntryDef {
|
|
83
|
+
kind: SlotKind;
|
|
84
|
+
scope: SlotScope;
|
|
85
|
+
owner?: object;
|
|
86
|
+
/**
|
|
87
|
+
* Optional keyed-entry prop table. A keyed registration contributes one
|
|
88
|
+
* literal key and receives the corresponding prop share; ordinary owner
|
|
89
|
+
* props remain common to every key.
|
|
90
|
+
*/
|
|
91
|
+
keyProps?: Record<string, object>;
|
|
92
|
+
/**
|
|
93
|
+
* Optional opaque context carried by one renderSlot occurrence. Only
|
|
94
|
+
* function-valued members of the slot-level injected hooks compartment
|
|
95
|
+
* receive it; the slot machinery never interprets the value.
|
|
96
|
+
*/
|
|
97
|
+
hookContext?: unknown;
|
|
98
|
+
/**
|
|
99
|
+
* Optional Slot-level inject face supplied by the parent registration's
|
|
100
|
+
* child declaration. Every registered entry receives its bound component
|
|
101
|
+
* face; child registrants do not own or replace this common capability.
|
|
102
|
+
*/
|
|
103
|
+
inject?: object;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Runtime dispatch spec for one slot, recorded from a register call's
|
|
107
|
+
* `children` value. The literal is compile-time checked against the SlotMap
|
|
108
|
+
* entry (`SlotSpec<SlotMap[P]>` in {@link ChildrenDecl}), so kind, scope, and
|
|
109
|
+
* any common inject face are declared at one point and validate each other.
|
|
110
|
+
*/
|
|
111
|
+
export type SlotSpec<E extends SlotEntryDef> = {
|
|
112
|
+
kind: E['kind'];
|
|
113
|
+
scope: E['scope'];
|
|
114
|
+
} & ('inject' extends keyof E ? E extends {
|
|
115
|
+
inject: infer Injected extends object;
|
|
116
|
+
} ? {
|
|
117
|
+
inject: Injected;
|
|
118
|
+
} : {
|
|
119
|
+
inject?: object;
|
|
120
|
+
} : {
|
|
121
|
+
inject?: never;
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Child-slot declaration table for register(): keys are the declared (and
|
|
125
|
+
* thereby render-authorized) slot names, values are their runtime dispatch
|
|
126
|
+
* specs. Declaring is claiming: the registering entry becomes the only entry
|
|
127
|
+
* allowed to render these keys.
|
|
128
|
+
*/
|
|
129
|
+
export type ChildrenDecl = {
|
|
130
|
+
[P in keyof SlotMap & string]?: SlotSpec<SlotMap[P]>;
|
|
131
|
+
};
|
|
132
|
+
/** Owner-supplied props share for a slot key ({} for entries declaring no `owner`). */
|
|
133
|
+
export type OwnerOf<K extends keyof SlotMap & string> = SlotMap[K] extends {
|
|
134
|
+
owner: infer O extends object;
|
|
135
|
+
} ? O : object;
|
|
136
|
+
/** Registration/dispatch key domain of one keyed slot. */
|
|
137
|
+
export type EntryKeyOf<K extends keyof SlotMap & string> = SlotMap[K] extends {
|
|
138
|
+
kind: 'keyed';
|
|
139
|
+
keyProps: infer P extends object;
|
|
140
|
+
} ? keyof P & string : string;
|
|
141
|
+
/** Key-dependent props supplied by the owner at one keyed dispatch site. */
|
|
142
|
+
export type KeyPropsOf<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>> = SlotMap[K] extends {
|
|
143
|
+
kind: 'keyed';
|
|
144
|
+
keyProps: infer P extends object;
|
|
145
|
+
} ? EntryKey extends keyof P ? P[EntryKey] extends object ? P[EntryKey] : never : never : object;
|
|
146
|
+
/** Opaque per-render occurrence context declared by one slot. */
|
|
147
|
+
export type HookContextOf<K extends keyof SlotMap & string> = SlotMap[K] extends {
|
|
148
|
+
hookContext: infer Context;
|
|
149
|
+
} ? Context : never;
|
|
150
|
+
/** Common render-occurrence inject face declared by one slot. */
|
|
151
|
+
export type SlotInjectOf<K extends keyof SlotMap & string> = SlotMap[K] extends {
|
|
152
|
+
inject: infer Injected extends object;
|
|
153
|
+
} ? Injected : object;
|
|
154
|
+
/** Scope axis of a slot key's SlotMap entry. */
|
|
155
|
+
export type ScopeOf<K extends keyof SlotMap & string> = SlotMap[K]['scope'];
|
|
156
|
+
/**
|
|
157
|
+
* Framework standard kit delivered to every session-scope slot component.
|
|
158
|
+
* Declared EMPTY here (zero-dependency layer): the runtime package merges the
|
|
159
|
+
* real members (`useSession` bound to the conversation snapshot and the
|
|
160
|
+
* framework-supplied `sessionId`) exactly as consumers merge SlotMap keys.
|
|
161
|
+
*/
|
|
162
|
+
export interface SessionStandardProps {
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Framework standard kit delivered to current-session-optional slots. Its
|
|
166
|
+
* hooks stay callable while no session is selected and return `undefined`
|
|
167
|
+
* until one becomes current; concrete members merge in at runtime packages.
|
|
168
|
+
*/
|
|
169
|
+
export interface SessionMaybeStandardProps {
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Framework standard kit delivered to EVERY slot component (the global seat).
|
|
173
|
+
* Declared empty here; the runtime package merges the global object-layer
|
|
174
|
+
* selector hooks that shared page composition consumes.
|
|
175
|
+
*/
|
|
176
|
+
export interface GlobalStandardProps {
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The session id type as the runtime's SessionStandardProps merge declares it
|
|
180
|
+
* (branded); falls back to `string` in programs without the merge (this
|
|
181
|
+
* package's own tests).
|
|
182
|
+
*/
|
|
183
|
+
export type SessionIdOf = SessionStandardProps extends {
|
|
184
|
+
sessionId: infer S;
|
|
185
|
+
} ? S : string;
|
|
186
|
+
/**
|
|
187
|
+
* Runtime props share for a slot key: owner share (parent's renderSlot call
|
|
188
|
+
* site) + session standard kit (session scope only) + the global seat.
|
|
189
|
+
*/
|
|
190
|
+
export type PropsRuntime<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>> = OwnerOf<K> & KeyPropsOf<K, EntryKey> & SlotInjectFace<SlotInjectOf<K>> & (ScopeOf<K> extends 'session' ? SessionStandardProps : ScopeOf<K> extends 'session-maybe' ? SessionMaybeStandardProps : object) & GlobalStandardProps;
|
|
191
|
+
/** renderSlot dispatch options: keyed dispatch key, list filtering, and empty fallback. */
|
|
192
|
+
export interface RenderOpts<EntryKey extends string = string> {
|
|
193
|
+
entryKey?: EntryKey;
|
|
194
|
+
only?: string;
|
|
195
|
+
fallback?: ReactNode;
|
|
196
|
+
/** Type-erased runtime seat; PropsRenderSlots narrows or removes it per slot declaration. */
|
|
197
|
+
hookContext?: unknown;
|
|
198
|
+
}
|
|
199
|
+
/** renderSlotChain dispatch options. */
|
|
200
|
+
export interface ChainRenderOpts {
|
|
201
|
+
/** The owner's fallback body, rendered when every entry's selector declines. */
|
|
202
|
+
fallback?: ReactNode;
|
|
203
|
+
/**
|
|
204
|
+
* Keep the fallback permanently mounted: an election hides it (wrapped,
|
|
205
|
+
* display:none) instead of unmounting it, and the all-decline case shows it
|
|
206
|
+
* as-is — fallback-held state (composer drafts, DOM state) survives a
|
|
207
|
+
* takeover. Chain kind only. Sole consumer today: the
|
|
208
|
+
* 'conversation.composer' chain.
|
|
209
|
+
*/
|
|
210
|
+
overlay?: boolean;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Chain-entry selector: the routing decision of one chain contribution.
|
|
214
|
+
* Runs at render time in chain order (ascending `priority`, default 0, lower
|
|
215
|
+
* tries first; ties keep registration = assembly order); the first non-null
|
|
216
|
+
* return elects its entry
|
|
217
|
+
* and becomes the component's `matched` prop; `null` passes to the next
|
|
218
|
+
* entry; all-null falls to the owner's {@link ChainRenderOpts} fallback.
|
|
219
|
+
* MUST be pure — a function of the owner props only, no external mutable
|
|
220
|
+
* reads, no side effects (the decline decision lives here, never in a
|
|
221
|
+
* mounted component probing its own props).
|
|
222
|
+
*/
|
|
223
|
+
export type ChainSelect<O extends object, M> = (owner: O) => M | null;
|
|
224
|
+
/** Keys of a slot-key union whose SlotMap entry is chain-kind (renderSlotChain's dispatch domain). */
|
|
225
|
+
export type ChainKeysOf<S extends keyof SlotMap & string> = S extends unknown ? (SlotMap[S]['kind'] extends 'chain' ? S : never) : never;
|
|
226
|
+
/** Keys in a render share whose dispatch occurrence requires hookContext. */
|
|
227
|
+
type ContextualKeysOf<S extends keyof SlotMap & string> = S extends unknown ? (SlotMap[S] extends {
|
|
228
|
+
hookContext: unknown;
|
|
229
|
+
} ? S : never) : never;
|
|
230
|
+
/** Keys in a render share with the ordinary optional options bag. */
|
|
231
|
+
type OrdinaryKeysOf<S extends keyof SlotMap & string> = Exclude<S, ContextualKeysOf<S>>;
|
|
232
|
+
/**
|
|
233
|
+
* Plain and contextual child dispatch signatures. Keeping them as separate
|
|
234
|
+
* call signatures preserves ordinary renderSlot assignability while making a
|
|
235
|
+
* declared hookContext mandatory only for the Slot keys that need it.
|
|
236
|
+
*/
|
|
237
|
+
type RenderSlotFn<S extends keyof SlotMap & string> = ([ContextualKeysOf<S>] extends [never] ? object : {
|
|
238
|
+
<K extends ContextualKeysOf<S>, EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>>(key: K, owner: OwnerOf<K> & KeyPropsOf<K, NoInfer<EntryKey>>, opts: RenderOpts<EntryKey> & {
|
|
239
|
+
hookContext: HookContextOf<K>;
|
|
240
|
+
}): ReactNode;
|
|
241
|
+
}) & ([OrdinaryKeysOf<S>] extends [never] ? object : {
|
|
242
|
+
<K extends OrdinaryKeysOf<S>, EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>>(key: K, owner: OwnerOf<K> & KeyPropsOf<K, NoInfer<EntryKey>>, opts?: Omit<RenderOpts<EntryKey>, 'hookContext'>): ReactNode;
|
|
243
|
+
});
|
|
244
|
+
/**
|
|
245
|
+
* Chain matched share: a chain-slot component receives its selector's
|
|
246
|
+
* non-null result as the framework-injected `matched` prop; other kinds add
|
|
247
|
+
* nothing to the composed constraint.
|
|
248
|
+
*/
|
|
249
|
+
export type MatchedShare<E extends SlotEntryDef, M> = E['kind'] extends 'chain' ? {
|
|
250
|
+
matched: M;
|
|
251
|
+
} : object;
|
|
252
|
+
/**
|
|
253
|
+
* Conversation-session selector hook alias for props contracts. Wide by
|
|
254
|
+
* default at this dependency-inverted layer; the runtime narrows at its
|
|
255
|
+
* export outlet (`UseSession<ConversationSnapshot>`).
|
|
256
|
+
*/
|
|
257
|
+
export type UseSession<Snap extends object = object> = SnapshotSelectorHook<Snap>;
|
|
258
|
+
/** Props of the standard-kit SessionProvider seat (render-prop form). */
|
|
259
|
+
export interface SessionAreaProps {
|
|
260
|
+
/** No-session body (also covers a current id whose session cannot be resolved). */
|
|
261
|
+
empty?: (() => ReactNode) | undefined;
|
|
262
|
+
/** Session body; the framework remounts it per session (key=sessionId). */
|
|
263
|
+
children: (sessionId: SessionIdOf) => ReactNode;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Framework-wired session area component. It subscribes to runtime-owned
|
|
267
|
+
* session selection and is injected into entries that declare session-scoped
|
|
268
|
+
* children; business code does not import it directly.
|
|
269
|
+
*/
|
|
270
|
+
export type SessionProviderComponent = (props: SessionAreaProps) => ReactNode;
|
|
271
|
+
/**
|
|
272
|
+
* Child-slot render share: `renderSlot` statically narrowed to the entry's
|
|
273
|
+
* declared children keys. Delegation is plain props passing (hand
|
|
274
|
+
* `props.renderSlot` down); the authorizing identity stays the registering
|
|
275
|
+
* entry. `__renders` is a phantom variance anchor (never materialized):
|
|
276
|
+
* generic method signatures compare loosely across differing key unions, so
|
|
277
|
+
* this contravariant marker is what actually enforces "component key set ⊆
|
|
278
|
+
* children declaration" at the register call site.
|
|
279
|
+
*/
|
|
280
|
+
export type PropsRenderSlots<S extends keyof SlotMap & string> = {
|
|
281
|
+
/**
|
|
282
|
+
* Render a declared non-chain child slot (chain keys dispatch through
|
|
283
|
+
* `renderSlotChain` — their routing lives in entry selectors).
|
|
284
|
+
* @param key - declared child key.
|
|
285
|
+
* @param owner - owner props share for that key (decided at the render site).
|
|
286
|
+
* @param opts - kind dispatch options.
|
|
287
|
+
* @returns rendered node(s).
|
|
288
|
+
*/
|
|
289
|
+
renderSlot: RenderSlotFn<Exclude<S, ChainKeysOf<S>>>;
|
|
290
|
+
readonly __renders?: ((key: S) => void) | undefined;
|
|
291
|
+
} & ([ChainKeysOf<S>] extends [never] ? object : {
|
|
292
|
+
/**
|
|
293
|
+
* Render a declared chain child slot: entry selectors run in chain order
|
|
294
|
+
* over `owner`; the first non-null match renders its component with the
|
|
295
|
+
* selector result injected as `matched`; all-null renders `opts.fallback`.
|
|
296
|
+
* @param key - declared chain child key.
|
|
297
|
+
* @param owner - owner props share (the selectors' routing input).
|
|
298
|
+
* @param opts - fallback body for the all-null case.
|
|
299
|
+
* @returns rendered node(s).
|
|
300
|
+
*/
|
|
301
|
+
renderSlotChain: <K extends ChainKeysOf<S>>(key: K, owner: OwnerOf<K>, opts?: ChainRenderOpts) => ReactNode;
|
|
302
|
+
}) & ('session' extends ScopeOf<S> ? {
|
|
303
|
+
SessionProvider: SessionProviderComponent;
|
|
304
|
+
} : object);
|
|
305
|
+
/**
|
|
306
|
+
* Registration-position component shape: the bare call signature, so composed
|
|
307
|
+
* constraints check through clean parameter contravariance (FC statics add
|
|
308
|
+
* covariant noise rejecting legitimate narrowings).
|
|
309
|
+
*/
|
|
310
|
+
export type SlotComponent<P> = (props: P) => ReactNode;
|
|
311
|
+
/**
|
|
312
|
+
* Registrant hooks compartment: bare observable sources (getSnapshot +
|
|
313
|
+
* subscribe pairs) supplied under the reserved `hooks` key of an entry's
|
|
314
|
+
* inject face. These retain the original source-to-selector binding and do
|
|
315
|
+
* not participate in render-occurrence context.
|
|
316
|
+
*/
|
|
317
|
+
export type HooksSources = Record<string, HostObservable<unknown>>;
|
|
318
|
+
/** Framework-owned props visible while a slot-level contextual Hook is bound. */
|
|
319
|
+
export type StandardPropsOf<K extends keyof SlotMap & string> = (ScopeOf<K> extends 'session' ? SessionStandardProps : ScopeOf<K> extends 'session-maybe' ? SessionMaybeStandardProps : object) & GlobalStandardProps;
|
|
320
|
+
/**
|
|
321
|
+
* One function-valued slot-level inject.hooks member. The factory is pure and
|
|
322
|
+
* returns the actual custom Hook; it must not invoke a Hook while being bound.
|
|
323
|
+
*/
|
|
324
|
+
export type SlotHookFactory<K extends keyof SlotMap & string, Hook extends (...args: never[]) => unknown> = (standard: StandardPropsOf<K>, hookContext: HookContextOf<K>) => Hook;
|
|
325
|
+
/** Component-side Hook produced from one slot-level inject.hooks member. */
|
|
326
|
+
type BoundHookOf<Definition> = Definition extends HostObservable<infer Snapshot> ? SnapshotSelectorHook<Snapshot> : Definition extends (...args: never[]) => infer Hook ? Hook extends (...args: never[]) => unknown ? Hook : never : never;
|
|
327
|
+
/**
|
|
328
|
+
* Selector-hook share synthesized from a hooks compartment: each source
|
|
329
|
+
* `name` becomes a `use<Name>` selector hook over its snapshot type.
|
|
330
|
+
*/
|
|
331
|
+
export type PropsSlotHooks<HS extends object> = {
|
|
332
|
+
[N in keyof HS & string as `use${Capitalize<N>}`]: BoundHookOf<HS[N]>;
|
|
333
|
+
};
|
|
334
|
+
/** Component-side view of a slot dispatcher's common inject face. */
|
|
335
|
+
export type SlotInjectFace<I extends object> = I extends {
|
|
336
|
+
hooks: infer HS extends object;
|
|
337
|
+
} ? Omit<I, 'hooks'> & PropsSlotHooks<HS> : I;
|
|
338
|
+
/** Selector-hook share synthesized from an entry inject hooks compartment. */
|
|
339
|
+
export type PropsHooks<HS extends HooksSources> = {
|
|
340
|
+
[N in keyof HS & string as `use${Capitalize<N>}`]: SnapshotSelectorHook<HS[N] extends HostObservable<infer T> ? T : never>;
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* The component-side view of an inject face: the reserved `hooks`
|
|
344
|
+
* compartment (when declared) arrives as bound `use<Name>` selector hooks;
|
|
345
|
+
* every other member passes through verbatim.
|
|
346
|
+
*/
|
|
347
|
+
export type InjectFace<I extends object> = I extends {
|
|
348
|
+
hooks: infer HS extends HooksSources;
|
|
349
|
+
} ? Omit<I, 'hooks'> & PropsHooks<HS> : I;
|
|
350
|
+
/**
|
|
351
|
+
* The composed component props intersection: runtime share (SlotMap) +
|
|
352
|
+
* child-render share (children declaration) + store share (declared handle) +
|
|
353
|
+
* the registrant's injected business face (its hooks compartment bound, see
|
|
354
|
+
* {@link InjectFace}) + the locale `t` seat (declared namespace, see
|
|
355
|
+
* {@link PropsLocale}). Each share derives from its single source of truth;
|
|
356
|
+
* components reference this composition, never re-type it.
|
|
357
|
+
*/
|
|
358
|
+
export type ComposedProps<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>, S extends keyof SlotMap & string, H, I extends object, M = never, N = undefined> = PropsRuntime<K, EntryKey> & PropsRenderSlots<S> & PropsStore<H> & InjectFace<I> & MatchedShare<SlotMap[K], M> & PropsLocale<N>;
|
|
359
|
+
/**
|
|
360
|
+
* Inject factory parameter list, derived from the registration's declaration:
|
|
361
|
+
* strict session slots receive a definite framework-resolved `sessionId`;
|
|
362
|
+
* session-maybe slots receive the current id or `undefined`; a declared store
|
|
363
|
+
* appends the baked `actions` (the same callbacks the component receives).
|
|
364
|
+
* Business data access happens through the apply closure's ctx — no binding
|
|
365
|
+
* object parameter exists.
|
|
366
|
+
*/
|
|
367
|
+
export type InjectParams<K extends keyof SlotMap & string, H> = ScopeOf<K> extends 'session' ? ([H] extends [StoreDecl] ? [sessionId: SessionIdOf, actions: BoundActions<HandleOf<H>>] : [sessionId: SessionIdOf]) : ScopeOf<K> extends 'session-maybe' ? ([H] extends [StoreDecl] ? [sessionId: SessionIdOf | undefined, actions: BoundActions<HandleOf<H>> | undefined] : [sessionId: SessionIdOf | undefined]) : ([H] extends [StoreDecl] ? [actions: BoundActions<HandleOf<H>>] : []);
|
|
368
|
+
/**
|
|
369
|
+
* A list-entry display label: a plain string, or a thunk re-evaluated per
|
|
370
|
+
* read so registration-time text (nav rows, tabs) follows the active locale
|
|
371
|
+
* without re-registration. Owners resolve through {@link resolveSlotLabel}.
|
|
372
|
+
*/
|
|
373
|
+
export type SlotLabel = string | (() => string);
|
|
374
|
+
/** Kind shape fields carried in register options (keyed dispatch key; list id/order/label; chain select/priority). */
|
|
375
|
+
export type KindOptions<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>, M = never> = SlotMap[K]['kind'] extends 'keyed' ? {
|
|
376
|
+
key: EntryKey;
|
|
377
|
+
} : SlotMap[K]['kind'] extends 'list' ? {
|
|
378
|
+
id: string;
|
|
379
|
+
order?: number;
|
|
380
|
+
label?: SlotLabel;
|
|
381
|
+
} : SlotMap[K]['kind'] extends 'chain' ? {
|
|
382
|
+
/** Routing selector, mandatory on chain entries; `M` (the component's `matched` prop) infers from its return. */
|
|
383
|
+
select: ChainSelect<SlotMap[K] extends {
|
|
384
|
+
owner: infer O extends object;
|
|
385
|
+
} ? O : object, M>;
|
|
386
|
+
/** Explicit chain position (ascending, default 0, lower tries first); ties keep registration = assembly order. */
|
|
387
|
+
priority?: number;
|
|
388
|
+
} : object;
|
|
389
|
+
/**
|
|
390
|
+
* Compile-time presence check: an entry declaring children MUST consume
|
|
391
|
+
* `renderSlot` (or `renderSlotChain` when its only children are chain slots)
|
|
392
|
+
* — declaring is claiming; an entry that does not render its children should
|
|
393
|
+
* not declare them. Evaluates to an unsatisfiable intersection member naming
|
|
394
|
+
* the declared keys when violated.
|
|
395
|
+
*/
|
|
396
|
+
type RendersCheck<C, D> = [
|
|
397
|
+
keyof D & keyof SlotMap & string
|
|
398
|
+
] extends [never] ? unknown : C extends (props: infer P) => ReactNode ? ('renderSlot' extends keyof P ? unknown : 'renderSlotChain' extends keyof P ? unknown : {
|
|
399
|
+
'children declared but the component consumes no renderSlot': keyof D & keyof SlotMap & string;
|
|
400
|
+
}) : unknown;
|
|
401
|
+
/** Common register options share (see {@link SlotCore.register} for semantics). */
|
|
402
|
+
type BaseOptions<K extends keyof SlotMap & string, EntryKey extends EntryKeyOf<K>, D extends ChildrenDecl, H, M = never, N = undefined> = {
|
|
403
|
+
/** Target slot key (the entry contributes INTO this slot). */
|
|
404
|
+
name: K;
|
|
405
|
+
/** Child-slot declaration + render authorization + runtime spec, in one table. */
|
|
406
|
+
children?: D;
|
|
407
|
+
/** Store seat: a shared handle (apply-constructed) or an exclusive factory (framework-called per entry x scope). */
|
|
408
|
+
store?: H;
|
|
409
|
+
/**
|
|
410
|
+
* Dictionary namespace of this entry's copy. Declaring it puts the
|
|
411
|
+
* framework-synthesized `t` seat (typed to the namespace's dictionary
|
|
412
|
+
* union) on the component props; rendering requires an installed locale
|
|
413
|
+
* face — fails loud otherwise.
|
|
414
|
+
*/
|
|
415
|
+
locale?: N;
|
|
416
|
+
/** Registrant identity label for diagnostics (the runtime Service wrapper stamps the caller's fiber name). */
|
|
417
|
+
registrant?: string;
|
|
418
|
+
} & KindOptions<K, EntryKey, M>;
|
|
419
|
+
/**
|
|
420
|
+
* One stored registration, as recorded by the core and read by the render
|
|
421
|
+
* machinery (type-erased at this boundary; the registration contract already proved
|
|
422
|
+
* the shares against the component).
|
|
423
|
+
*/
|
|
424
|
+
export interface StoredEntry {
|
|
425
|
+
component: unknown;
|
|
426
|
+
options: {
|
|
427
|
+
key?: string;
|
|
428
|
+
id?: string;
|
|
429
|
+
order?: number;
|
|
430
|
+
label?: SlotLabel;
|
|
431
|
+
priority?: number;
|
|
432
|
+
};
|
|
433
|
+
/** Chain routing selector (type-erased like `inject`; present exactly on chain-slot entries). */
|
|
434
|
+
select?: ((owner: never) => unknown) | undefined;
|
|
435
|
+
/** Registrant business face; positional params derive from the declaration (sessionId?, actions?). */
|
|
436
|
+
inject?: ((...args: never[]) => Record<string, unknown>) | undefined;
|
|
437
|
+
/** Child-slot declaration table (declaration + authorization + runtime spec in one). */
|
|
438
|
+
children?: Readonly<Record<string, SlotSpec<SlotEntryDef>>> | undefined;
|
|
439
|
+
/** Declared store seat (instance resolution and lifecycle live with the host machinery). */
|
|
440
|
+
store?: StoreDecl | undefined;
|
|
441
|
+
/** Declared dictionary namespace (the render machinery synthesizes the `t` seat from it). */
|
|
442
|
+
locale?: string | undefined;
|
|
443
|
+
/** Diagnostics label of who registered. */
|
|
444
|
+
registrant?: string | undefined;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Resolve a possibly-thunked list label at read time (thunks follow the
|
|
448
|
+
* active locale; owners projecting ledger rows call this instead of reading
|
|
449
|
+
* `options.label` raw).
|
|
450
|
+
* @param label - the stored label.
|
|
451
|
+
* @returns the display string, or undefined when the entry declared none.
|
|
452
|
+
*/
|
|
453
|
+
export declare function resolveSlotLabel(label: SlotLabel | undefined): string | undefined;
|
|
454
|
+
/**
|
|
455
|
+
* Pure slot registry (no cordis; event emission and the renderer installation contract
|
|
456
|
+
* live in the runtime Service wrapper).
|
|
457
|
+
*
|
|
458
|
+
* The 'root' slot is the one a-priori declaration, seeded at construction
|
|
459
|
+
* (single/root, declared by the framework) — the render tree's root hole.
|
|
460
|
+
*
|
|
461
|
+
* Change propagation contract: versions bump and {@link SlotCore.onMutate}
|
|
462
|
+
* fires synchronously per mutation (registry state is consistent when they
|
|
463
|
+
* fire); {@link SlotCore.subscribeDeclaration} fires synchronously for each
|
|
464
|
+
* declaration lifetime boundary; {@link SlotCore.subscribe} notifications
|
|
465
|
+
* batch per microtask, so N same-tick mutations produce one notification per
|
|
466
|
+
* touched key.
|
|
467
|
+
*/
|
|
468
|
+
export declare class SlotCore {
|
|
469
|
+
private records;
|
|
470
|
+
private mutateListeners;
|
|
471
|
+
/** Shared-handle scope ledger: handle → the scope it first mounted under + live mount count. */
|
|
472
|
+
private handleScopes;
|
|
473
|
+
private dirty;
|
|
474
|
+
private flushScheduled;
|
|
475
|
+
constructor();
|
|
476
|
+
/**
|
|
477
|
+
* Contribute a component to a declared slot and (optionally) declare child
|
|
478
|
+
* slots, a store seat, and the registrant's business face.
|
|
479
|
+
*
|
|
480
|
+
* Load-time validation (misconfiguration fails loud; the render hot path
|
|
481
|
+
* re-checks nothing): registering into an undeclared slot throws; declaring
|
|
482
|
+
* an already-declared child key throws (one declarer per slot — the message
|
|
483
|
+
* names the first declarer); mounting one shared store handle under slots
|
|
484
|
+
* of different scopes throws. Kind constraints: single — duplicate
|
|
485
|
+
* registration throws; keyed — missing/duplicate `key` throws; list —
|
|
486
|
+
* missing/duplicate `id` throws; chain — missing `select` throws (the
|
|
487
|
+
* selector is the entry's routing seat, see {@link ChainSelect}).
|
|
488
|
+
*
|
|
489
|
+
* Lifecycle: the disposer removes the contribution AND collapses every
|
|
490
|
+
* declared child slot (child entries clear recursively; their stale
|
|
491
|
+
* disposers become no-ops) — one lifecycle axis, no dangling state.
|
|
492
|
+
*
|
|
493
|
+
* @param options - registration options: target `name`, `children`
|
|
494
|
+
* declaration table, `store` seat, `inject` business-face factory, kind
|
|
495
|
+
* shape fields (keyed `key`; list `id`/`order`/`label`).
|
|
496
|
+
* @param component - component honoring the four-share composed props
|
|
497
|
+
* contract ({@link ComposedProps}); checked at this call site.
|
|
498
|
+
* @returns disposer removing the registration and its declarations
|
|
499
|
+
* (idempotent; stale disposers after a cascade are no-ops).
|
|
500
|
+
*/
|
|
501
|
+
register<K extends keyof SlotMap & string, const EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>, const D extends ChildrenDecl = Record<never, never>, H extends StoreDecl | undefined = undefined, M = never, N extends (keyof LocaleNamespaceMap & string) | undefined = undefined, C extends SlotComponent<never> = SlotComponent<never>>(options: BaseOptions<K, EntryKey, D, H, M, N> & {
|
|
502
|
+
inject?: undefined;
|
|
503
|
+
}, component: C & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, object, NoInfer<M>, NoInfer<N>>> & RendersCheck<C, D>): () => void;
|
|
504
|
+
/**
|
|
505
|
+
* Inject-bearing overload: identical semantics to the overload above, plus
|
|
506
|
+
* the registrant's business face — `I` is inferred from the inject
|
|
507
|
+
* factory's return and joins the component's composed-props constraint
|
|
508
|
+
* (factory parameters derive from the declaration, {@link InjectParams}).
|
|
509
|
+
* @param options - registration options plus the `inject` business-face factory.
|
|
510
|
+
* @param component - component honoring the four-share composed props
|
|
511
|
+
* contract including the inject share `I`.
|
|
512
|
+
* @returns disposer removing the registration and its declarations.
|
|
513
|
+
*/
|
|
514
|
+
register<K extends keyof SlotMap & string, I extends object, const EntryKey extends EntryKeyOf<K> = EntryKeyOf<K>, const D extends ChildrenDecl = Record<never, never>, H extends StoreDecl | undefined = undefined, M = never, N extends (keyof LocaleNamespaceMap & string) | undefined = undefined, C extends SlotComponent<never> = SlotComponent<never>>(options: BaseOptions<K, EntryKey, D, H, M, N> & {
|
|
515
|
+
inject: (...args: InjectParams<K, H>) => I;
|
|
516
|
+
}, component: C & SlotComponent<ComposedProps<K, NoInfer<EntryKey>, keyof NoInfer<D> & keyof SlotMap & string, HandleOf<NoInfer<H>>, I, NoInfer<M>, NoInfer<N>>> & RendersCheck<C, D>): () => void;
|
|
517
|
+
/**
|
|
518
|
+
* Whether a previously obtained entry is still registered (the render
|
|
519
|
+
* machinery's stale-authorization probe: a retained renderSlot binding
|
|
520
|
+
* whose entry left the ledger must not render).
|
|
521
|
+
* @param entry - a previously read entry.
|
|
522
|
+
* @returns false once the entry's registration was disposed.
|
|
523
|
+
*/
|
|
524
|
+
isLive(entry: StoredEntry): boolean;
|
|
525
|
+
/**
|
|
526
|
+
* Snapshot the registered entries for a key. Returns the cached array
|
|
527
|
+
* reference (stable between mutations — safe as a uSES getSnapshot source);
|
|
528
|
+
* empty for keys not (or no longer) declared, so renderers may probe ahead
|
|
529
|
+
* of plugin load order.
|
|
530
|
+
* @param key - slot key (dynamic: the render machinery holds keys as strings).
|
|
531
|
+
* @returns entries in registration (list: order) sequence.
|
|
532
|
+
*/
|
|
533
|
+
entries(key: string): readonly StoredEntry[];
|
|
534
|
+
/**
|
|
535
|
+
* Look up a slot's declared spec, narrowed by the SlotMap key.
|
|
536
|
+
* @param key - SlotMap key.
|
|
537
|
+
* @returns the spec, or undefined while undeclared.
|
|
538
|
+
*/
|
|
539
|
+
spec<K extends keyof SlotMap & string>(key: K): SlotSpec<SlotMap[K]> | undefined;
|
|
540
|
+
/**
|
|
541
|
+
* Dynamic-key escape hatch for spec lookup — renderers resolving keys they
|
|
542
|
+
* only hold as strings (generic dispatch) use this wide form; statically
|
|
543
|
+
* keyed callers use {@link SlotCore.spec}.
|
|
544
|
+
* @param key - candidate slot key.
|
|
545
|
+
* @returns the wide-typed spec, or undefined while undeclared.
|
|
546
|
+
*/
|
|
547
|
+
specDynamic(key: string): SlotSpec<SlotEntryDef> | undefined;
|
|
548
|
+
/**
|
|
549
|
+
* Read the declaration lifetime of a key. Entry additions and removals do
|
|
550
|
+
* not change it; declaration creation and collapse each advance it.
|
|
551
|
+
* @param key - slot key.
|
|
552
|
+
* @returns monotonic epoch (0 before the first declaration).
|
|
553
|
+
*/
|
|
554
|
+
declarationEpoch(key: string): number;
|
|
555
|
+
/**
|
|
556
|
+
* Subscribe to registration changes for a key (microtask-batched).
|
|
557
|
+
* Subscribing ahead of declaration is allowed; the declaration notifies.
|
|
558
|
+
* @param key - slot key.
|
|
559
|
+
* @param fn - change callback.
|
|
560
|
+
* @returns unsubscribe.
|
|
561
|
+
*/
|
|
562
|
+
subscribe(key: string, fn: () => void): () => void;
|
|
563
|
+
/**
|
|
564
|
+
* Subscribe to declaration lifetime boundaries for a key. Notifications
|
|
565
|
+
* are synchronous so declaration teardown finishes before a subsequent
|
|
566
|
+
* same-tick registration can observe stale resources. Ordinary entry
|
|
567
|
+
* mutations do not notify this surface. A children table commits every
|
|
568
|
+
* sibling declaration before its first notification.
|
|
569
|
+
* @param key - slot key.
|
|
570
|
+
* @param fn - declaration or collapse callback.
|
|
571
|
+
* @returns unsubscribe.
|
|
572
|
+
*/
|
|
573
|
+
subscribeDeclaration(key: string, fn: () => void): () => void;
|
|
574
|
+
/**
|
|
575
|
+
* Monotonic version for a key, bumped synchronously per mutation so a
|
|
576
|
+
* uSES getSnapshot read is never stale when its batched notification lands.
|
|
577
|
+
* @param key - slot key.
|
|
578
|
+
* @returns current version (0 for untouched keys).
|
|
579
|
+
*/
|
|
580
|
+
getVersion(key: string): number;
|
|
581
|
+
/**
|
|
582
|
+
* Hook every mutation (the runtime Service wrapper bridges this to ctx.emit).
|
|
583
|
+
* Fires synchronously per mutation, unbatched — event semantics need one
|
|
584
|
+
* emission per change.
|
|
585
|
+
* @param fn - called with the mutated key.
|
|
586
|
+
* @returns unsubscribe.
|
|
587
|
+
*/
|
|
588
|
+
onMutate(fn: (key: string) => void): () => void;
|
|
589
|
+
/**
|
|
590
|
+
* Cascade for a removed entry: release its store mount and collapse every
|
|
591
|
+
* child slot it declared — specs clear, contributions empty (their stale
|
|
592
|
+
* disposers no-op), recursively down the declaration tree. One lifecycle
|
|
593
|
+
* axis: ledger rows, slots, contributions, and store mounts die together.
|
|
594
|
+
*/
|
|
595
|
+
private releaseEntry;
|
|
596
|
+
private record;
|
|
597
|
+
private markDirty;
|
|
598
|
+
private notifyDeclaration;
|
|
599
|
+
private flush;
|
|
600
|
+
}
|
|
601
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-slots`.
|
|
3
|
+
* @module @deepseek-ai/dsh-client-ui-slots/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "client-ui-slots-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|