@prettier-ai/dsh-client-ui-renderer 0.1.2-alpha.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/lib/client.js ADDED
@@ -0,0 +1,1449 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@prettier-ai/dsh-client-ui-renderer",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
9
+ //#endregion
10
+ let react = require("react");
11
+ let react_dom = require("react-dom");
12
+ let react_dom_client = require("react-dom/client");
13
+ let react_jsx_runtime = require("react/jsx-runtime");
14
+ let _prettier_ai_dsh_client_ui_slots = require("@prettier-ai/dsh-client-ui-slots");
15
+ let _prettier_ai_cordis = require("@prettier-ai/cordis");
16
+ //#region ../../../node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.3.1/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.js
17
+ /**
18
+ * @license React
19
+ * use-sync-external-store-shim.production.min.js
20
+ *
21
+ * Copyright (c) Facebook, Inc. and its affiliates.
22
+ *
23
+ * This source code is licensed under the MIT license found in the
24
+ * LICENSE file in the root directory of this source tree.
25
+ */
26
+ var require_use_sync_external_store_shim_production_min = /* @__PURE__ */ __commonJSMin(((exports) => {
27
+ var e = require("react");
28
+ function h(a, b) {
29
+ return a === b && (0 !== a || 1 / a === 1 / b) || a !== a && b !== b;
30
+ }
31
+ var k = "function" === typeof Object.is ? Object.is : h, l = e.useState, m = e.useEffect, n = e.useLayoutEffect, p = e.useDebugValue;
32
+ function q(a, b) {
33
+ var d = b(), f = l({ inst: {
34
+ value: d,
35
+ getSnapshot: b
36
+ } }), c = f[0].inst, g = f[1];
37
+ n(function() {
38
+ c.value = d;
39
+ c.getSnapshot = b;
40
+ r(c) && g({ inst: c });
41
+ }, [
42
+ a,
43
+ d,
44
+ b
45
+ ]);
46
+ m(function() {
47
+ r(c) && g({ inst: c });
48
+ return a(function() {
49
+ r(c) && g({ inst: c });
50
+ });
51
+ }, [a]);
52
+ p(d);
53
+ return d;
54
+ }
55
+ function r(a) {
56
+ var b = a.getSnapshot;
57
+ a = a.value;
58
+ try {
59
+ var d = b();
60
+ return !k(a, d);
61
+ } catch (f) {
62
+ return !0;
63
+ }
64
+ }
65
+ function t(a, b) {
66
+ return b();
67
+ }
68
+ var u = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? t : q;
69
+ exports.useSyncExternalStore = void 0 !== e.useSyncExternalStore ? e.useSyncExternalStore : u;
70
+ }));
71
+ //#endregion
72
+ //#region ../../../node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.3.1/node_modules/use-sync-external-store/shim/index.js
73
+ var require_shim = /* @__PURE__ */ __commonJSMin(((exports, module) => {
74
+ module.exports = require_use_sync_external_store_shim_production_min();
75
+ }));
76
+ //#endregion
77
+ //#region ../../../node_modules/.pnpm/use-sync-external-store@1.2.0_react@18.3.1/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js
78
+ /**
79
+ * @license React
80
+ * use-sync-external-store-shim/with-selector.production.min.js
81
+ *
82
+ * Copyright (c) Facebook, Inc. and its affiliates.
83
+ *
84
+ * This source code is licensed under the MIT license found in the
85
+ * LICENSE file in the root directory of this source tree.
86
+ */
87
+ var require_with_selector_production_min = /* @__PURE__ */ __commonJSMin(((exports) => {
88
+ var h = require("react"), n = require_shim();
89
+ function p(a, b) {
90
+ return a === b && (0 !== a || 1 / a === 1 / b) || a !== a && b !== b;
91
+ }
92
+ var q = "function" === typeof Object.is ? Object.is : p, r = n.useSyncExternalStore, t = h.useRef, u = h.useEffect, v = h.useMemo, w = h.useDebugValue;
93
+ exports.useSyncExternalStoreWithSelector = function(a, b, e, l, g) {
94
+ var c = t(null);
95
+ if (null === c.current) {
96
+ var f = {
97
+ hasValue: !1,
98
+ value: null
99
+ };
100
+ c.current = f;
101
+ } else f = c.current;
102
+ c = v(function() {
103
+ function a(a) {
104
+ if (!c) {
105
+ c = !0;
106
+ d = a;
107
+ a = l(a);
108
+ if (void 0 !== g && f.hasValue) {
109
+ var b = f.value;
110
+ if (g(b, a)) return k = b;
111
+ }
112
+ return k = a;
113
+ }
114
+ b = k;
115
+ if (q(d, a)) return b;
116
+ var e = l(a);
117
+ if (void 0 !== g && g(b, e)) return b;
118
+ d = a;
119
+ return k = e;
120
+ }
121
+ var c = !1, d, k, m = void 0 === e ? null : e;
122
+ return [function() {
123
+ return a(b());
124
+ }, null === m ? void 0 : function() {
125
+ return a(m());
126
+ }];
127
+ }, [
128
+ b,
129
+ e,
130
+ l,
131
+ g
132
+ ]);
133
+ var d = r(a, c[0], c[1]);
134
+ u(function() {
135
+ f.hasValue = !0;
136
+ f.value = d;
137
+ }, [d]);
138
+ w(d);
139
+ return d;
140
+ };
141
+ }));
142
+ //#endregion
143
+ //#region lib/types/client/bind.js
144
+ var import_with_selector = (/* @__PURE__ */ __commonJSMin(((exports, module) => {
145
+ module.exports = require_with_selector_production_min();
146
+ })))();
147
+ /**
148
+ * Bind a bare observable source to a typed uSES selector hook.
149
+ * subscribe/getSnapshot are captured once per source into stable closures
150
+ * (also re-binds `this` for method-based sources), so components never
151
+ * resubscribe across renders. Equality defaults to Object.is.
152
+ * @param w - snapshot source (engine store, Session object, store instance).
153
+ * @returns the selector hook.
154
+ */
155
+ function bindSnapshotSelector(w) {
156
+ const subscribe = (fn) => w.subscribe(fn);
157
+ const getSnapshot = () => w.getSnapshot();
158
+ return function useSelector(sel, eq) {
159
+ return (0, import_with_selector.useSyncExternalStoreWithSelector)(subscribe, getSnapshot, void 0, sel, eq);
160
+ };
161
+ }
162
+ //#endregion
163
+ //#region lib/types/client/bindings.js
164
+ /** Internal React bindings for renderer hosts and standard-source scopes. */
165
+ /** Missing renderer assembly dependency. */
166
+ var SlotAssemblyError = class extends Error {};
167
+ /** In-package renderer host context. */
168
+ const HostContext = (0, react.createContext)(null);
169
+ /**
170
+ * Read the installed renderer host.
171
+ * @returns the host API.
172
+ */
173
+ function useHost() {
174
+ const host = (0, react.useContext)(HostContext);
175
+ if (host === null) throw new SlotAssemblyError("slot machinery rendered outside the installed renderer tree");
176
+ return host;
177
+ }
178
+ const RootBindingContext = (0, react.createContext)(null);
179
+ const ScopeBindingContext = (0, react.createContext)(null);
180
+ /**
181
+ * Read the root standard-source binding.
182
+ * @returns the current root binding.
183
+ */
184
+ function useRootBinding() {
185
+ const binding = (0, react.useContext)(RootBindingContext);
186
+ if (binding === null) throw new SlotAssemblyError("slot rendered outside the root standard-source provider");
187
+ return binding;
188
+ }
189
+ /**
190
+ * Read the current-session-optional binding.
191
+ * @returns a binding whose key is absent when no Session is selected.
192
+ */
193
+ function useScopeBinding() {
194
+ const binding = (0, react.useContext)(ScopeBindingContext);
195
+ if (binding === null) throw new SlotAssemblyError("scoped slot rendered outside its scope provider");
196
+ return binding;
197
+ }
198
+ /**
199
+ * Bind one observable source to an identity-stable selector Hook.
200
+ * @param source - observable source.
201
+ * @returns cached selector Hook.
202
+ */
203
+ function observableHook(source) {
204
+ let hook = hookCache.get(source);
205
+ if (hook === void 0) {
206
+ hook = bindSnapshotSelector(source);
207
+ hookCache.set(source, hook);
208
+ }
209
+ return hook;
210
+ }
211
+ const hookCache = /* @__PURE__ */ new WeakMap();
212
+ const absentSource = {
213
+ getSnapshot: () => void 0,
214
+ subscribe: () => () => {}
215
+ };
216
+ /**
217
+ * Bind an optional source without changing Hook call order.
218
+ * @param source - current source, or absence.
219
+ * @returns selector Hook returning `undefined` while absent.
220
+ */
221
+ function maybeObservableHook(source) {
222
+ if (source !== void 0) return observableHook(source);
223
+ return useAbsentSnapshot;
224
+ }
225
+ function useAbsentSnapshot(_selector, _equal) {
226
+ observableHook(absentSource)(() => void 0);
227
+ }
228
+ /**
229
+ * Bind an open-key source family.
230
+ * @param source - keyed resolver, or absence for an optional scope.
231
+ * @returns cached keyed selector Hook.
232
+ */
233
+ function keyedObservableHook(source) {
234
+ if (source === void 0) return absentKeyedHook;
235
+ let hook = keyedHookCache.get(source);
236
+ if (hook === void 0) {
237
+ hook = (key, selector, equal) => {
238
+ return observableHook(source(key) ?? absentSource)(selector ?? identity, equal);
239
+ };
240
+ keyedHookCache.set(source, hook);
241
+ }
242
+ return hook;
243
+ }
244
+ const keyedHookCache = /* @__PURE__ */ new WeakMap();
245
+ const identity = (value) => value;
246
+ const absentKeyedHook = (_key, selector, equal) => observableHook(absentSource)(selector ?? identity, equal);
247
+ /** Subscribe the tree to the atomically assembled root standard-source roster. */
248
+ function RootStandardProvider({ children }) {
249
+ const binding = observableHook(useHost().root)((value) => value);
250
+ return (0, react_jsx_runtime.jsx)(RootBindingContext.Provider, {
251
+ value: binding,
252
+ children
253
+ });
254
+ }
255
+ /** Subscribe to the scope roster before resolving and binding its current adapter. */
256
+ function ScopeProvider({ scope, children }) {
257
+ const host = useHost();
258
+ observableHook(host.scopeRevision)((value) => value);
259
+ const adapter = host.scope(scope);
260
+ if (adapter === void 0) throw new SlotAssemblyError(`scope '${scope}' rendered without an installed adapter`);
261
+ const binding = observableHook(adapter.current)((value) => value);
262
+ return (0, react_jsx_runtime.jsx)(ScopeBindingContext.Provider, {
263
+ value: binding,
264
+ children
265
+ });
266
+ }
267
+ //#endregion
268
+ //#region lib/types/client/scoped-slots.js
269
+ /**
270
+ * React renderer for declarative slots. Per-entry bindings enforce child
271
+ * authorization, and entry boundaries contain registrant failures.
272
+ */
273
+ /**
274
+ * Per-entry renderSlot bindings. The binding is identity-stable per entry
275
+ * (memoized components must not resubscribe on unrelated re-renders) and dies
276
+ * with the entry: a retained closure calling after the entry's disposal hits
277
+ * the in-ledger check and throws.
278
+ */
279
+ const renderSlotCache = /* @__PURE__ */ new WeakMap();
280
+ function boundRenderSlot(host, entry) {
281
+ let binding = renderSlotCache.get(entry);
282
+ if (!binding) {
283
+ binding = (key, owner, opts) => {
284
+ if (!host.isLive(entry)) throw new _prettier_ai_dsh_client_ui_slots.StaleAuthorizationError(`renderSlot('${key}') from a disposed registration`);
285
+ const declared = entry.children?.[key];
286
+ if (declared === void 0) throw new _prettier_ai_dsh_client_ui_slots.SlotOwnershipError(`slot '${key}' is not declared by this entry's children`);
287
+ if (declared.kind === "chain") throw new _prettier_ai_dsh_client_ui_slots.SlotOwnershipError(`slot '${key}' is declared 'chain' — use renderSlotChain`);
288
+ return (0, react_jsx_runtime.jsx)(SlotOutlet, {
289
+ slotKey: key,
290
+ ownerProps: owner,
291
+ opts
292
+ });
293
+ };
294
+ renderSlotCache.set(entry, binding);
295
+ }
296
+ return binding;
297
+ }
298
+ /**
299
+ * Per-entry renderSlotChain bindings: identity-stable per entry (same cache
300
+ * axis as renderSlot — a per-frame dispatch must not rebuild the binding) and
301
+ * dead with the entry. The chain-kind check is the plain-JS backstop twin of
302
+ * the declaration check; typed callers are narrowed to chain keys.
303
+ */
304
+ const renderSlotChainCache = /* @__PURE__ */ new WeakMap();
305
+ function boundRenderSlotChain(host, entry) {
306
+ let binding = renderSlotChainCache.get(entry);
307
+ if (!binding) {
308
+ binding = (key, owner, opts) => {
309
+ if (!host.isLive(entry)) throw new _prettier_ai_dsh_client_ui_slots.StaleAuthorizationError(`renderSlotChain('${key}') from a disposed registration`);
310
+ const declared = entry.children?.[key];
311
+ if (declared === void 0) throw new _prettier_ai_dsh_client_ui_slots.SlotOwnershipError(`slot '${key}' is not declared by this entry's children`);
312
+ if (declared.kind !== "chain") throw new _prettier_ai_dsh_client_ui_slots.SlotOwnershipError(`slot '${key}' is declared '${declared.kind}', not 'chain' — use renderSlot`);
313
+ return (0, react_jsx_runtime.jsx)(SlotOutlet, {
314
+ slotKey: key,
315
+ ownerProps: owner,
316
+ opts
317
+ });
318
+ };
319
+ renderSlotChainCache.set(entry, binding);
320
+ }
321
+ return binding;
322
+ }
323
+ /**
324
+ * Inject results cache: root entries per entry, session entries per
325
+ * (entry x scope binding). WeakMap keys are entry/binding objects (both
326
+ * identity-stable per registration/session scope), so cache lifetime rides
327
+ * the same axes as the values it memoizes.
328
+ */
329
+ const rootInjectCache = /* @__PURE__ */ new WeakMap();
330
+ const sessionInjectCache = /* @__PURE__ */ new WeakMap();
331
+ const sessionMaybeInjectCache = /* @__PURE__ */ new WeakMap();
332
+ const EMPTY_INJECTED_PROPS = {};
333
+ function runInject(entry, binding, actions) {
334
+ const inject = entry.inject;
335
+ if (!inject) return EMPTY_INJECTED_PROPS;
336
+ const args = [];
337
+ if (binding !== void 0) args.push(binding.key);
338
+ if (actions !== void 0) args.push(actions);
339
+ return bindInjectHooks(inject(...args));
340
+ }
341
+ /**
342
+ * Normalize one entry-owned inject face on its existing cache axis. Its hooks
343
+ * compartment remains the original Observable-only contract.
344
+ */
345
+ function bindInjectHooks(face) {
346
+ const sources = face["hooks"];
347
+ if (sources === void 0) return face;
348
+ const { hooks: _hooks, ...rest } = face;
349
+ const bound = rest;
350
+ for (const [name, source] of Object.entries(sources)) {
351
+ const hookName = (0, _prettier_ai_dsh_client_ui_slots.standardHookPropName)(name);
352
+ bound[hookName] = observableHook(source);
353
+ }
354
+ return bound;
355
+ }
356
+ const slotInjectCache = /* @__PURE__ */ new WeakMap();
357
+ const EMPTY_SLOT_INJECT = { props: EMPTY_INJECTED_PROPS };
358
+ /** Normalize one dispatcher-owned inject face by its stable object identity. */
359
+ function cachedSlotInject(face) {
360
+ if (face === void 0) return EMPTY_SLOT_INJECT;
361
+ let bound = slotInjectCache.get(face);
362
+ if (bound !== void 0) return bound;
363
+ const definitions = face["hooks"];
364
+ if (definitions === void 0) {
365
+ bound = { props: face };
366
+ slotInjectCache.set(face, bound);
367
+ return bound;
368
+ }
369
+ const { hooks: _hooks, ...rest } = face;
370
+ const props = rest;
371
+ let factories;
372
+ for (const [name, definition] of Object.entries(definitions)) {
373
+ const hookName = (0, _prettier_ai_dsh_client_ui_slots.standardHookPropName)(name);
374
+ if (typeof definition === "function") {
375
+ factories ??= {};
376
+ factories[name] = definition;
377
+ } else props[hookName] = observableHook(definition);
378
+ }
379
+ bound = factories === void 0 ? { props } : {
380
+ props,
381
+ slotHookFactories: factories
382
+ };
383
+ slotInjectCache.set(face, bound);
384
+ return bound;
385
+ }
386
+ /** Bind deferred slot-level factories for one stable renderSlot occurrence. */
387
+ function bindSlotHookFactories(factories, standard, hookContext) {
388
+ const hooks = {};
389
+ for (const [name, factory] of Object.entries(factories)) {
390
+ const hookName = (0, _prettier_ai_dsh_client_ui_slots.standardHookPropName)(name);
391
+ hooks[hookName] = factory(standard, hookContext);
392
+ }
393
+ return hooks;
394
+ }
395
+ function cachedRootInject(entry, actions) {
396
+ let props = rootInjectCache.get(entry);
397
+ if (!props) {
398
+ props = runInject(entry, void 0, actions);
399
+ rootInjectCache.set(entry, props);
400
+ }
401
+ return props;
402
+ }
403
+ function cachedSessionInject(entry, binding, actions) {
404
+ let perBinding = sessionInjectCache.get(entry);
405
+ if (!perBinding) {
406
+ perBinding = /* @__PURE__ */ new WeakMap();
407
+ sessionInjectCache.set(entry, perBinding);
408
+ }
409
+ let props = perBinding.get(binding);
410
+ if (!props) {
411
+ props = runInject(entry, binding, actions);
412
+ perBinding.set(binding, props);
413
+ }
414
+ return props;
415
+ }
416
+ function cachedSessionMaybeInject(entry, binding, actions) {
417
+ let perBinding = sessionMaybeInjectCache.get(entry);
418
+ if (!perBinding) {
419
+ perBinding = /* @__PURE__ */ new WeakMap();
420
+ sessionMaybeInjectCache.set(entry, perBinding);
421
+ }
422
+ let props = perBinding.get(binding);
423
+ if (!props) {
424
+ props = runInject(entry, binding, actions);
425
+ perBinding.set(binding, props);
426
+ }
427
+ return props;
428
+ }
429
+ /**
430
+ * Locale `t` seat bindings, cached per (face, namespace, revision). The
431
+ * revision is part of the cache key ON PURPOSE: a locale switch mints a NEW
432
+ * function reference per namespace, so `React.memo` components taking `t`
433
+ * re-render through ordinary shallow comparison — freshness rides identity,
434
+ * no extra invalidation channel. Within one revision the reference is stable
435
+ * (memoized children do not churn on unrelated re-renders).
436
+ */
437
+ const localeSeatCache = /* @__PURE__ */ new WeakMap();
438
+ function localeSeat(face, ns) {
439
+ let perNs = localeSeatCache.get(face);
440
+ if (!perNs) {
441
+ perNs = /* @__PURE__ */ new Map();
442
+ localeSeatCache.set(face, perNs);
443
+ }
444
+ const revision = face.getSnapshot().revision;
445
+ const cached = perNs.get(ns);
446
+ if (cached && cached.revision === revision) return cached.t;
447
+ const bound = face.bind(ns);
448
+ const t = (key, params) => bound(key, params);
449
+ perNs.set(ns, {
450
+ revision,
451
+ t
452
+ });
453
+ return t;
454
+ }
455
+ const noopSubscribe = () => () => {};
456
+ const zeroRevision = () => 0;
457
+ /**
458
+ * Per-face subscribe/getSnapshot closure pair. Cached by face identity: the
459
+ * face is one global source shared by every outlet, and uSES resubscribes
460
+ * whenever the subscribe reference changes — fresh closures per render would
461
+ * churn one unsubscribe/resubscribe pair per outlet per render.
462
+ */
463
+ const localeSubscriptionCache = /* @__PURE__ */ new WeakMap();
464
+ function localeSubscription(face) {
465
+ let cached = localeSubscriptionCache.get(face);
466
+ if (!cached) {
467
+ cached = {
468
+ subscribe: (fn) => face.subscribe(fn),
469
+ getRevision: () => face.getSnapshot().revision
470
+ };
471
+ localeSubscriptionCache.set(face, cached);
472
+ }
473
+ return cached;
474
+ }
475
+ /**
476
+ * Subscribe an outlet to the installed locale face's revision (0 while none
477
+ * is installed — exactly one uSES call either way, keeping hook order
478
+ * stable). Every outlet re-renders on a locale switch; entry bodies then
479
+ * re-derive their `t` seat at the new revision. The face must be installed
480
+ * before the first render that needs it — a face appearing later has no
481
+ * notification channel to already-mounted outlets.
482
+ */
483
+ function useLocaleRevision(face) {
484
+ const subscription = face !== void 0 ? localeSubscription(face) : void 0;
485
+ return (0, react.useSyncExternalStore)(subscription?.subscribe ?? noopSubscribe, subscription?.getRevision ?? zeroRevision);
486
+ }
487
+ /**
488
+ * Entry-identity React keys for entry boundaries. An outlet renders one
489
+ * winner per position (single/keyed/list cell head, chain election) through
490
+ * an error boundary; without a key, a boundary that failed on entry A would
491
+ * survive a winner change (re-election, shadowing fallback after an
492
+ * abdication, HMR re-registration) and keep a healthy entry B blacked out.
493
+ * Keying by entry identity remounts the boundary fresh whenever the winner
494
+ * changes (entries are identity-stable per registration, so the key is
495
+ * stable while the same entry stays the winner).
496
+ */
497
+ let nextEntryKey = 0;
498
+ const entryKeys = /* @__PURE__ */ new WeakMap();
499
+ function entryKeyOf(entry) {
500
+ let key = entryKeys.get(entry);
501
+ if (key === void 0) {
502
+ key = nextEntryKey++;
503
+ entryKeys.set(entry, key);
504
+ }
505
+ return key;
506
+ }
507
+ /**
508
+ * Per-entry isolation: one registrant crashing (component render or inject
509
+ * factory) must not take down siblings. Assembly errors (missing providers)
510
+ * rethrow — a miswired shell must fail loud, not degrade into fallbacks.
511
+ * Every catch reports through `onEntryError` (the ledger's supervision
512
+ * seam); for shadowing kinds the report abdicates the entry, the outlet
513
+ * re-renders onto the cell's next survivor, and this boundary's crash face
514
+ * only shows until that re-render lands (permanently once the cell is dry —
515
+ * the outlet then owns the crash face).
516
+ */
517
+ var SlotErrorBoundary = class extends react.Component {
518
+ state = { failed: false };
519
+ static getDerivedStateFromError(error) {
520
+ if (error instanceof SlotAssemblyError) throw error;
521
+ return { failed: true };
522
+ }
523
+ componentDidCatch(error) {
524
+ console.error(`slot entry crashed in '${this.props.slotKey}':`, error);
525
+ this.props.onEntryError(error);
526
+ }
527
+ render() {
528
+ if (this.state.failed) return (0, react_jsx_runtime.jsx)("div", { "data-slot-error": this.props.slotKey });
529
+ return this.props.children;
530
+ }
531
+ };
532
+ const rootStandardCache = /* @__PURE__ */ new WeakMap();
533
+ const sessionStandardCache = /* @__PURE__ */ new WeakMap();
534
+ const sessionMaybeStandardCache = /* @__PURE__ */ new WeakMap();
535
+ /** Materialize one binding into stable framework Hook and plain-prop seats. */
536
+ function materializeStandardBinding(binding, optional) {
537
+ const standard = { ...binding.props };
538
+ for (const [name, source] of Object.entries(binding.hooks)) {
539
+ if (source === void 0 && !optional) throw new SlotAssemblyError(`strict standard hook '${name}' has no source`);
540
+ standard[(0, _prettier_ai_dsh_client_ui_slots.standardHookPropName)(name)] = optional ? maybeObservableHook(source) : observableHook(source);
541
+ }
542
+ for (const [name, source] of Object.entries(binding.keyedHooks)) {
543
+ if (source === void 0 && !optional) throw new SlotAssemblyError(`strict keyed standard hook '${name}' has no source resolver`);
544
+ standard[(0, _prettier_ai_dsh_client_ui_slots.standardHookPropName)(name)] = keyedObservableHook(source);
545
+ }
546
+ return standard;
547
+ }
548
+ /** Stable official-props object used by contextual Hook factories. */
549
+ function standardProps(scope, rootBinding, scopeBinding) {
550
+ let root = rootStandardCache.get(rootBinding);
551
+ if (root === void 0) {
552
+ root = materializeStandardBinding(rootBinding, false);
553
+ rootStandardCache.set(rootBinding, root);
554
+ }
555
+ if (scope === "root") return root;
556
+ if (scopeBinding === void 0) throw new SlotAssemblyError(`scope '${scope}' rendered without a standard-source binding`);
557
+ const cache = scope === "session" ? sessionStandardCache : sessionMaybeStandardCache;
558
+ let perScope = cache.get(rootBinding);
559
+ if (perScope === void 0) {
560
+ perScope = /* @__PURE__ */ new WeakMap();
561
+ cache.set(rootBinding, perScope);
562
+ }
563
+ let standard = perScope.get(scopeBinding);
564
+ if (standard !== void 0) return standard;
565
+ standard = {
566
+ ...root,
567
+ ...materializeStandardBinding(scopeBinding, scope === "session-maybe")
568
+ };
569
+ perScope.set(scopeBinding, standard);
570
+ return standard;
571
+ }
572
+ const scopeAreaCache = /* @__PURE__ */ new WeakMap();
573
+ /** Bind one domain-owned scope area renderer to the current scope binding. */
574
+ function scopeAreaProvider(adapter) {
575
+ let Provider = scopeAreaCache.get(adapter);
576
+ if (Provider !== void 0) return Provider;
577
+ if (adapter.renderArea === void 0) throw new SlotAssemblyError("scope 'session' adapter does not provide its area renderer");
578
+ const renderArea = adapter.renderArea.bind(adapter);
579
+ Provider = function ScopeAreaProvider(props) {
580
+ return renderArea(useScopeBinding(), props);
581
+ };
582
+ scopeAreaCache.set(adapter, Provider);
583
+ return Provider;
584
+ }
585
+ /**
586
+ * Standard-kit synthesis shared by both scope branches: the global
587
+ * useSessions/useWorkspaces hooks, the per-session provide bundle (every
588
+ * `hooks` source becomes a `use<Name>` selector hook — useSession is the
589
+ * runtime's own 'session' contribution, no special case — and `props` spread
590
+ * verbatim), the store pair when declared, the renderSlot binding when
591
+ * children are declared, and the SessionProvider seat when the children
592
+ * declare a session-scope slot. Hosts hand out BARE observable sources
593
+ * (hooks never cross the host contract); every hook is bound HERE, cached
594
+ * per source (observableHook), so spreading a fresh kit object per render
595
+ * never churns child subscriptions.
596
+ */
597
+ function standardKit(host, entry, scope, rootBinding, scopeBinding) {
598
+ const standard = standardProps(scope, rootBinding, scopeBinding);
599
+ const kit = { ...standard };
600
+ if (entry.locale !== void 0) {
601
+ const face = host.locale;
602
+ if (face === void 0) throw new SlotAssemblyError(`entry declares locale namespace '${entry.locale}' but no locale face is installed (locale plugin missing from the composition?)`);
603
+ kit["t"] = localeSeat(face, entry.locale);
604
+ }
605
+ const scopedStoreBinding = scopeBinding?.key === void 0 ? void 0 : scopeBinding;
606
+ const store = host.storeOf(entry, scopedStoreBinding);
607
+ if (store !== void 0) {
608
+ kit["useStore"] = observableHook(store);
609
+ kit["actions"] = store.actions;
610
+ }
611
+ if (entry.children !== void 0) {
612
+ kit["renderSlot"] = boundRenderSlot(host, entry);
613
+ if (Object.values(entry.children).some((spec) => spec.kind === "chain")) kit["renderSlotChain"] = boundRenderSlotChain(host, entry);
614
+ if (Object.values(entry.children).some((spec) => spec.scope === "session")) {
615
+ const adapter = host.scope("session");
616
+ if (adapter === void 0) throw new SlotAssemblyError("entry declares a session child without an installed 'session' scope adapter");
617
+ kit["SessionProvider"] = scopeAreaProvider(adapter);
618
+ }
619
+ }
620
+ return {
621
+ kit,
622
+ standard,
623
+ actions: store?.actions
624
+ };
625
+ }
626
+ /**
627
+ * One rendered entry: standard kit + cached entry inject + common slot inject
628
+ * + owner props (owner wins). The shares are erased at this render boundary;
629
+ * the registration and renderSlot seams already proved their contracts.
630
+ */
631
+ function ContextualEntry({ slotKey, Comp, kit, standard, injected, slotInjected, ownerProps, hookContext, hasHookContext }) {
632
+ const contextual = (0, react.useMemo)(() => {
633
+ if (!hasHookContext) throw new SlotAssemblyError(`slot '${slotKey}' has contextual injected Hooks but no hookContext`);
634
+ return bindSlotHookFactories(slotInjected.slotHookFactories, standard, hookContext);
635
+ }, [
636
+ hasHookContext,
637
+ hookContext,
638
+ slotInjected.slotHookFactories,
639
+ slotKey,
640
+ standard
641
+ ]);
642
+ return (0, react_jsx_runtime.jsx)(Comp, {
643
+ ...kit,
644
+ ...injected,
645
+ ...slotInjected.props,
646
+ ...contextual,
647
+ ...ownerProps
648
+ });
649
+ }
650
+ function renderEntry(slotKey, Comp, kit, standard, injected, slotInjected, ownerProps, hookContext, hasHookContext) {
651
+ if (slotInjected.slotHookFactories === void 0) return (0, react_jsx_runtime.jsx)(Comp, {
652
+ ...kit,
653
+ ...injected,
654
+ ...slotInjected.props,
655
+ ...ownerProps
656
+ });
657
+ return (0, react_jsx_runtime.jsx)(ContextualEntry, {
658
+ slotKey,
659
+ Comp,
660
+ kit,
661
+ standard,
662
+ injected,
663
+ slotInjected,
664
+ ownerProps,
665
+ hookContext,
666
+ hasHookContext
667
+ });
668
+ }
669
+ function SessionEntry({ entry, ownerProps, binding, slotKey, slotInjected, hookContext, hasHookContext }) {
670
+ const host = useHost();
671
+ const rootBinding = useRootBinding();
672
+ const Comp = entry.component;
673
+ const { kit, standard, actions } = standardKit(host, entry, "session", rootBinding, binding);
674
+ return renderEntry(slotKey, Comp, kit, standard, cachedSessionInject(entry, binding, actions), slotInjected, ownerProps, hookContext, hasHookContext);
675
+ }
676
+ function SessionMaybeEntryBody({ entry, ownerProps, binding, slotKey, slotInjected, hookContext, hasHookContext }) {
677
+ const host = useHost();
678
+ const rootBinding = useRootBinding();
679
+ const Comp = entry.component;
680
+ const { kit, standard, actions } = standardKit(host, entry, "session-maybe", rootBinding, binding);
681
+ return renderEntry(slotKey, Comp, kit, standard, cachedSessionMaybeInject(entry, binding, actions), slotInjected, ownerProps, hookContext, hasHookContext);
682
+ }
683
+ /**
684
+ * Session-maybe identity: adoption — the ONLY behavior (there is no
685
+ * hold-identity-forever mode). An incarnation born session-less ADOPTS the
686
+ * first session that arrives: identity holds across that one transition
687
+ * (undefined → first id), so a blank shell's DOM survives the moment a
688
+ * session appears. From then on the entry behaves exactly like a strict
689
+ * session entry: switching to a DIFFERENT session remounts (component-local
690
+ * state must not leak between sessions), and dropping back to no-session
691
+ * remounts into a fresh blank incarnation, which will adopt again.
692
+ * Component-local per-session state therefore clears by construction; state
693
+ * that must SURVIVE a switch belongs in session-bound sources (machine,
694
+ * store, hooks) — the existing layering rule, now load-bearing.
695
+ */
696
+ function SessionMaybeEntry({ entry, ownerProps, slotKey, slotInjected, hookContext, hasHookContext }) {
697
+ const binding = useScopeBinding();
698
+ const [state, setState] = (0, react.useState)(FIRST_INCARNATION);
699
+ let { adopted, epoch } = state;
700
+ if (binding.key !== void 0 && adopted === void 0) {
701
+ adopted = binding.key;
702
+ setState({
703
+ adopted,
704
+ epoch
705
+ });
706
+ } else if (adopted !== void 0 && binding.key !== void 0 && binding.key !== adopted) {
707
+ adopted = binding.key;
708
+ epoch += 1;
709
+ setState({
710
+ adopted,
711
+ epoch
712
+ });
713
+ } else if (adopted !== void 0 && binding.key === void 0) {
714
+ adopted = void 0;
715
+ epoch += 1;
716
+ setState({
717
+ adopted,
718
+ epoch
719
+ });
720
+ }
721
+ return (0, react_jsx_runtime.jsx)(SessionMaybeEntryBody, {
722
+ entry,
723
+ ownerProps,
724
+ binding,
725
+ slotKey,
726
+ slotInjected,
727
+ hookContext,
728
+ hasHookContext
729
+ }, epoch);
730
+ }
731
+ const FIRST_INCARNATION = {
732
+ adopted: void 0,
733
+ epoch: 0
734
+ };
735
+ function RootEntry({ entry, ownerProps, slotKey, slotInjected, hookContext, hasHookContext }) {
736
+ const host = useHost();
737
+ const rootBinding = useRootBinding();
738
+ const Comp = entry.component;
739
+ const { kit, standard, actions } = standardKit(host, entry, "root", rootBinding, void 0);
740
+ return renderEntry(slotKey, Comp, kit, standard, cachedRootInject(entry, actions), slotInjected, ownerProps, hookContext, hasHookContext);
741
+ }
742
+ function StrictSessionEntry({ slotKey, entry, ownerProps, slotInjected, hookContext, hasHookContext, onEntryError }) {
743
+ const binding = useScopeBinding();
744
+ if (binding.key === void 0) throw new SlotAssemblyError(`strict session slot '${slotKey}' rendered without a scope binding`);
745
+ return (0, react_jsx_runtime.jsx)(SlotErrorBoundary, {
746
+ slotKey,
747
+ onEntryError,
748
+ children: (0, react_jsx_runtime.jsx)(SessionEntry, {
749
+ entry,
750
+ ownerProps,
751
+ binding,
752
+ slotKey,
753
+ slotInjected,
754
+ hookContext,
755
+ hasHookContext
756
+ })
757
+ }, binding.key);
758
+ }
759
+ /**
760
+ * Anchor style shared by every outlet wrapper: `display:contents` keeps the
761
+ * wrapper out of layout (grid/flex parents see the slot's own children), so
762
+ * the anchor is purely addressable surface. Module-level constant — a stable
763
+ * reference so the wrapper never diffs its style prop.
764
+ */
765
+ const ANCHOR_STYLE = { display: "contents" };
766
+ function SlotOutlet({ slotKey, ownerProps, opts }) {
767
+ const host = useHost();
768
+ (0, react.useSyncExternalStore)((fn) => host.subscribe(slotKey, fn), () => host.getVersion(slotKey));
769
+ useLocaleRevision(host.locale);
770
+ return (0, react_jsx_runtime.jsx)("div", {
771
+ "data-slot": slotKey,
772
+ style: ANCHOR_STYLE,
773
+ children: renderOutletContent(host, slotKey, ownerProps, opts, useScopeBinding())
774
+ });
775
+ }
776
+ /** Kind dispatch behind the outlet anchor (single/keyed/list/chain, fallbacks, crash faces). */
777
+ function renderOutletContent(host, slotKey, ownerProps, opts, scopeBinding) {
778
+ const spec = host.specOf(slotKey);
779
+ if (!spec) return null;
780
+ if (spec.kind === "chain" && opts?.fallbackOnly === true) return renderChainResult(slotKey, null, opts);
781
+ if (spec.scope === "session" && scopeBinding.key === void 0) throw new SlotAssemblyError(`strict session slot '${slotKey}' rendered without a scope binding`);
782
+ const entries = host.entriesOf(slotKey);
783
+ const slotInjected = cachedSlotInject(spec.inject);
784
+ const guarded = (entry, key, owner = ownerProps) => {
785
+ const hasHookContext = opts !== void 0 && Object.hasOwn(opts, "hookContext");
786
+ const hookContext = opts?.hookContext;
787
+ const onEntryError = (error) => {
788
+ host.reportEntryError(slotKey, entry, error, { abdicate: spec.kind !== "chain" });
789
+ };
790
+ return spec.scope === "session" ? (0, react_jsx_runtime.jsx)(StrictSessionEntry, {
791
+ slotKey,
792
+ entry,
793
+ ownerProps: owner,
794
+ slotInjected,
795
+ hookContext,
796
+ hasHookContext,
797
+ onEntryError
798
+ }, key) : (0, react_jsx_runtime.jsx)(SlotErrorBoundary, {
799
+ slotKey,
800
+ onEntryError,
801
+ children: spec.scope === "session-maybe" ? (0, react_jsx_runtime.jsx)(SessionMaybeEntry, {
802
+ entry,
803
+ ownerProps: owner,
804
+ slotKey,
805
+ slotInjected,
806
+ hookContext,
807
+ hasHookContext
808
+ }) : (0, react_jsx_runtime.jsx)(RootEntry, {
809
+ entry,
810
+ ownerProps: owner,
811
+ slotKey,
812
+ slotInjected,
813
+ hookContext,
814
+ hasHookContext
815
+ })
816
+ }, key);
817
+ };
818
+ const deadCell = () => (0, react_jsx_runtime.jsx)("div", { "data-slot-error": slotKey });
819
+ if (spec.kind === "single") {
820
+ const entry = host.entriesOfSlot(slotKey)[0];
821
+ if (!entry) return entries.length > 0 ? deadCell() : (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: opts?.fallback ?? null });
822
+ return guarded(entry, entryKeyOf(entry));
823
+ }
824
+ if (spec.kind === "keyed") {
825
+ const entry = host.entriesOfSlot(slotKey).find((e) => e.options.key === opts?.entryKey);
826
+ if (!entry) return entries.some((e) => e.options.key === opts?.entryKey) ? deadCell() : (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: opts?.fallback ?? null });
827
+ return guarded(entry, entryKeyOf(entry));
828
+ }
829
+ if (spec.kind === "chain") {
830
+ let elected = null;
831
+ for (const entry of entries) {
832
+ let matched;
833
+ try {
834
+ matched = entry.select(ownerProps);
835
+ } catch (error) {
836
+ console.error(`chain selector crashed in '${slotKey}' (${entry.registrant ?? "unknown registrant"}), treating as declined:`, error);
837
+ continue;
838
+ }
839
+ if (matched !== null) {
840
+ elected = guarded(entry, entryKeyOf(entry), {
841
+ ...ownerProps,
842
+ matched
843
+ });
844
+ break;
845
+ }
846
+ }
847
+ return renderChainResult(slotKey, elected, opts);
848
+ }
849
+ const rows = host.entriesOfSlot(slotKey).map((entry) => ({
850
+ entry,
851
+ id: entry.options.id,
852
+ order: entry.options.order ?? 0
853
+ }));
854
+ const rowIds = new Set(rows.map((row) => row.id));
855
+ for (const entry of entries) {
856
+ if (rowIds.has(entry.options.id)) continue;
857
+ rowIds.add(entry.options.id);
858
+ rows.push({
859
+ entry: void 0,
860
+ id: entry.options.id,
861
+ order: entry.options.order ?? 0
862
+ });
863
+ }
864
+ let list = [...rows].sort((a, b) => a.order - b.order);
865
+ if (opts?.only !== void 0) list = list.filter((item) => item.id === opts.only);
866
+ if (list.length === 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: opts?.fallback ?? null });
867
+ return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: list.map((item, i) => item.entry !== void 0 ? guarded(item.entry, `e${entryKeyOf(item.entry)}`) : (0, react_jsx_runtime.jsx)("div", { "data-slot-error": slotKey }, `x${item.id ?? i}`)) });
868
+ }
869
+ /** Render a chain election while preserving the overlay fallback's tree position. */
870
+ function renderChainResult(slotKey, elected, opts) {
871
+ if (!opts?.overlay) return elected ?? (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: opts?.fallback ?? null });
872
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
873
+ "data-chain-overlay-fallback": slotKey,
874
+ style: { display: elected === null ? "contents" : "none" },
875
+ children: opts.fallback ?? null
876
+ }), elected] });
877
+ }
878
+ /** Root outlet: the shell's single ctx-level render entry — an unregistered 'root' is a boot-order failure, never a silent blank. */
879
+ function RootOutlet({ ownerProps }) {
880
+ const host = useHost();
881
+ (0, react.useSyncExternalStore)((fn) => host.subscribe("root", fn), () => host.getVersion("root"));
882
+ useLocaleRevision(host.locale);
883
+ const entry = host.entriesOfSlot("root")[0];
884
+ if (!entry) {
885
+ if (host.entriesOf("root").length > 0) return (0, react_jsx_runtime.jsx)("div", { "data-slot-error": "root" });
886
+ throw new SlotAssemblyError("renderSlot('root') before any 'root' registration (boot order)");
887
+ }
888
+ return (0, react_jsx_runtime.jsx)("div", {
889
+ "data-slot": "root",
890
+ style: ANCHOR_STYLE,
891
+ children: (0, react_jsx_runtime.jsx)(SlotErrorBoundary, {
892
+ slotKey: "root",
893
+ onEntryError: (error) => {
894
+ host.reportEntryError("root", entry, error, { abdicate: true });
895
+ },
896
+ children: (0, react_jsx_runtime.jsx)(RootEntry, {
897
+ entry,
898
+ ownerProps,
899
+ slotKey: "root",
900
+ slotInjected: EMPTY_SLOT_INJECT,
901
+ hookContext: void 0,
902
+ hasHookContext: false
903
+ })
904
+ }, entryKeyOf(entry))
905
+ });
906
+ }
907
+ /**
908
+ * Build the renderer installed into the `ui-renderer` SlotRegistry
909
+ * (ctx.slots.install(createSlotRenderer()) at boot; the service owns the
910
+ * install/renderSlot contract and the double-install/not-installed throws).
911
+ * @returns the renderer.
912
+ */
913
+ function createSlotRenderer() {
914
+ return { renderRoot(host, ownerProps) {
915
+ return (0, react_jsx_runtime.jsx)(HostContext.Provider, {
916
+ value: host,
917
+ children: (0, react_jsx_runtime.jsx)(RootStandardProvider, { children: (0, react_jsx_runtime.jsx)(ScopeProvider, {
918
+ scope: "session-maybe",
919
+ children: (0, react_jsx_runtime.jsx)(RootOutlet, { ownerProps })
920
+ }) })
921
+ });
922
+ } };
923
+ }
924
+ //#endregion
925
+ //#region lib/types/client/app.js
926
+ /**
927
+ * Build the assembled application factory.
928
+ * @param deps - Active UI-renderer dependencies.
929
+ * @returns Factory producing the application React tree.
930
+ */
931
+ function buildRenderApp(deps) {
932
+ const { ctx } = deps;
933
+ return () => ctx.slots.renderSlot("root", {});
934
+ }
935
+ //#endregion
936
+ //#region lib/types/client/registry.js
937
+ /**
938
+ * SlotRegistry: the renderer-owned Cordis service over the pure
939
+ * SlotCore (ui-slots owns registration semantics, the declaration ledger,
940
+ * the load-time validations, and the unload cascade). This layer owns what
941
+ * needs a live application: the 'slots/changed' event bridge, register and
942
+ * declaration injection through the caller's ctx.effect (fiber unload
943
+ * collects both), the renderer installation contract (install()/renderSlot('root') +
944
+ * the SlotRendererHost face), and the store INSTANCE axis — handle x scope
945
+ * key -> create/cache, dropped with the last holding entry, session instances
946
+ * cleared (with persisted state) on scope death.
947
+ */
948
+ /** Instance key for root-scoped store records (session records key by session id, so the literal cannot collide). */
949
+ const ROOT_INSTANCE_KEY = "root";
950
+ /** cordis Service layer of the slot system; see the module doc for the split with SlotCore. */
951
+ var SlotRegistry = class extends _prettier_ai_cordis.Service {
952
+ _core = new _prettier_ai_dsh_client_ui_slots.SlotCore();
953
+ /** Store-instance axis: handle -> mounted scope, refcount, resolved instances. */
954
+ _stores = /* @__PURE__ */ new Map();
955
+ /** Latest live Context generation for each scoped store key. */
956
+ _storeScopeOwners = /* @__PURE__ */ new Map();
957
+ _renderer;
958
+ _locale;
959
+ _host;
960
+ _rootContributions = [];
961
+ _rootListeners = /* @__PURE__ */ new Set();
962
+ _rootBinding = {
963
+ key: void 0,
964
+ hooks: {},
965
+ keyedHooks: {},
966
+ props: {}
967
+ };
968
+ _rootSource = {
969
+ getSnapshot: () => this._rootBinding,
970
+ subscribe: (listener) => {
971
+ this._rootListeners.add(listener);
972
+ return () => {
973
+ this._rootListeners.delete(listener);
974
+ };
975
+ }
976
+ };
977
+ _scopes = /* @__PURE__ */ new Map();
978
+ _scopeRevision = 0;
979
+ _scopeListeners = /* @__PURE__ */ new Set();
980
+ _scopeRevisionSource = {
981
+ getSnapshot: () => this._scopeRevision,
982
+ subscribe: (listener) => {
983
+ this._scopeListeners.add(listener);
984
+ return () => {
985
+ this._scopeListeners.delete(listener);
986
+ };
987
+ }
988
+ };
989
+ /**
990
+ * @param ctx - owning root context.
991
+ */
992
+ constructor(ctx) {
993
+ super(ctx, "slots");
994
+ this._core.onMutate((key) => {
995
+ ctx.emit("slots/changed", key);
996
+ });
997
+ }
998
+ /**
999
+ * Install an effect for each declaration lifetime of a slot. The callback
1000
+ * runs synchronously when the declaration already exists; otherwise it runs
1001
+ * inside the declaring `register()` call after the declaration is committed.
1002
+ * Collapse disposes the effect and a later declaration runs it again.
1003
+ * Callback effects are synchronous disposers; iterable effects install
1004
+ * transactionally and dispose in reverse order. The controller belongs to
1005
+ * the caller's fiber, so plugin unload cancels a pending wait and removes any
1006
+ * active contribution.
1007
+ *
1008
+ * @param key - declared SlotMap key to depend on.
1009
+ * @param callback - creates one disposer or an iterable of disposers.
1010
+ * @returns idempotent disposer for the wait and active effect.
1011
+ * @throws callback setup failures synchronously when the slot is already declared.
1012
+ */
1013
+ inject(key, callback) {
1014
+ const ctx = this.ctx;
1015
+ const disposeController = ctx.effect(() => {
1016
+ let active;
1017
+ let activeEpoch;
1018
+ let stopped = false;
1019
+ let unsubscribe = () => {};
1020
+ const stop = () => {
1021
+ if (stopped) return;
1022
+ stopped = true;
1023
+ unsubscribe();
1024
+ const dispose = active;
1025
+ active = void 0;
1026
+ activeEpoch = void 0;
1027
+ dispose?.();
1028
+ };
1029
+ const reconcile = () => {
1030
+ if (stopped) return;
1031
+ const spec = this._core.specDynamic(key);
1032
+ const epoch = this._core.declarationEpoch(key);
1033
+ if (active !== void 0 && activeEpoch === epoch) return;
1034
+ const dispose = active;
1035
+ active = void 0;
1036
+ activeEpoch = void 0;
1037
+ dispose?.();
1038
+ if (spec === void 0) return;
1039
+ const disposeEffect = ctx.effect(callback, `slots.inject(${JSON.stringify(key)}): declaration`);
1040
+ active = () => {
1041
+ disposeEffect();
1042
+ };
1043
+ activeEpoch = epoch;
1044
+ };
1045
+ const changed = () => {
1046
+ try {
1047
+ reconcile();
1048
+ } catch (error) {
1049
+ if (error?.code === "INACTIVE_EFFECT") {
1050
+ stop();
1051
+ return;
1052
+ }
1053
+ stop();
1054
+ const failure = error instanceof Error ? error : new Error(String(error));
1055
+ queueMicrotask(() => {
1056
+ throw failure;
1057
+ });
1058
+ }
1059
+ };
1060
+ unsubscribe = this._core.subscribeDeclaration(key, changed);
1061
+ try {
1062
+ reconcile();
1063
+ } catch (error) {
1064
+ stop();
1065
+ throw error;
1066
+ }
1067
+ return stop;
1068
+ }, `slots.inject(${JSON.stringify(key)})`);
1069
+ return () => {
1070
+ disposeController();
1071
+ };
1072
+ }
1073
+ /**
1074
+ * Install the shell's renderer (ui-renderer's createSlotRenderer product).
1075
+ * Boot-once: a second install throws. Runs through the caller's ctx.effect,
1076
+ * so shell fiber unload uninstalls the renderer.
1077
+ * @param renderer - the outlet machinery implementing SlotRenderer.
1078
+ */
1079
+ install(renderer) {
1080
+ if (this._renderer !== void 0) throw new Error("slot renderer already installed (install() is boot-once)");
1081
+ this.ctx.effect(() => {
1082
+ this._renderer = renderer;
1083
+ return () => {
1084
+ if (this._renderer === renderer) this._renderer = void 0;
1085
+ };
1086
+ }, "slots.install()");
1087
+ }
1088
+ /**
1089
+ * Install the locale face backing the `t` standard seat (the locale
1090
+ * plugin's product; same boot-once discipline as the renderer install).
1091
+ * Runs through the caller's ctx.effect, so the installing fiber's unload
1092
+ * uninstalls the face.
1093
+ * @param face - namespace binder + revision observable.
1094
+ */
1095
+ installLocale(face) {
1096
+ if (this._locale !== void 0) throw new Error("locale face already installed (installLocale() is boot-once)");
1097
+ this.ctx.effect(() => {
1098
+ this._locale = face;
1099
+ return () => {
1100
+ if (this._locale === face) this._locale = void 0;
1101
+ };
1102
+ }, "slots.installLocale()");
1103
+ }
1104
+ /**
1105
+ * Contribute domain-owned root data. Hook names must be globally unique;
1106
+ * registration and disposal republish one atomic root binding.
1107
+ * @param contribution - bare sources and stable props.
1108
+ * @returns disposer owned by the caller's Cordis fiber.
1109
+ */
1110
+ provideRoot(contribution) {
1111
+ const dispose = this.ctx.effect(() => {
1112
+ this._rootContributions.push(contribution);
1113
+ try {
1114
+ this.rebuildRootBinding();
1115
+ } catch (error) {
1116
+ this._rootContributions.pop();
1117
+ throw error;
1118
+ }
1119
+ return () => {
1120
+ const index = this._rootContributions.indexOf(contribution);
1121
+ if (index === -1) return;
1122
+ this._rootContributions.splice(index, 1);
1123
+ this.rebuildRootBinding();
1124
+ };
1125
+ }, "slots.provideRoot()");
1126
+ return () => {
1127
+ dispose();
1128
+ };
1129
+ }
1130
+ /**
1131
+ * Install the owner adapter for one strict scope. Its optional counterpart
1132
+ * resolves through the same adapter.
1133
+ * @param scope - strict scope name.
1134
+ * @param adapter - current/resolved binding source and release notifications.
1135
+ */
1136
+ installScope(scope, adapter) {
1137
+ if (this._scopes.has(scope)) throw new Error(`slot scope '${scope}' already has an adapter`);
1138
+ this.ctx.effect(() => {
1139
+ this._scopes.set(scope, adapter);
1140
+ this.publishScopeRevision();
1141
+ return () => {
1142
+ if (this._scopes.get(scope) === adapter) {
1143
+ this._scopes.delete(scope);
1144
+ this.publishScopeRevision();
1145
+ }
1146
+ };
1147
+ }, `slots.installScope(${JSON.stringify(scope)})`);
1148
+ }
1149
+ /**
1150
+ * Bind all scoped Store handles to one owner Context lifetime. The cleanup
1151
+ * materializes an otherwise-unused handle before clearing it, because a
1152
+ * previous application run may have persisted state for a Slot that this
1153
+ * scope never rendered. Rebinding the same key transfers cleanup ownership
1154
+ * to the newest Context generation.
1155
+ *
1156
+ * @param binding - materialized scope identity and its owning Context.
1157
+ */
1158
+ bindStoreScope(binding) {
1159
+ if (this._storeScopeOwners.get(binding.key) === binding.ctx) return;
1160
+ this._storeScopeOwners.set(binding.key, binding.ctx);
1161
+ binding.ctx.effect(() => () => {
1162
+ if (this._storeScopeOwners.get(binding.key) !== binding.ctx) return;
1163
+ this._storeScopeOwners.delete(binding.key);
1164
+ this.clearStoreScope(binding.key);
1165
+ }, `slots: store scope ${binding.key}`);
1166
+ }
1167
+ /**
1168
+ * The single ctx-level render entry: the shell renders 'root'; every other
1169
+ * key renders inside components through the props renderSlot face. All
1170
+ * three guards are fail-loud boot-order checks, no fallback.
1171
+ * @param key - must be 'root' (runtime-enforced for dynamically composed callers).
1172
+ * @param owner - owner share for the root entry (the shell supplies {}).
1173
+ * @returns the rendered root tree.
1174
+ */
1175
+ renderSlot(key, owner) {
1176
+ if (key !== "root") throw new Error(`ctx-level renderSlot only renders 'root' (got "${key}"); child slots render through the component props face`);
1177
+ if (this._renderer === void 0) throw new Error("slot renderer not installed — boot must call ctx.slots.install(createSlotRenderer()) before rendering 'root'");
1178
+ if (this._core.entries("root").length === 0) throw new Error("'root' has no registration — a layout entry must register into 'root' before the shell renders it");
1179
+ return this._renderer.renderRoot(this.hostFace(), owner);
1180
+ }
1181
+ /**
1182
+ * Snapshot entries for a key (render-erased view; stable reference between mutations).
1183
+ * @param key - SlotMap key.
1184
+ * @returns registered entries.
1185
+ */
1186
+ entries(key) {
1187
+ return this._core.entries(key);
1188
+ }
1189
+ /**
1190
+ * Shadowing winners per cell for a key: the first live (non-abdicated)
1191
+ * entry of each cell in priority order — what outlets render; chain keys
1192
+ * pass through unchanged (election consumes every entry). The raw
1193
+ * {@link SlotsService.entries} view stays the inspection surface. Fresh
1194
+ * array per call, not a uSES getSnapshot source.
1195
+ * @param key - SlotMap key.
1196
+ * @returns the winning entry per occupied cell.
1197
+ */
1198
+ entriesOfSlot(key) {
1199
+ return this._core.entriesOfSlot(key);
1200
+ }
1201
+ /**
1202
+ * Export the current JSON-safe Slot declaration tree for read-only inspection.
1203
+ * @param root - exact live Slot root; omitted returns all roots.
1204
+ * @returns selected Slot trees.
1205
+ */
1206
+ snapshot(root) {
1207
+ return this._core.snapshot(root);
1208
+ }
1209
+ /**
1210
+ * Observe entry boundary crashes (every render-time entry failure the
1211
+ * boundaries contain, abdicating or not) — the supervision seam for
1212
+ * plugins mirroring contribution health. Fires synchronously per report,
1213
+ * after the registry mutated for abdicating crashes. Callers own the
1214
+ * disposer (wire it through ctx.effect for fiber-lifetime cleanup, as with
1215
+ * {@link SlotsService.subscribe}).
1216
+ * @param fn - called with the slot key, the crashed entry, the crash
1217
+ * cause, and `abdicated`: whether the crash retired the entry from its cell.
1218
+ * @returns unsubscribe.
1219
+ */
1220
+ onEntryError(fn) {
1221
+ return this._core.onEntryError(fn);
1222
+ }
1223
+ /**
1224
+ * Look up a declared spec (register-declared or the built-in 'root').
1225
+ * @param key - SlotMap key.
1226
+ * @returns spec or undefined.
1227
+ */
1228
+ spec(key) {
1229
+ return this._core.spec(key);
1230
+ }
1231
+ /**
1232
+ * Subscribe to a key's registration changes (microtask-batched).
1233
+ * @param key - SlotMap key.
1234
+ * @param fn - change callback.
1235
+ * @returns unsubscribe.
1236
+ */
1237
+ subscribe(key, fn) {
1238
+ return this._core.subscribe(key, fn);
1239
+ }
1240
+ /**
1241
+ * Version counter for uSES pairing.
1242
+ * @param key - SlotMap key.
1243
+ * @returns current version.
1244
+ */
1245
+ getVersion(key) {
1246
+ return this._core.getVersion(key);
1247
+ }
1248
+ /** Delegating registration path: factory minting + registrant stamp + core write + instance-axis bookkeeping. */
1249
+ _register(options, component) {
1250
+ const store = typeof options.store === "function" ? options.store() : options.store;
1251
+ const registrant = options.registrant ?? this.ctx.fiber?.name;
1252
+ const erased = {
1253
+ ...options,
1254
+ ...store !== void 0 ? { store } : {},
1255
+ ...registrant !== void 0 ? { registrant } : {}
1256
+ };
1257
+ const dispose = this._core.register(erased, component);
1258
+ if (store !== void 0) {
1259
+ const scope = this._core.specDynamic(options.name).scope;
1260
+ this._acquire(store, scope);
1261
+ }
1262
+ let disposed = false;
1263
+ return () => {
1264
+ if (disposed) return;
1265
+ disposed = true;
1266
+ dispose();
1267
+ if (store !== void 0) this._release(store);
1268
+ };
1269
+ }
1270
+ /** Build the domain-neutral host face once; installed adapters remain live through getters. */
1271
+ hostFace() {
1272
+ if (this._host !== void 0) return this._host;
1273
+ const service = this;
1274
+ this._host = {
1275
+ subscribe: (key, fn) => this._core.subscribe(key, fn),
1276
+ getVersion: (key) => this._core.getVersion(key),
1277
+ entriesOf: (key) => this._core.entries(key),
1278
+ entriesOfSlot: (key) => this._core.entriesOfSlot(key),
1279
+ reportEntryError: (key, entry, error, info) => {
1280
+ this._core.reportEntryError(key, entry, error, info);
1281
+ },
1282
+ specOf: (key) => this._core.specDynamic(key),
1283
+ isLive: (entry) => this._core.isLive(entry),
1284
+ storeOf: (entry, scopeBinding) => entry.store === void 0 ? void 0 : this.resolveStore(entry.store, scopeBinding),
1285
+ root: this._rootSource,
1286
+ scopeRevision: this._scopeRevisionSource,
1287
+ scope: (scope) => service._scopes.get(scope === "session-maybe" ? "session" : scope),
1288
+ get locale() {
1289
+ return service._locale;
1290
+ }
1291
+ };
1292
+ return this._host;
1293
+ }
1294
+ /** Validate and atomically publish the current root contribution roster. */
1295
+ rebuildRootBinding() {
1296
+ const hooks = {};
1297
+ const keyedHooks = {};
1298
+ const props = {};
1299
+ const finalProps = /* @__PURE__ */ new Set();
1300
+ for (const contribution of this._rootContributions) {
1301
+ copyUnique("hook", hooks, contribution.hooks, finalProps, _prettier_ai_dsh_client_ui_slots.standardHookPropName);
1302
+ copyUnique("keyed hook", keyedHooks, contribution.keyedHooks, finalProps, _prettier_ai_dsh_client_ui_slots.standardHookPropName);
1303
+ copyUnique("prop", props, contribution.props, finalProps, (name) => name);
1304
+ }
1305
+ this._rootBinding = {
1306
+ key: void 0,
1307
+ hooks,
1308
+ keyedHooks,
1309
+ props
1310
+ };
1311
+ for (const listener of [...this._rootListeners]) try {
1312
+ listener();
1313
+ } catch (error) {
1314
+ console.error("root standard-source subscriber failed:", error);
1315
+ }
1316
+ }
1317
+ /** Publish one installed-scope roster transition after the map is authoritative. */
1318
+ publishScopeRevision() {
1319
+ this._scopeRevision += 1;
1320
+ for (const listener of [...this._scopeListeners]) try {
1321
+ listener();
1322
+ } catch (error) {
1323
+ console.error("scope-adapter subscriber failed:", error);
1324
+ }
1325
+ }
1326
+ /** Resolve (create or reuse) the store instance for a registered handle under a scope key. */
1327
+ resolveStore(handle, scopeBinding) {
1328
+ const record = this._stores.get(handle);
1329
+ if (record === void 0) throw new Error("store handle is not registered (entry unloaded, or the handle never went through register)");
1330
+ let key;
1331
+ if (record.scope === "root") key = ROOT_INSTANCE_KEY;
1332
+ else {
1333
+ if (scopeBinding === void 0) throw new Error(`${record.scope} store resolution requires a session id`);
1334
+ key = scopeBinding.key;
1335
+ this.bindStoreScope(scopeBinding);
1336
+ }
1337
+ let instance = record.instances.get(key);
1338
+ if (instance === void 0) {
1339
+ instance = record.scope === "root" ? handle.create() : handle.create(key);
1340
+ record.instances.set(key, instance);
1341
+ }
1342
+ return instance;
1343
+ }
1344
+ /** Clear every live non-root Store handle for one dead scope key. */
1345
+ clearStoreScope(key) {
1346
+ for (const [handle, record] of this._stores) {
1347
+ if (record.scope === "root") continue;
1348
+ (record.instances.get(key) ?? handle.create(key)).clearPersisted();
1349
+ record.instances.delete(key);
1350
+ }
1351
+ }
1352
+ /** Bind (or re-reference) a handle on the axis; cross-scope conflicts already threw in the core. */
1353
+ _acquire(handle, scope) {
1354
+ const record = this._stores.get(handle);
1355
+ if (record === void 0) {
1356
+ this._stores.set(handle, {
1357
+ scope,
1358
+ refs: 1,
1359
+ instances: /* @__PURE__ */ new Map()
1360
+ });
1361
+ return;
1362
+ }
1363
+ record.refs += 1;
1364
+ }
1365
+ /** Drop one reference; the last holder's unload drops the record (instances go with it — engine stores need no explicit dispose). */
1366
+ _release(handle) {
1367
+ const record = this._stores.get(handle);
1368
+ /* v8 ignore next -- defensive: release only runs from a disposer whose
1369
+ * register acquired the same handle, so the record must exist; kept so a
1370
+ * future call site cannot underflow the axis. */
1371
+ if (record === void 0) return;
1372
+ record.refs -= 1;
1373
+ if (record.refs !== 0) return;
1374
+ this._stores.delete(handle);
1375
+ }
1376
+ };
1377
+ function copyUnique(kind, target, values, finalProps, propNameOf) {
1378
+ if (values === void 0) return;
1379
+ for (const [name, value] of Object.entries(values)) {
1380
+ const propName = propNameOf(name);
1381
+ if (finalProps.has(propName)) throw new Error(`duplicate root standard ${kind} '${name}' at prop '${propName}'`);
1382
+ finalProps.add(propName);
1383
+ target[name] = value;
1384
+ }
1385
+ }
1386
+ SlotRegistry.prototype.register = function register(rawOptions, component) {
1387
+ const options = rawOptions;
1388
+ return this.ctx.effect(() => this["_register"](options, component), "slots.register()");
1389
+ };
1390
+ //#endregion
1391
+ //#region lib/types/client/index.js
1392
+ /**
1393
+ * Browser UI renderer. It installs the slot renderer after its Cordis
1394
+ * dependencies activate and exposes the mount operation used by the web boot
1395
+ * kernel after the complete client roster settles.
1396
+ */
1397
+ /** Services required before application assembly. */
1398
+ const inject = [];
1399
+ /** Hydrate the kernel-owned loading DOM before replacing it with the application. */
1400
+ function BootHandoff(props) {
1401
+ const [ready, setReady] = (0, react.useState)(false);
1402
+ (0, react.useLayoutEffect)(() => {
1403
+ setReady(true);
1404
+ }, []);
1405
+ if (ready) return props.app();
1406
+ return (0, react.createElement)("div", {
1407
+ className: props.boot.className,
1408
+ "data-dsh-boot": "",
1409
+ dangerouslySetInnerHTML: { __html: props.boot.html }
1410
+ });
1411
+ }
1412
+ /** Mount React while preserving the framework-free boot DOM through hydration. */
1413
+ function mountApp(container, app) {
1414
+ const boot = container.querySelector(":scope > [data-dsh-boot]");
1415
+ if (boot !== null) return (0, react_dom_client.hydrateRoot)(container, (0, react.createElement)(BootHandoff, {
1416
+ app,
1417
+ boot: {
1418
+ className: boot.className,
1419
+ html: boot.innerHTML
1420
+ }
1421
+ }));
1422
+ const root = (0, react_dom_client.createRoot)(container);
1423
+ (0, react_dom.flushSync)(() => {
1424
+ root.render(app());
1425
+ });
1426
+ return root;
1427
+ }
1428
+ /**
1429
+ * Install the slot renderer and provide the application mount face.
1430
+ * @param ctx - Plugin context.
1431
+ */
1432
+ function apply(ctx) {
1433
+ new SlotRegistry(ctx).install(createSlotRenderer());
1434
+ ctx.reflect.provide("uiRenderer", { mount: (container) => {
1435
+ const root = mountApp(container, buildRenderApp({ ctx }));
1436
+ return () => {
1437
+ root.unmount();
1438
+ };
1439
+ } });
1440
+ }
1441
+ //#endregion
1442
+ exports.SlotRegistry = SlotRegistry;
1443
+ exports.apply = apply;
1444
+ exports.inject = inject;
1445
+ return module.exports;
1446
+ }
1447
+ });
1448
+
1449
+ //# sourceMappingURL=client.js.map