@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/index.js ADDED
@@ -0,0 +1,6 @@
1
+ //#region lib/types/index.js
2
+ /** Host loader entry for the browser-only UI renderer. */
3
+ /** Provides no host-side behavior. */
4
+ function apply() {}
5
+ //#endregion
6
+ export { apply };
@@ -0,0 +1,34 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@prettier-ai/dsh-client-ui-renderer`.
4
+ * @module @prettier-ai/dsh-client-ui-renderer/invariant
5
+ */
6
+ const PACKAGE_NAME = "@prettier-ai/dsh-client-ui-renderer";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-renderer-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * Verify that each `slots/changed` dispatch observes its mutation already
13
+ * applied to the renderer-owned slot registry.
14
+ */
15
+ const install = (ctx, fail) => {
16
+ ctx.on("internal/dispatch", (_mode, eventName, args) => {
17
+ if (eventName !== "slots/changed") return;
18
+ const key = args[0];
19
+ if (typeof key !== "string" || key === "") {
20
+ fail("'slots/changed' dispatched without a slot key argument");
21
+ return;
22
+ }
23
+ const slots = ctx.get("slots");
24
+ if (slots !== void 0 && slots.getVersion(key) === 0) fail(`'slots/changed' fired for "${key}" before any mutation bumped its version — emission must follow the applied mutation`);
25
+ }, { global: true });
26
+ };
27
+ /**
28
+ * Register this package's invariant companion.
29
+ * @param ctx - Cordis context carrying the invariant service.
30
+ * @returns the installed registration's disposer after setup succeeds.
31
+ */
32
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
33
+ //#endregion
34
+ export { apply, inject, name };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Real-UI assembly closure. The whole layout tree hangs from the built-in
3
+ * `root` slot, which is the only ctx-level slot render in the application.
4
+ */
5
+ import type { ReactNode } from 'react';
6
+ import type { Context } from '@prettier-ai/cordis';
7
+ /** Inputs available after the UI renderer's inject set activates. */
8
+ export interface AssemblyDeps {
9
+ /** Client context carrying the renderer-owned Slot registry. */
10
+ ctx: Context;
11
+ }
12
+ /**
13
+ * Build the assembled application factory.
14
+ * @param deps - Active UI-renderer dependencies.
15
+ * @returns Factory producing the application React tree.
16
+ */
17
+ export declare function buildRenderApp(deps: AssemblyDeps): () => ReactNode;
18
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1,11 @@
1
+ import type { HostObservable, SnapshotSelectorHook } from '@prettier-ai/dsh-client-ui-slots';
2
+ /**
3
+ * Bind a bare observable source to a typed uSES selector hook.
4
+ * subscribe/getSnapshot are captured once per source into stable closures
5
+ * (also re-binds `this` for method-based sources), so components never
6
+ * resubscribe across renders. Equality defaults to Object.is.
7
+ * @param w - snapshot source (engine store, Session object, store instance).
8
+ * @returns the selector hook.
9
+ */
10
+ export declare function bindSnapshotSelector<T>(w: HostObservable<T>): SnapshotSelectorHook<T>;
11
+ //# sourceMappingURL=bind.d.ts.map
@@ -0,0 +1,53 @@
1
+ /** Internal React bindings for renderer hosts and standard-source scopes. */
2
+ import { type ReactNode } from 'react';
3
+ import type { HostObservable, KeyedStandardSource, MaybeSnapshotSelectorHook, SlotRendererHost, SnapshotSelectorHook, StandardSourceBinding } from '@prettier-ai/dsh-client-ui-slots';
4
+ /** Missing renderer assembly dependency. */
5
+ export declare class SlotAssemblyError extends Error {
6
+ }
7
+ /** In-package renderer host context. */
8
+ export declare const HostContext: import("react").Context<SlotRendererHost | null>;
9
+ /**
10
+ * Read the installed renderer host.
11
+ * @returns the host API.
12
+ */
13
+ export declare function useHost(): SlotRendererHost;
14
+ /**
15
+ * Read the root standard-source binding.
16
+ * @returns the current root binding.
17
+ */
18
+ export declare function useRootBinding(): StandardSourceBinding;
19
+ /**
20
+ * Read the current-session-optional binding.
21
+ * @returns a binding whose key is absent when no Session is selected.
22
+ */
23
+ export declare function useScopeBinding(): StandardSourceBinding;
24
+ /**
25
+ * Bind one observable source to an identity-stable selector Hook.
26
+ * @param source - observable source.
27
+ * @returns cached selector Hook.
28
+ */
29
+ export declare function observableHook<T>(source: HostObservable<T>): SnapshotSelectorHook<T>;
30
+ /**
31
+ * Bind an optional source without changing Hook call order.
32
+ * @param source - current source, or absence.
33
+ * @returns selector Hook returning `undefined` while absent.
34
+ */
35
+ export declare function maybeObservableHook<T>(source: HostObservable<T> | undefined): MaybeSnapshotSelectorHook<T>;
36
+ /** Erased open-key selector Hook synthesized from one keyed source family. */
37
+ export type KeyedSnapshotHook = (key: string, selector?: (value: unknown) => unknown, equal?: (left: unknown, right: unknown) => boolean) => unknown;
38
+ /**
39
+ * Bind an open-key source family.
40
+ * @param source - keyed resolver, or absence for an optional scope.
41
+ * @returns cached keyed selector Hook.
42
+ */
43
+ export declare function keyedObservableHook(source: KeyedStandardSource | undefined): KeyedSnapshotHook;
44
+ /** Subscribe the tree to the atomically assembled root standard-source roster. */
45
+ export declare function RootStandardProvider({ children }: {
46
+ children: ReactNode;
47
+ }): import("react").JSX.Element;
48
+ /** Subscribe to the scope roster before resolving and binding its current adapter. */
49
+ export declare function ScopeProvider({ scope, children, }: {
50
+ scope: 'session' | 'session-maybe';
51
+ children: ReactNode;
52
+ }): import("react").JSX.Element;
53
+ //# sourceMappingURL=bindings.d.ts.map
@@ -0,0 +1,38 @@
1
+ import type { Context } from '@prettier-ai/cordis';
2
+ import { SlotRegistry } from './registry.ts';
3
+ export { SlotRegistry } from './registry.ts';
4
+ export type { RootOwnerProps } from './registry.ts';
5
+ export type { ChainRenderOpts, HostObservable, RenderOpts, SnapshotSelectorHook, SlotRenderer, ScopedStandardSourceBinding, SlotRendererHost, SlotScopeAdapter, StandardSourceBinding, StoreInstanceLike, } from '@prettier-ai/dsh-client-ui-slots';
6
+ /** Mount operation exposed to the framework-free boot kernel. */
7
+ export interface UiRendererService {
8
+ /**
9
+ * Mount the assembled application into the supplied element.
10
+ * @param container - Application mount point.
11
+ * @returns Disposer that unmounts the React root.
12
+ */
13
+ mount: (container: HTMLElement) => () => void;
14
+ }
15
+ declare module '@prettier-ai/cordis' {
16
+ interface Events {
17
+ /**
18
+ * A slot declaration or registration set changed.
19
+ * @mode emit
20
+ * @param key - mutated SlotMap key.
21
+ */
22
+ 'slots/changed'(key: string): void;
23
+ }
24
+ interface Context {
25
+ /** Renderer-owned UI composition registry. */
26
+ slots: SlotRegistry;
27
+ /** Mount face provided after the UI renderer activates. */
28
+ uiRenderer: UiRendererService;
29
+ }
30
+ }
31
+ /** Services required before application assembly. */
32
+ export declare const inject: string[];
33
+ /**
34
+ * Install the slot renderer and provide the application mount face.
35
+ * @param ctx - Plugin context.
36
+ */
37
+ export declare function apply(ctx: Context): void;
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,222 @@
1
+ /**
2
+ * SlotRegistry: the renderer-owned Cordis service over the pure
3
+ * SlotCore (ui-slots owns registration semantics, the declaration ledger,
4
+ * the load-time validations, and the unload cascade). This layer owns what
5
+ * needs a live application: the 'slots/changed' event bridge, register and
6
+ * declaration injection through the caller's ctx.effect (fiber unload
7
+ * collects both), the renderer installation contract (install()/renderSlot('root') +
8
+ * the SlotRendererHost face), and the store INSTANCE axis — handle x scope
9
+ * key -> create/cache, dropped with the last holding entry, session instances
10
+ * cleared (with persisted state) on scope death.
11
+ */
12
+ import { Service } from '@prettier-ai/cordis';
13
+ import type { Context } from '@prettier-ai/cordis';
14
+ import { SlotCore } from '@prettier-ai/dsh-client-ui-slots';
15
+ import type { LiveSlotNode, LocaleFace, OwnerOf, SlotMap, SlotRenderer, RootStandardSourceContribution, ScopedStandardSourceBinding, SlotScope, SlotScopeAdapter, SlotSpec, StoredEntry } from '@prettier-ai/dsh-client-ui-slots';
16
+ declare module '@prettier-ai/dsh-client-ui-slots' {
17
+ interface SlotMap {
18
+ /**
19
+ * The built-in render-tree root hole (seeded by SlotCore): the one slot the
20
+ * shell itself renders, and the ancestor of every other seat. OCCUPIED by
21
+ * ui-layout's AppFrame, which declares the sidebar, conversation, details,
22
+ * and shell.overlay seats inside it.
23
+ *
24
+ * DO NOT register here. This is a single slot, so a second entry does not
25
+ * sit beside the frame — it shadows it, and a dynamically registered entry
26
+ * is assigned a lower priority than the shipped one, which makes it the
27
+ * winner: the page would render your component alone, with every seat the
28
+ * frame declares gone. For a surface of your own that floats over the whole
29
+ * app, register into `shell.overlay` instead (a list slot: additive, and
30
+ * click-through until your entry opts into pointer events).
31
+ */
32
+ 'root': {
33
+ kind: 'single';
34
+ scope: 'root';
35
+ owner: RootOwnerProps;
36
+ };
37
+ }
38
+ }
39
+ /** Root owner share: the shell supplies nothing — the frame is inject-assembled. */
40
+ export interface RootOwnerProps {
41
+ children?: never;
42
+ }
43
+ /** One synchronous effect installed while an injected slot declaration is live. */
44
+ type SlotInjectionEffect = (() => void) | Iterable<() => void, void, void>;
45
+ /** cordis Service layer of the slot system; see the module doc for the split with SlotCore. */
46
+ export declare class SlotRegistry extends Service {
47
+ private readonly _core;
48
+ /** Store-instance axis: handle -> mounted scope, refcount, resolved instances. */
49
+ private readonly _stores;
50
+ /** Latest live Context generation for each scoped store key. */
51
+ private readonly _storeScopeOwners;
52
+ private _renderer;
53
+ private _locale;
54
+ private _host;
55
+ private readonly _rootContributions;
56
+ private readonly _rootListeners;
57
+ private _rootBinding;
58
+ private readonly _rootSource;
59
+ private readonly _scopes;
60
+ private _scopeRevision;
61
+ private readonly _scopeListeners;
62
+ private readonly _scopeRevisionSource;
63
+ /**
64
+ * @param ctx - owning root context.
65
+ */
66
+ constructor(ctx: Context);
67
+ /**
68
+ * The single registration API. The typed face IS the core's register
69
+ * (both overloads reused verbatim — one authority, no structural copy;
70
+ * see SlotCore.register for children declaration, store seat, inject
71
+ * face, load-time validation, and the unload cascade). This layer adds:
72
+ * disposal through the caller's ctx.effect (fiber unload = cascade),
73
+ * exclusive-factory minting (`store: createXxxStore` becomes a per-entry
74
+ * handle), the registrant diagnostics stamp, and store-instance lifecycle
75
+ * on the entry axis.
76
+ *
77
+ * Declared here, implemented by prototype assignment below the class: it
78
+ * MUST stay a prototype method (never an instance arrow) — the cordis
79
+ * service proxy binds `this.ctx` to the CALLER's context at call time,
80
+ * which is what routes the effect (and the unload cascade) into the
81
+ * caller's fiber. An arrow property would freeze `this` to the service's
82
+ * own root ctx and silently break per-plugin disposal.
83
+ */
84
+ readonly register: SlotCore['register'];
85
+ /**
86
+ * Install an effect for each declaration lifetime of a slot. The callback
87
+ * runs synchronously when the declaration already exists; otherwise it runs
88
+ * inside the declaring `register()` call after the declaration is committed.
89
+ * Collapse disposes the effect and a later declaration runs it again.
90
+ * Callback effects are synchronous disposers; iterable effects install
91
+ * transactionally and dispose in reverse order. The controller belongs to
92
+ * the caller's fiber, so plugin unload cancels a pending wait and removes any
93
+ * active contribution.
94
+ *
95
+ * @param key - declared SlotMap key to depend on.
96
+ * @param callback - creates one disposer or an iterable of disposers.
97
+ * @returns idempotent disposer for the wait and active effect.
98
+ * @throws callback setup failures synchronously when the slot is already declared.
99
+ */
100
+ inject(key: keyof SlotMap & string, callback: () => SlotInjectionEffect): () => void;
101
+ /**
102
+ * Install the shell's renderer (ui-renderer's createSlotRenderer product).
103
+ * Boot-once: a second install throws. Runs through the caller's ctx.effect,
104
+ * so shell fiber unload uninstalls the renderer.
105
+ * @param renderer - the outlet machinery implementing SlotRenderer.
106
+ */
107
+ install(renderer: SlotRenderer): void;
108
+ /**
109
+ * Install the locale face backing the `t` standard seat (the locale
110
+ * plugin's product; same boot-once discipline as the renderer install).
111
+ * Runs through the caller's ctx.effect, so the installing fiber's unload
112
+ * uninstalls the face.
113
+ * @param face - namespace binder + revision observable.
114
+ */
115
+ installLocale(face: LocaleFace): void;
116
+ /**
117
+ * Contribute domain-owned root data. Hook names must be globally unique;
118
+ * registration and disposal republish one atomic root binding.
119
+ * @param contribution - bare sources and stable props.
120
+ * @returns disposer owned by the caller's Cordis fiber.
121
+ */
122
+ provideRoot(contribution: RootStandardSourceContribution): () => void;
123
+ /**
124
+ * Install the owner adapter for one strict scope. Its optional counterpart
125
+ * resolves through the same adapter.
126
+ * @param scope - strict scope name.
127
+ * @param adapter - current/resolved binding source and release notifications.
128
+ */
129
+ installScope(scope: Exclude<SlotScope, 'root' | 'session-maybe'>, adapter: SlotScopeAdapter): void;
130
+ /**
131
+ * Bind all scoped Store handles to one owner Context lifetime. The cleanup
132
+ * materializes an otherwise-unused handle before clearing it, because a
133
+ * previous application run may have persisted state for a Slot that this
134
+ * scope never rendered. Rebinding the same key transfers cleanup ownership
135
+ * to the newest Context generation.
136
+ *
137
+ * @param binding - materialized scope identity and its owning Context.
138
+ */
139
+ bindStoreScope(binding: Pick<ScopedStandardSourceBinding, 'key' | 'ctx'>): void;
140
+ /**
141
+ * The single ctx-level render entry: the shell renders 'root'; every other
142
+ * key renders inside components through the props renderSlot face. All
143
+ * three guards are fail-loud boot-order checks, no fallback.
144
+ * @param key - must be 'root' (runtime-enforced for dynamically composed callers).
145
+ * @param owner - owner share for the root entry (the shell supplies {}).
146
+ * @returns the rendered root tree.
147
+ */
148
+ renderSlot<K extends keyof SlotMap & string>(key: K, owner: OwnerOf<K>): ReturnType<SlotRenderer['renderRoot']>;
149
+ /**
150
+ * Snapshot entries for a key (render-erased view; stable reference between mutations).
151
+ * @param key - SlotMap key.
152
+ * @returns registered entries.
153
+ */
154
+ entries(key: keyof SlotMap & string): readonly StoredEntry[];
155
+ /**
156
+ * Shadowing winners per cell for a key: the first live (non-abdicated)
157
+ * entry of each cell in priority order — what outlets render; chain keys
158
+ * pass through unchanged (election consumes every entry). The raw
159
+ * {@link SlotsService.entries} view stays the inspection surface. Fresh
160
+ * array per call, not a uSES getSnapshot source.
161
+ * @param key - SlotMap key.
162
+ * @returns the winning entry per occupied cell.
163
+ */
164
+ entriesOfSlot(key: keyof SlotMap & string): readonly StoredEntry[];
165
+ /**
166
+ * Export the current JSON-safe Slot declaration tree for read-only inspection.
167
+ * @param root - exact live Slot root; omitted returns all roots.
168
+ * @returns selected Slot trees.
169
+ */
170
+ snapshot(root?: string): LiveSlotNode[];
171
+ /**
172
+ * Observe entry boundary crashes (every render-time entry failure the
173
+ * boundaries contain, abdicating or not) — the supervision seam for
174
+ * plugins mirroring contribution health. Fires synchronously per report,
175
+ * after the registry mutated for abdicating crashes. Callers own the
176
+ * disposer (wire it through ctx.effect for fiber-lifetime cleanup, as with
177
+ * {@link SlotsService.subscribe}).
178
+ * @param fn - called with the slot key, the crashed entry, the crash
179
+ * cause, and `abdicated`: whether the crash retired the entry from its cell.
180
+ * @returns unsubscribe.
181
+ */
182
+ onEntryError(fn: (key: string, entry: StoredEntry, error: unknown, info: {
183
+ abdicated: boolean;
184
+ }) => void): () => void;
185
+ /**
186
+ * Look up a declared spec (register-declared or the built-in 'root').
187
+ * @param key - SlotMap key.
188
+ * @returns spec or undefined.
189
+ */
190
+ spec<K extends keyof SlotMap & string>(key: K): SlotSpec<SlotMap[K]> | undefined;
191
+ /**
192
+ * Subscribe to a key's registration changes (microtask-batched).
193
+ * @param key - SlotMap key.
194
+ * @param fn - change callback.
195
+ * @returns unsubscribe.
196
+ */
197
+ subscribe(key: keyof SlotMap & string, fn: () => void): () => void;
198
+ /**
199
+ * Version counter for uSES pairing.
200
+ * @param key - SlotMap key.
201
+ * @returns current version.
202
+ */
203
+ getVersion(key: keyof SlotMap & string): number;
204
+ /** Delegating registration path: factory minting + registrant stamp + core write + instance-axis bookkeeping. */
205
+ private _register;
206
+ /** Build the domain-neutral host face once; installed adapters remain live through getters. */
207
+ private hostFace;
208
+ /** Validate and atomically publish the current root contribution roster. */
209
+ private rebuildRootBinding;
210
+ /** Publish one installed-scope roster transition after the map is authoritative. */
211
+ private publishScopeRevision;
212
+ /** Resolve (create or reuse) the store instance for a registered handle under a scope key. */
213
+ private resolveStore;
214
+ /** Clear every live non-root Store handle for one dead scope key. */
215
+ private clearStoreScope;
216
+ /** Bind (or re-reference) a handle on the axis; cross-scope conflicts already threw in the core. */
217
+ private _acquire;
218
+ /** Drop one reference; the last holder's unload drops the record (instances go with it — engine stores need no explicit dispose). */
219
+ private _release;
220
+ }
221
+ export {};
222
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { type SlotRenderer } from '@prettier-ai/dsh-client-ui-slots';
2
+ /**
3
+ * Build the renderer installed into the `ui-renderer` SlotRegistry
4
+ * (ctx.slots.install(createSlotRenderer()) at boot; the service owns the
5
+ * install/renderSlot contract and the double-install/not-installed throws).
6
+ * @returns the renderer.
7
+ */
8
+ export declare function createSlotRenderer(): SlotRenderer;
9
+ //# sourceMappingURL=scoped-slots.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Host loader entry for the browser-only UI renderer. */
2
+ /** Provides no host-side behavior. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@prettier-ai/dsh-client-ui-renderer`.
3
+ * @module @prettier-ai/dsh-client-ui-renderer/invariant
4
+ */
5
+ import type { Context } from '@prettier-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-ui-renderer-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
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@prettier-ai/dsh-client-ui-renderer",
3
+ "description": "Browser UI renderer: React slot bindings, ctx.uiRenderer, and the assembled application root",
4
+ "version": "0.1.2-alpha.1",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/client/ui-renderer"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./src/*": "./src/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "dsh": {
33
+ "client": {
34
+ "platform": "web",
35
+ "immediately": true
36
+ }
37
+ },
38
+ "license": "MIT",
39
+ "dependencies": {
40
+ "use-sync-external-store": "1.2.0"
41
+ },
42
+ "peerDependencies": {
43
+ "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
44
+ "@prettier-ai/cordis": "^4.0.1"
45
+ },
46
+ "devDependencies": {
47
+ "@types/react": "~18.3.1",
48
+ "@types/react-dom": "~18.3.0",
49
+ "@types/use-sync-external-store": "^1.5.0",
50
+ "react": "^18.2.0",
51
+ "react-dom": "^18.2.0",
52
+ "@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.1",
53
+ "@prettier-ai/cordis": "^4.0.1",
54
+ "@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
55
+ "@prettier-ai/dsh-client-test-runtime": "^0.1.2-alpha.1"
56
+ },
57
+ "files": [
58
+ "lib/index.js",
59
+ "lib/invariant.js",
60
+ "lib/client.js",
61
+ "lib/types/**/*.d.ts"
62
+ ],
63
+ "scripts": {
64
+ "bundle": "tsdown",
65
+ "watch": "tsdown --watch"
66
+ }
67
+ }