@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 ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, DeepSeek
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-slots/README.md
5
+ README.md: 7ee2a3d22c2a41b0e5c356c52d383a047cf99029
6
+ README.zh.md: 793bb2d1f77e02e34a41fab16b2c2c64b78744dc
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @deepseek-ai/dsh-client-ui-slots
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Slot registry pure core, slot terminal design: SlotMap declaration merging, the single `register` composition API on SlotCore, the four-share component-props type family, the store-seat type family, and the renderer installation contract. React types only at runtime — the package is React-free and cordis-free.
6
+
7
+ One `register({ name, children?, store?, inject?, ...kind }, Component)` call contributes a component into a declared slot and, in the same breath, declares child slots (declaration = render authorization = runtime spec, one table), a store seat, and the registrant's business face. The component is checked at the call site against `ComposedProps` — the intersection of four shares, each derived from its single source of truth:
8
+
9
+ | share | type | source |
10
+ |---|---|---|
11
+ | runtime | `PropsRuntime<K>` | SlotMap entry: `owner` (parent's renderSlot call site) + session standard kit + global seat |
12
+ | child render | `PropsRenderSlots<S>` | the register call's `children` key set (statically narrowed `renderSlot`) |
13
+ | store | `PropsStore<H>` | the declared handle: `useStore` selector hook + draft-stripped `actions` |
14
+ | business | `I` | inferred from the `inject` factory's return |
15
+
16
+ Chain-kind slots invert keyed routing — entries self-nominate instead of the dispatch site picking an `entryKey`: each registration carries a pure `ChainSelect` selector (plus optional ascending `priority`, ties in registration order), the first non-null return elects its entry and becomes the component's `matched` prop, and all-null falls to the owner's `renderSlotChain` fallback (`ChainRenderOpts`).
17
+
18
+ The standard-kit interfaces (`SessionStandardProps`, `GlobalStandardProps`) are declared empty here and merged by the runtime package (same declare-merge pattern as SlotMap keys). The renderer binds the runtime's session and workspace observable sources into selector hooks. Inject factory parameters derive from the declaration (`InjectParams`): session slots get `sessionId`, a declared store appends baked `actions`, nothing else — data access lives in the apply closure's ctx.
19
+
20
+ The store family (`defineStore` spec in / `StoreHandle<T, A>` out) types the store seat: `init` infers the state schema, `actions` is the complete draft-transform write set, `BakedActions` strips the draft parameter into the callbacks components and inject factories receive. The `defineStore` value implementation lives in the runtime package (the engine's home) and satisfies the `DefineStore` contract exported here. Engine products and the renderer host contract carry bare snapshot sources (`getSnapshot`/`subscribe`), never React hooks — hook binding belongs to the render machinery; only the props-contract hook type (`SnapshotSelectorHook`) lives here.
21
+
22
+ `SlotCore` seeds the a-priori `'root'` slot at construction and enforces load-time validation (undeclared-slot registration, duplicate child declaration, one shared handle under two scopes, a chain registration without `select` — all throw at register). An entry's disposer collapses its declared child slots recursively: ledger rows, contributions, and store mounts die on one lifecycle axis. Each key also carries a declaration epoch that advances only on declaration and collapse; the runtime uses it for [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection), independently from ordinary entry versions. `renderer.ts` carries the installation contract (`SlotRenderer`, `SlotRendererHost`) plus `StaleAuthorizationError`/`SlotOwnershipError`; the implementation lives in web-react, the installation in the shell boot.
23
+
24
+ ## Model Experience
25
+
26
+ None, as the slot registry is browser-side UI plumbing; nothing here reaches a model request.
27
+
28
+ #### KV Cache effect
29
+
30
+ None; this package neither assembles nor sends a provider request.
31
+
32
+ ## Known Limitations and Deferred Work
33
+
34
+ - **`isLive` scans all records linearly** — fine at UI-plugin registration counts (tens); revisit with an entry→record backref if ledgers ever grow hot.
35
+ - **The `__renders` phantom anchor is visible on `PropsRenderSlots`** — the same accepted noise as the type-chain design's `__accepts`: generic method signatures compare loosely across key unions, so the contravariant marker is what enforces "component key set ⊆ children declaration".
package/README.zh.md ADDED
@@ -0,0 +1,35 @@
1
+ # @deepseek-ai/dsh-client-ui-slots
2
+
3
+ [English](README.md) | 中文
4
+
5
+ Slot 注册表纯核心、slot 终端设计:SlotMap 声明合并、SlotCore 上唯一的 `register` 组合 API、四 share 组件 props 类型家族、store seat 类型家族,以及 renderer 安装约定。只使用 React 类型;该包不依赖 React,也不依赖 Cordis。
6
+
7
+ 一次 `register({ name, children?, store?, inject?, ...kind }, Component)` 调用会向已声明 slot 贡献一个组件,同时声明子 slot(声明 = 渲染授权 = 运行时规范,三者共用一张表)、store seat 以及注册方的业务表层。组件会在调用点依据 `ComposedProps` 接受检查;该类型是四个 share 的交集,每个 share 都从各自的唯一真源派生:
8
+
9
+ | share | 类型 | 来源 |
10
+ |---|---|---|
11
+ | runtime | `PropsRuntime<K>` | SlotMap 条目:`owner`(父级 renderSlot 调用点)+ Session 标准工具包 + 全局 seat |
12
+ | child render | `PropsRenderSlots<S>` | register 调用的 `children` key 集合(静态缩窄的 `renderSlot`) |
13
+ | store | `PropsStore<H>` | 已声明 handle:`useStore` selector hook + 移除 draft 的 `actions` |
14
+ | business | `I` | 从 `inject` factory 返回值推断 |
15
+
16
+ chain-kind slot 会反转键控路由:条目自行提名,而不是由分发点选择 `entryKey`。每次注册都携带一个纯 `ChainSelect` selector(另有可选的升序 `priority`,相同值按注册顺序处理);第一个非 null 返回值选中其条目,并成为组件的 `matched` prop;全部返回 null 时则使用 owner 的 `renderSlotChain` fallback(`ChainRenderOpts`)。
17
+
18
+ 标准工具包接口(`SessionStandardProps`、`GlobalStandardProps`)在这里声明为空,由 runtime 包合并(与 SlotMap key 相同的 declare-merge 模式)。renderer 会把运行时 Session 和 Workspace observable source 绑定为 selector hook。Inject factory 参数从声明派生(`InjectParams`):Session slot 获得 `sessionId`;声明 store 时追加 baked `actions`;没有其他参数,数据访问位于 apply 闭包的 ctx 中。
19
+
20
+ store 家族(输入 `defineStore` 规范/输出 `StoreHandle<T, A>`)为 store seat 建模:`init` 推断状态 schema;`actions` 是完整的 draft-transform 写入集合;`BakedActions` 移除 draft 参数,成为组件和 inject factory 收到的回调。`defineStore` 值实现位于 runtime 包(引擎所属位置),并满足这里导出的 `DefineStore` 约定。引擎产物与 renderer host 约定携带裸快照 source(`getSnapshot`/`subscribe`),绝不携带 React hook;hook 绑定属于渲染机制,只有 props 约定 hook 类型(`SnapshotSelectorHook`)位于这里。
21
+
22
+ `SlotCore` 在构造时预置 `'root'` slot,并强制执行加载时验证(注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot:账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch(声明代次),它只在声明与折叠时递增;运行时将其用于 [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer`、`SlotRendererHost`)以及 `StaleAuthorizationError`/`SlotOwnershipError`;实现在 web-react 中,安装则在外壳启动中完成。
23
+
24
+ ## 模型体验
25
+
26
+ 无。slot 注册表属于浏览器侧 UI 接线;这里没有任何内容进入模型请求。
27
+
28
+ #### KV Cache 影响
29
+
30
+ 无;该包既不组装也不发送提供方请求。
31
+
32
+ ## 已知限制与暂缓事项
33
+
34
+ - **`isLive` 会线性扫描所有记录**:在 UI 插件的注册规模(数十项)下没有问题;如果账本变得频繁访问,再使用条目→记录反向引用改进。
35
+ - **`__renders` 幻象锚点在 `PropsRenderSlots` 上可见**:这是与类型链设计的 `__accepts` 相同且已接受的噪声;泛型方法签名在 key 联合之间比较宽松,因此必须依靠逆变标记强制执行「组件 key 集合 ⊆ children 声明」。
package/lib/index.js ADDED
@@ -0,0 +1,292 @@
1
+ //#region lib/types/renderer.js
2
+ /** Thrown when a retained renderSlot binding is invoked after its declaring entry was disposed. */
3
+ var StaleAuthorizationError = class extends Error {};
4
+ /**
5
+ * Thrown when a renderSlot binding is invoked for a key outside its entry's
6
+ * children declaration (plain-JS backstop; typed callers are narrowed
7
+ * statically).
8
+ */
9
+ var SlotOwnershipError = class extends Error {};
10
+ //#endregion
11
+ //#region lib/types/index.js
12
+ /**
13
+ * Resolve a possibly-thunked list label at read time (thunks follow the
14
+ * active locale; owners projecting ledger rows call this instead of reading
15
+ * `options.label` raw).
16
+ * @param label - the stored label.
17
+ * @returns the display string, or undefined when the entry declared none.
18
+ */
19
+ function resolveSlotLabel(label) {
20
+ return typeof label === "function" ? label() : label;
21
+ }
22
+ const NO_ENTRIES = Object.freeze([]);
23
+ /**
24
+ * Pure slot registry (no cordis; event emission and the renderer installation contract
25
+ * live in the runtime Service wrapper).
26
+ *
27
+ * The 'root' slot is the one a-priori declaration, seeded at construction
28
+ * (single/root, declared by the framework) — the render tree's root hole.
29
+ *
30
+ * Change propagation contract: versions bump and {@link SlotCore.onMutate}
31
+ * fires synchronously per mutation (registry state is consistent when they
32
+ * fire); {@link SlotCore.subscribeDeclaration} fires synchronously for each
33
+ * declaration lifetime boundary; {@link SlotCore.subscribe} notifications
34
+ * batch per microtask, so N same-tick mutations produce one notification per
35
+ * touched key.
36
+ */
37
+ var SlotCore = class {
38
+ records = /* @__PURE__ */ new Map();
39
+ mutateListeners = /* @__PURE__ */ new Set();
40
+ /** Shared-handle scope ledger: handle → the scope it first mounted under + live mount count. */
41
+ handleScopes = /* @__PURE__ */ new Map();
42
+ dirty = /* @__PURE__ */ new Set();
43
+ flushScheduled = false;
44
+ constructor() {
45
+ const root = this.record("root");
46
+ root.spec = {
47
+ kind: "single",
48
+ scope: "root"
49
+ };
50
+ root.declaredBy = "(built-in)";
51
+ root.declarationEpoch = 1;
52
+ }
53
+ register(options, component) {
54
+ const rec = this.records.get(options.name);
55
+ if (!rec?.spec) throw new Error(`slot "${options.name}" is not declared (a parent entry's children table must declare it)`);
56
+ const spec = rec.spec;
57
+ switch (spec.kind) {
58
+ case "single":
59
+ if (rec.entries.length > 0) throw new Error(`single slot "${options.name}" already has a registration`);
60
+ break;
61
+ case "keyed":
62
+ if (options.key === void 0) throw new Error(`keyed slot "${options.name}" requires options.key`);
63
+ if (rec.entries.some((e) => e.options.key === options.key)) throw new Error(`keyed slot "${options.name}" already has an entry for key "${options.key}"`);
64
+ break;
65
+ case "list":
66
+ if (options.id === void 0) throw new Error(`list slot "${options.name}" requires options.id`);
67
+ if (rec.entries.some((e) => e.options.id === options.id)) throw new Error(`list slot "${options.name}" already has an entry with id "${options.id}"`);
68
+ break;
69
+ case "chain":
70
+ if (options.select === void 0) throw new Error(`chain slot "${options.name}" requires options.select`);
71
+ break;
72
+ }
73
+ if (options.children) for (const childKey of Object.keys(options.children)) {
74
+ const childRec = this.records.get(childKey);
75
+ if (childRec?.spec) throw new Error(`slot "${childKey}" is already declared (by ${childRec.declaredBy ?? "an unknown entry"})`);
76
+ }
77
+ if (options.store !== void 0 && typeof options.store !== "function") {
78
+ const pinned = this.handleScopes.get(options.store);
79
+ if (pinned && pinned.scope !== spec.scope) throw new Error(`store handle mounted under "${options.name}" (scope "${spec.scope}") is already mounted under scope "${pinned.scope}" — one handle, one scope`);
80
+ if (pinned) pinned.count += 1;
81
+ else this.handleScopes.set(options.store, {
82
+ scope: spec.scope,
83
+ count: 1
84
+ });
85
+ }
86
+ const entry = {
87
+ component,
88
+ options: {
89
+ ...options.key !== void 0 ? { key: options.key } : {},
90
+ ...options.id !== void 0 ? { id: options.id } : {},
91
+ ...options.order !== void 0 ? { order: options.order } : {},
92
+ ...options.label !== void 0 ? { label: options.label } : {},
93
+ ...options.priority !== void 0 ? { priority: options.priority } : {}
94
+ },
95
+ ...options.select !== void 0 ? { select: options.select } : {},
96
+ ...options.inject !== void 0 ? { inject: options.inject } : {},
97
+ ...options.children !== void 0 ? { children: options.children } : {},
98
+ ...options.store !== void 0 ? { store: options.store } : {},
99
+ ...options.locale !== void 0 ? { locale: options.locale } : {},
100
+ ...options.registrant !== void 0 ? { registrant: options.registrant } : {}
101
+ };
102
+ const next = [...rec.entries, entry];
103
+ if (spec.kind === "list") next.sort((a, b) => (a.options.order ?? 0) - (b.options.order ?? 0));
104
+ if (spec.kind === "chain") next.sort((a, b) => (a.options.priority ?? 0) - (b.options.priority ?? 0));
105
+ rec.entries = next;
106
+ this.markDirty(options.name, rec);
107
+ if (options.children) {
108
+ const declarations = [];
109
+ for (const [childKey, childSpec] of Object.entries(options.children)) {
110
+ const childRec = this.record(childKey);
111
+ childRec.spec = childSpec;
112
+ childRec.declaredBy = `an entry in "${options.name}"${options.registrant ? ` (${options.registrant})` : ""}`;
113
+ childRec.declarationEpoch += 1;
114
+ declarations.push([childKey, childRec]);
115
+ }
116
+ for (const [childKey, childRec] of declarations) this.markDirty(childKey, childRec);
117
+ for (const [, childRec] of declarations) this.notifyDeclaration(childRec);
118
+ }
119
+ return () => {
120
+ if (!rec.entries.includes(entry)) return;
121
+ rec.entries = rec.entries.filter((e) => e !== entry);
122
+ this.markDirty(options.name, rec);
123
+ this.releaseEntry(entry);
124
+ };
125
+ }
126
+ /**
127
+ * Whether a previously obtained entry is still registered (the render
128
+ * machinery's stale-authorization probe: a retained renderSlot binding
129
+ * whose entry left the ledger must not render).
130
+ * @param entry - a previously read entry.
131
+ * @returns false once the entry's registration was disposed.
132
+ */
133
+ isLive(entry) {
134
+ for (const rec of this.records.values()) if (rec.entries.includes(entry)) return true;
135
+ return false;
136
+ }
137
+ /**
138
+ * Snapshot the registered entries for a key. Returns the cached array
139
+ * reference (stable between mutations — safe as a uSES getSnapshot source);
140
+ * empty for keys not (or no longer) declared, so renderers may probe ahead
141
+ * of plugin load order.
142
+ * @param key - slot key (dynamic: the render machinery holds keys as strings).
143
+ * @returns entries in registration (list: order) sequence.
144
+ */
145
+ entries(key) {
146
+ return this.records.get(key)?.entries ?? NO_ENTRIES;
147
+ }
148
+ /**
149
+ * Look up a slot's declared spec, narrowed by the SlotMap key.
150
+ * @param key - SlotMap key.
151
+ * @returns the spec, or undefined while undeclared.
152
+ */
153
+ spec(key) {
154
+ return this.records.get(key)?.spec;
155
+ }
156
+ /**
157
+ * Dynamic-key escape hatch for spec lookup — renderers resolving keys they
158
+ * only hold as strings (generic dispatch) use this wide form; statically
159
+ * keyed callers use {@link SlotCore.spec}.
160
+ * @param key - candidate slot key.
161
+ * @returns the wide-typed spec, or undefined while undeclared.
162
+ */
163
+ specDynamic(key) {
164
+ return this.records.get(key)?.spec;
165
+ }
166
+ /**
167
+ * Read the declaration lifetime of a key. Entry additions and removals do
168
+ * not change it; declaration creation and collapse each advance it.
169
+ * @param key - slot key.
170
+ * @returns monotonic epoch (0 before the first declaration).
171
+ */
172
+ declarationEpoch(key) {
173
+ return this.records.get(key)?.declarationEpoch ?? 0;
174
+ }
175
+ /**
176
+ * Subscribe to registration changes for a key (microtask-batched).
177
+ * Subscribing ahead of declaration is allowed; the declaration notifies.
178
+ * @param key - slot key.
179
+ * @param fn - change callback.
180
+ * @returns unsubscribe.
181
+ */
182
+ subscribe(key, fn) {
183
+ const rec = this.record(key);
184
+ rec.listeners.add(fn);
185
+ return () => {
186
+ rec.listeners.delete(fn);
187
+ };
188
+ }
189
+ /**
190
+ * Subscribe to declaration lifetime boundaries for a key. Notifications
191
+ * are synchronous so declaration teardown finishes before a subsequent
192
+ * same-tick registration can observe stale resources. Ordinary entry
193
+ * mutations do not notify this surface. A children table commits every
194
+ * sibling declaration before its first notification.
195
+ * @param key - slot key.
196
+ * @param fn - declaration or collapse callback.
197
+ * @returns unsubscribe.
198
+ */
199
+ subscribeDeclaration(key, fn) {
200
+ const rec = this.record(key);
201
+ rec.declarationListeners.add(fn);
202
+ return () => {
203
+ rec.declarationListeners.delete(fn);
204
+ };
205
+ }
206
+ /**
207
+ * Monotonic version for a key, bumped synchronously per mutation so a
208
+ * uSES getSnapshot read is never stale when its batched notification lands.
209
+ * @param key - slot key.
210
+ * @returns current version (0 for untouched keys).
211
+ */
212
+ getVersion(key) {
213
+ return this.records.get(key)?.version ?? 0;
214
+ }
215
+ /**
216
+ * Hook every mutation (the runtime Service wrapper bridges this to ctx.emit).
217
+ * Fires synchronously per mutation, unbatched — event semantics need one
218
+ * emission per change.
219
+ * @param fn - called with the mutated key.
220
+ * @returns unsubscribe.
221
+ */
222
+ onMutate(fn) {
223
+ this.mutateListeners.add(fn);
224
+ return () => {
225
+ this.mutateListeners.delete(fn);
226
+ };
227
+ }
228
+ /**
229
+ * Cascade for a removed entry: release its store mount and collapse every
230
+ * child slot it declared — specs clear, contributions empty (their stale
231
+ * disposers no-op), recursively down the declaration tree. One lifecycle
232
+ * axis: ledger rows, slots, contributions, and store mounts die together.
233
+ */
234
+ releaseEntry(entry) {
235
+ if (entry.store !== void 0 && typeof entry.store !== "function") {
236
+ const pinned = this.handleScopes.get(entry.store);
237
+ if (pinned && --pinned.count === 0) this.handleScopes.delete(entry.store);
238
+ }
239
+ if (!entry.children) return;
240
+ for (const childKey of Object.keys(entry.children)) {
241
+ const childRec = this.records.get(childKey);
242
+ /* v8 ignore next -- defensive: declaring always creates the record */
243
+ if (!childRec) continue;
244
+ const doomed = childRec.entries;
245
+ childRec.spec = void 0;
246
+ childRec.declaredBy = void 0;
247
+ childRec.declarationEpoch += 1;
248
+ childRec.entries = NO_ENTRIES;
249
+ this.markDirty(childKey, childRec);
250
+ this.notifyDeclaration(childRec);
251
+ for (const dead of doomed) this.releaseEntry(dead);
252
+ }
253
+ }
254
+ record(key) {
255
+ let rec = this.records.get(key);
256
+ if (!rec) {
257
+ rec = {
258
+ spec: void 0,
259
+ declaredBy: void 0,
260
+ declarationEpoch: 0,
261
+ entries: NO_ENTRIES,
262
+ version: 0,
263
+ listeners: /* @__PURE__ */ new Set(),
264
+ declarationListeners: /* @__PURE__ */ new Set()
265
+ };
266
+ this.records.set(key, rec);
267
+ }
268
+ return rec;
269
+ }
270
+ markDirty(key, rec) {
271
+ rec.version += 1;
272
+ for (const fn of [...this.mutateListeners]) fn(key);
273
+ this.dirty.add(rec);
274
+ if (!this.flushScheduled) {
275
+ this.flushScheduled = true;
276
+ queueMicrotask(() => {
277
+ this.flush();
278
+ });
279
+ }
280
+ }
281
+ notifyDeclaration(rec) {
282
+ for (const fn of [...rec.declarationListeners]) fn();
283
+ }
284
+ flush() {
285
+ this.flushScheduled = false;
286
+ const dirty = [...this.dirty];
287
+ this.dirty.clear();
288
+ for (const rec of dirty) for (const fn of [...rec.listeners]) fn();
289
+ }
290
+ };
291
+ //#endregion
292
+ export { SlotCore, SlotOwnershipError, StaleAuthorizationError, resolveSlotLabel };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-slots`.
4
+ * @module @deepseek-ai/dsh-client-ui-slots/invariant
5
+ */
6
+ const PACKAGE_NAME = "@deepseek-ai/dsh-client-ui-slots";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-slots-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: a zero-dependency pure registry core — it emits no
13
+ * cordis events itself (the runtime SlotsService wrapper owns the event
14
+ * bridge and its invariants); define/register/dispose sequencing is asserted
15
+ * directly by this package's behavior specs.
16
+ */
17
+ const install = () => {};
18
+ /**
19
+ * Register this package's invariant companion.
20
+ * @param ctx - Cordis context carrying the invariant service.
21
+ * @returns the installed registration's disposer after setup succeeds.
22
+ */
23
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
+ //#endregion
25
+ export { apply, inject, name };