@hydraharness/harness-client-ui-settings 0.1.1-rc.6

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 implementation exported from `./client`. */
3
+ /** Host plugin body — no host-side behavior for the settings domain base plugin. */
4
+ function apply() {}
5
+ //#endregion
6
+ export { apply };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hydraharness/harness-client-ui-settings`.
4
+ * @module @hydraharness/harness-client-ui-settings/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hydraharness/harness-client-ui-settings";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-settings-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: a presentation shell projecting the settings.section
13
+ * ledger into navigation — it emits no cordis events and owns no cross-plugin
14
+ * mutable relation; slot declaration/registration conflicts already fail loud
15
+ * in the slot core at load time.
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 };
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Settings slot contract — the canonical home of every settings slot type,
3
+ * owned by the settings domain base rather than by the shell that renders
4
+ * them (ui-settings-general, which occupies `sidebar.settings`). The shell has
5
+ * zero copy of its own: ALL text (trigger label, panel title, header actions,
6
+ * close aria, section content) arrives from registrants. A feature owns its
7
+ * own settings pages — adding a setting never means editing the shell; copy
8
+ * that belongs to no single feature (chrome, the General section) is owned by
9
+ * ui-settings-general too.
10
+ */
11
+ declare module '@hydraharness/harness-client-ui-slots' {
12
+ interface SlotMap {
13
+ /**
14
+ * The sidebar-foot trigger row content: icon + label, supplied as slot
15
+ * content (the accessible name comes from the content — rail state
16
+ * renders the label visually hidden). The shell renders the button
17
+ * chrome and owns open state. Absent contribution degrades to an
18
+ * icon-only button without an accessible name (broken-composition state;
19
+ * the shipped composition always registers the seat).
20
+ */
21
+ 'settings.trigger': {
22
+ kind: 'single';
23
+ scope: 'root';
24
+ owner: SettingsTriggerOwnerProps;
25
+ };
26
+ /**
27
+ * The panel title text seat. Content renders inside the nav heading row;
28
+ * the dialog's accessible name points at that node via aria-labelledby.
29
+ * Absent contribution leaves the heading empty.
30
+ */
31
+ 'settings.header': {
32
+ kind: 'single';
33
+ scope: 'root';
34
+ owner: SettingsHeaderOwnerProps;
35
+ };
36
+ /**
37
+ * Optional actions rendered in the content-column header before Close.
38
+ * Registrants own visibility, behavior, copy, and failure presentation;
39
+ * the shell supplies only the ordered render site.
40
+ */
41
+ 'settings.action': {
42
+ kind: 'list';
43
+ scope: 'root';
44
+ owner: SettingsHeaderOwnerProps;
45
+ };
46
+ /**
47
+ * The close button's visually-hidden label text (the button itself —
48
+ * icon, geometry, focus — is shell chrome). Absent contribution leaves
49
+ * the button without an accessible name (broken-composition state).
50
+ */
51
+ 'settings.close': {
52
+ kind: 'single';
53
+ scope: 'root';
54
+ owner: SettingsHeaderOwnerProps;
55
+ };
56
+ /**
57
+ * One settings page per list entry. Registrant options carry the nav
58
+ * identity: `id` (section key, drives `only` filtering), `order` (nav
59
+ * position), `label` (registrant-localized display text — the registrant
60
+ * re-registers with fresh text on locale change, so the shell never
61
+ * subscribes locale state; the ledger bump doubles as the shell's
62
+ * re-render trigger). Sections render inside the panel content column.
63
+ * (`settings.general.item`, declared by ui-settings-general's General
64
+ * entry, is typed in the locale package — the common dependency of every
65
+ * item registrant; the shell neither declares nor renders it.)
66
+ */
67
+ 'settings.section': {
68
+ kind: 'list';
69
+ scope: 'root';
70
+ owner: SettingsSectionOwnerProps;
71
+ };
72
+ /**
73
+ * Feature-owned rows inside the desktop Browser settings page. The page
74
+ * only stacks contributions; each item owns its copy, state, and writes.
75
+ * Declared at runtime only when the desktop browser is available.
76
+ */
77
+ 'settings.browser.item': {
78
+ kind: 'list';
79
+ scope: 'root';
80
+ owner: SettingsBrowserItemOwnerProps;
81
+ };
82
+ /**
83
+ * One page inside the Plugins settings section. The section owner renders
84
+ * localized entry labels as tabs and mounts each contribution inside its
85
+ * corresponding tab panel. Options: `id` (tab key), `order` (tab order),
86
+ * and `label` (registrant-localized tab text). Declared at runtime by the
87
+ * feature that owns the Plugins section; the type lives here so inventory
88
+ * and configuration plugins collaborate without depending on one another.
89
+ * The section also owns the one shared search box for the whole Plugins
90
+ * surface (Plugins, Skills, Hooks, Marketplaces, MCP); each tab receives
91
+ * its current text as `query` and filters its own rows against it.
92
+ */
93
+ 'settings.plugins.tab': {
94
+ kind: 'list';
95
+ scope: 'root';
96
+ owner: SettingsPluginsTabOwnerProps;
97
+ };
98
+ /**
99
+ * Feature-owned catalog sections inside the Plugins section's Hooks page.
100
+ * The Hooks tab (ui-settings-plugins) stacks its own user-record catalog
101
+ * and these contributions; each item owns its heading, rows, and writes.
102
+ * Declared at runtime by that tab; the type lives here so the inventory
103
+ * plugin contributes its imported-bundle hooks catalog without depending
104
+ * on the tab owner (same rationale as `settings.plugins.tab`). Each item
105
+ * receives the section's shared search text as `query` and filters its own
106
+ * rows against it.
107
+ */
108
+ 'settings.plugins.hooks.item': {
109
+ kind: 'list';
110
+ scope: 'root';
111
+ owner: SettingsPluginsTabOwnerProps;
112
+ };
113
+ /**
114
+ * Root-scoped onboarding steps contributed by settings features. The
115
+ * shell mounts one ordered step at a time; the active registrant either
116
+ * completes itself or keeps ownership until the user completes its sole
117
+ * path. Registrants own readiness, copy, dialog behavior, AND visible
118
+ * chrome: a step wraps its visible content in its modal surface (including
119
+ * `#root` inert ownership) and renders null while private facts are still
120
+ * loading. The shell paints no chrome of its own, so a mounted-but-deciding
121
+ * step shows and blocks nothing.
122
+ */
123
+ 'settings.onboarding': {
124
+ kind: 'list';
125
+ scope: 'root';
126
+ owner: SettingsOnboardingOwnerProps;
127
+ };
128
+ /**
129
+ * One preference row inside the General section — the additive seat for a
130
+ * single setting that needs no page of its own (a whole page is
131
+ * `settings.section`), contributed by the feature plugin that owns the
132
+ * preference (locale → Language, ui-theme → Appearance, ui-conversation →
133
+ * Composer Enter). Options: `id` (row key), `order` (row position). The
134
+ * section column only stacks rows, so a row draws its own internals,
135
+ * including its label: nothing projects a `label` here and the owner passes
136
+ * no props at all — copy, current value, and the write path are all yours,
137
+ * through your own inject face and `host.call`. Declared at runtime by
138
+ * ui-settings-general's General entry; the type lives here with every other
139
+ * settings slot type, because this package is the settings domain's base
140
+ * layer and every registrant already depends on it for `ctx.settingsScope`.
141
+ */
142
+ 'settings.general.item': {
143
+ kind: 'list';
144
+ scope: 'root';
145
+ owner: SettingsGeneralItemOwnerProps;
146
+ };
147
+ }
148
+ }
149
+ /** Owner share of a Browser settings item (the page supplies nothing). */
150
+ export interface SettingsBrowserItemOwnerProps {
151
+ /** Marker field: item owner props are intentionally empty. */
152
+ children?: never;
153
+ }
154
+ /** Owner share of a General preference row (the section supplies nothing). */
155
+ export interface SettingsGeneralItemOwnerProps {
156
+ /** Marker field: item owner props are intentionally empty. */
157
+ children?: never;
158
+ }
159
+ /** Owner share of a Plugins tab: selection and the section's shared search box. */
160
+ export interface SettingsPluginsTabOwnerProps {
161
+ /** Whether this tab is selected; retained tabs refresh Host lists when selected again. */
162
+ active: boolean;
163
+ /** Current text of the section-level search box; empty string matches everything. */
164
+ query: string;
165
+ }
166
+ /** Owner share of the trigger content seat: the sidebar column state. */
167
+ export interface SettingsTriggerOwnerProps {
168
+ /** Whether the sidebar renders wide content (false = 56px rail, icon only). */
169
+ wide: boolean;
170
+ }
171
+ /** Owner share of the header title seat (the shell supplies nothing). */
172
+ export interface SettingsHeaderOwnerProps {
173
+ /** Marker field: header owner props are intentionally empty. */
174
+ children?: never;
175
+ }
176
+ /**
177
+ * Owner share of a settings section entry. The shell owns modal visibility
178
+ * and navigation; a section's data arrives through its own inject faces and
179
+ * stores. `close` is the one shell affordance a section receives, for flows
180
+ * that leave settings altogether (starting a session from a section) — the
181
+ * onboarding coordinator's `openSection`/`complete` precedent, inverted.
182
+ */
183
+ export interface SettingsSectionOwnerProps {
184
+ /** Close the settings panel (the shell owns the open state). */
185
+ close: () => void;
186
+ }
187
+ /** Owner share of the currently active settings-backed onboarding step. */
188
+ export interface SettingsOnboardingOwnerProps {
189
+ /** Stable id of the step currently selected by the coordinator. */
190
+ stepId: string;
191
+ /** Complete or skip this step and transfer ownership to the next entry. */
192
+ complete: () => void;
193
+ /** Open the settings panel directly on one registered section. */
194
+ openSection: (id: string) => void;
195
+ }
196
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Settings domain base plugin, browser half. Provides `ctx.settingsScope`, the
3
+ * settings-namespace scope service every preference row binds its durable
4
+ * section through, and owns the one `settings.describe` reader in the browser:
5
+ * the describe mirror, whose invalidation subscriptions
6
+ * (`settings/document-updated`, `settings/registry-updated`, `connection/reset`) live here so every derived
7
+ * surface refreshes from a single wire read. It depends on no `ui-*`
8
+ * presentation package, so any feature that owns a preference can reach it:
9
+ * the settings SHELL — the `sidebar.settings` occupant, its navigation, and
10
+ * the chrome — lives in ui-settings-general, because a shell dependency on
11
+ * ui-sidebar would close a reference cycle through ui-layout and ui-theme.
12
+ * Export discipline: packages/client/AGENTS.md.
13
+ */
14
+ import type { ClientContext } from '@hydraharness/harness-client-runtime/client';
15
+ export type { SettingsGeneralItemOwnerProps, SettingsHeaderOwnerProps, SettingsOnboardingOwnerProps, SettingsPluginsTabOwnerProps, SettingsSectionOwnerProps, SettingsTriggerOwnerProps, } from './contract/slots.ts';
16
+ export type { SettingsScopeController, SettingsScopeBinder } from './settings-scope.ts';
17
+ export type { SettingsSchemaService } from './schema.ts';
18
+ export type { SchemaNode } from './schema.ts';
19
+ export type { SettingsDescribeFace, SettingsDescribeView, SettingsMirrorSnapshot } from './settings-mirror.ts';
20
+ /**
21
+ * Required services: the wire handle for the mirror's reads and the forwarded
22
+ * settings invalidation the mirror refreshes on.
23
+ */
24
+ export declare const inject: string[];
25
+ /**
26
+ * Provide the settings-namespace scope service over one shared describe
27
+ * mirror, and keep that mirror fresh when the settings document or namespace
28
+ * registry moves, and on a (re)connect.
29
+ *
30
+ * Constructing the service in this plugin's fiber keeps its traced methods
31
+ * bound to each consuming plugin's context.
32
+ * @param ctx - client root context.
33
+ */
34
+ export declare function apply(ctx: ClientContext): void;
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,72 @@
1
+ /** Synchronous schema introspection and immutable settings-draft edits. */
2
+ import { Service } from '@hydraharness/cordis';
3
+ import type { Context } from '@hydraharness/cordis';
4
+ import Schema from '@hydraharness/schemastery';
5
+ /** Live schemastery node used for settings introspection and validation. */
6
+ export type SchemaNode = Schema;
7
+ /**
8
+ * Settings-owned synchronous schema service. Dynamic client plugins receive
9
+ * this Cordis entity instead of importing executable helpers from one another.
10
+ */
11
+ export declare class SettingsSchemaService extends Service {
12
+ /** @param ctx - providing ui-settings context. */
13
+ constructor(ctx: Context);
14
+ /**
15
+ * Rehydrate one serialized `schema.toJSON()` envelope.
16
+ * @param serialized - serialized Schemastery node.
17
+ * @returns live schema node.
18
+ */
19
+ rehydrate(serialized: unknown): SchemaNode;
20
+ /**
21
+ * Validate a settings draft.
22
+ * @param schema - live schema node.
23
+ * @param draft - candidate settings value.
24
+ * @returns validation failure text, or `undefined` when valid.
25
+ */
26
+ validate(schema: SchemaNode, draft: unknown): string | undefined;
27
+ /**
28
+ * Resolve an object, dict, or array schema node at a settings path.
29
+ * @param root - schema node to traverse.
30
+ * @param path - object keys or array indexes.
31
+ * @returns the resolved node, or `undefined` when the path is absent.
32
+ */
33
+ nodeAtPath(root: SchemaNode, path: readonly string[]): SchemaNode | undefined;
34
+ /**
35
+ * Read a nested value by a string-key or array-index path.
36
+ * @param value - value to traverse.
37
+ * @param path - object keys or array indexes.
38
+ * @returns the resolved value, or `undefined` when the path is absent.
39
+ */
40
+ getPath(value: unknown, path: readonly string[]): unknown;
41
+ /**
42
+ * Report whether the final path key exists independently of its value.
43
+ * @param value - value to traverse.
44
+ * @param path - object keys or array indexes.
45
+ * @returns whether the path exists.
46
+ */
47
+ hasPath(value: unknown, path: readonly string[]): boolean;
48
+ /**
49
+ * Immutably set a nested value, materializing missing containers.
50
+ * @param root - settings object to copy.
51
+ * @param path - non-empty object-key or array-index path.
52
+ * @param value - replacement value.
53
+ * @returns copied root containing the replacement.
54
+ * @throws when `path` is empty.
55
+ */
56
+ setPath(root: Record<string, unknown>, path: readonly string[], value: unknown): Record<string, unknown>;
57
+ /**
58
+ * Immutably remove a nested key, preserving an unchanged missing root.
59
+ * @param root - settings object to copy.
60
+ * @param path - non-empty object-key or array-index path.
61
+ * @returns copied root without the key, or `root` when the path is absent.
62
+ * @throws when `path` is empty.
63
+ */
64
+ deletePath(root: Record<string, unknown>, path: readonly string[]): Record<string, unknown>;
65
+ }
66
+ declare module '@hydraharness/cordis' {
67
+ interface Context {
68
+ /** Settings-owned synchronous schema and immutable path operations. */
69
+ settingsSchema: SettingsSchemaService;
70
+ }
71
+ }
72
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Client mirror of the Host settings document: the one `settings.describe`
3
+ * reader in the browser. Every settings consumer derives from this store —
4
+ * per-namespace scopes through `SettingsScopeBinder.bind`, cross-namespace
5
+ * surfaces through the binder's shared describe face — so startup cost and
6
+ * freshness are properties of this class, not of how many features own a
7
+ * preference. The Host stays the fact source: the mirror re-reads on the
8
+ * invalidations its owning plugin subscribes to and folds write answers in
9
+ * through {@link SettingsDescribeMirror.acceptView}.
10
+ */
11
+ import type { IApiClient, SettingsNamespaceView } from '@hydraharness/harness-api-remotes/client';
12
+ type SettingsFace = Pick<IApiClient, 'settings'>;
13
+ /** The full `settings.describe` answer the mirror serves. */
14
+ export interface SettingsDescribeView {
15
+ /** Every namespace a live Host plugin registered, as the Host reported it. */
16
+ namespaces: readonly SettingsNamespaceView[];
17
+ /** Whether the settings provider accepts writes. */
18
+ writable: boolean;
19
+ /** Whether a native settings document exists for the Host to open. */
20
+ hasDocument: boolean;
21
+ }
22
+ /** Mirror state every derived settings surface renders from. */
23
+ export interface SettingsMirrorSnapshot {
24
+ /**
25
+ * `unavailable` is the terminal non-loopback state; `ready` persists across
26
+ * later failed refreshes (the held view keeps serving); `idle` means no
27
+ * answer is held and no read is running, so `ensure` will start one.
28
+ */
29
+ status: 'idle' | 'loading' | 'ready' | 'unavailable';
30
+ /** The last good answer; undefined until the first success. */
31
+ view: SettingsDescribeView | undefined;
32
+ /** The latest refresh failure message, cleared by the next success. */
33
+ error: string | null;
34
+ }
35
+ /**
36
+ * The mirror as cross-namespace surfaces consume it: current answer,
37
+ * subscription, first-use read, and the write-answer fold. `load` stays off
38
+ * this face — invalidation refreshes belong to the mirror's owning plugin.
39
+ */
40
+ export interface SettingsDescribeFace {
41
+ /** @returns the current sync snapshot (stable reference until the next change). */
42
+ getSnapshot(): SettingsMirrorSnapshot;
43
+ /**
44
+ * Observe snapshot replacements.
45
+ * @param listener - invoked after each snapshot change.
46
+ * @returns the disposer removing this listener.
47
+ */
48
+ subscribe(listener: () => void): () => void;
49
+ /**
50
+ * Resolve once an answer is held (or the mirror is terminally unavailable),
51
+ * reading only from `idle`.
52
+ * @returns settlement of the current or newly started read, if any.
53
+ */
54
+ ensure(): Promise<void>;
55
+ /**
56
+ * Fold a non-stale write answer's namespace view into the held view without a wire
57
+ * read, invalidating any older read still in flight.
58
+ * @param view - the namespace view a settings write answered with.
59
+ */
60
+ acceptView(view: SettingsNamespaceView): void;
61
+ }
62
+ /**
63
+ * Serializes every Host `settings.describe` read behind one snapshot store.
64
+ * Concurrent {@link load} calls fold into the in-flight read plus one rerun,
65
+ * so an invalidation arriving mid-read is never lost and never duplicated.
66
+ */
67
+ export declare class SettingsDescribeMirror implements SettingsDescribeFace {
68
+ private readonly api;
69
+ private readonly persistence;
70
+ private readonly store;
71
+ private inFlight;
72
+ private rerun;
73
+ private generation;
74
+ /**
75
+ * @param api - settings wire face.
76
+ * @param persistence - remote browsers stay process-local because settings RPCs are loopback-only.
77
+ */
78
+ constructor(api: SettingsFace, persistence?: 'host' | 'memory');
79
+ /** @returns the current sync snapshot (stable reference until the next change). */
80
+ getSnapshot(): SettingsMirrorSnapshot;
81
+ /**
82
+ * Observe snapshot replacements.
83
+ * @param listener - invoked after each snapshot change.
84
+ * @returns the disposer removing this listener.
85
+ */
86
+ subscribe(listener: () => void): () => void;
87
+ /**
88
+ * Refresh from the Host. A call during an in-flight read marks one rerun
89
+ * after it settles instead of racing a second wire read.
90
+ * @returns settlement after this call's freshness is reflected.
91
+ */
92
+ load(): Promise<void>;
93
+ /**
94
+ * Resolve once an answer is held (or the mirror is terminally unavailable),
95
+ * reading only from `idle`. The cheap idempotent entry for surfaces that
96
+ * render on first use.
97
+ * @returns settlement of the current or newly started read, if any.
98
+ */
99
+ ensure(): Promise<void>;
100
+ /**
101
+ * Fold a non-stale write answer's namespace view into the held view without a wire
102
+ * read, and invalidate any read still in flight. With no held document, the
103
+ * answer is not published as a partial document; an in-flight read reruns so
104
+ * it cannot publish a document fetched before the write committed.
105
+ * @param view - the namespace view a settings write answered with.
106
+ */
107
+ acceptView(view: SettingsNamespaceView): void;
108
+ /**
109
+ * Convenience row lookup on the held view.
110
+ * @param ns - namespace identity.
111
+ * @returns the namespace view, or undefined while unanswered or unregistered.
112
+ */
113
+ namespace(ns: string): SettingsNamespaceView | undefined;
114
+ private run;
115
+ private shouldRerun;
116
+ }
117
+ export {};
118
+ //# sourceMappingURL=settings-mirror.d.ts.map
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Host transport for the settings-namespace scope contract. The contract types
3
+ * live in `@hydraharness/harness-client-runtime` (the common dependency of every feature that
4
+ * owns a preference); this file owns the per-namespace derivation over the
5
+ * shared {@link SettingsDescribeMirror} and the serialized write path, both of
6
+ * which are Settings-surface concerns. Reads never touch the wire here: the
7
+ * mirror is the one `settings.describe` reader, and every scope is a selector
8
+ * over its snapshot.
9
+ */
10
+ import { Service } from '@hydraharness/cordis';
11
+ import type { Context } from '@hydraharness/cordis';
12
+ import type { IApiClient } from '@hydraharness/harness-api-remotes/client';
13
+ import { type SettingsScope, type SettingsScopeSnapshot, type SettingsScopeSpec } from '@hydraharness/harness-client-runtime/client';
14
+ import type { SettingsSchemaService } from './schema.ts';
15
+ import { SettingsDescribeMirror, type SettingsDescribeFace } from './settings-mirror.ts';
16
+ type SettingsFace = Pick<IApiClient, 'settings'>;
17
+ /**
18
+ * One namespace's derived view over the shared describe mirror, plus that
19
+ * namespace's serialized Host writes. Writes carry the latest known namespace
20
+ * revision, fold their answers back into the mirror, and teardown waits for
21
+ * the operation already crossing the wire.
22
+ */
23
+ export declare class SettingsScopeController<T> implements SettingsScope<T> {
24
+ private readonly api;
25
+ private readonly spec;
26
+ private readonly mirror;
27
+ private readonly persistence;
28
+ private readonly schema;
29
+ private readonly store;
30
+ private tail;
31
+ private writeGeneration;
32
+ private disposed;
33
+ private readonly unsubscribe;
34
+ /**
35
+ * Revision answered by a superseded write still ahead of the mirror: the
36
+ * mirror only folds the LATEST settlement in, so a queued successor takes
37
+ * its fence from here first.
38
+ */
39
+ private pendingRevision;
40
+ /**
41
+ * @param api - settings wire face (writes only; reads ride the mirror).
42
+ * @param spec - namespace identity and optional narrowing decoder.
43
+ * @param mirror - the shared describe mirror this scope derives from.
44
+ * @param persistence - remote browsers remain process-local because settings RPCs are loopback-only.
45
+ * @param schema - settings-owned schema operations.
46
+ */
47
+ constructor(api: SettingsFace, spec: SettingsScopeSpec<T>, mirror: SettingsDescribeMirror, persistence: 'host' | 'memory', schema: SettingsSchemaService);
48
+ /** @returns the current sync snapshot (stable reference until the next change). */
49
+ getSnapshot(): SettingsScopeSnapshot<T>;
50
+ /**
51
+ * Observe snapshot replacements.
52
+ * @param listener - invoked after each snapshot change.
53
+ * @returns the disposer removing this listener.
54
+ */
55
+ subscribe(listener: () => void): () => void;
56
+ /**
57
+ * Queue one field write; see {@link SettingsScope.set} for the ordering,
58
+ * revision, and recovery contract.
59
+ * @param field - scalar field inside the namespace section.
60
+ * @param value - JSON-shaped value selected by the user.
61
+ * @returns settlement after the write and any latest-write recovery read.
62
+ */
63
+ set(field: string, value: unknown): Promise<void>;
64
+ /**
65
+ * Queue one field clear; see {@link SettingsScope.unset} for the ordering,
66
+ * revision, and recovery contract.
67
+ * @param field - scalar field inside the namespace section.
68
+ * @returns settlement after the clear and any latest-write recovery read.
69
+ */
70
+ unset(field: string): Promise<void>;
71
+ private write;
72
+ /** Reload Host state for the latest failed write; superseded failures leave recovery to it. */
73
+ private recover;
74
+ /**
75
+ * Stop queued operations, stop deriving, and wait for the current wire call
76
+ * to settle.
77
+ * @returns settlement after the controller reaches quiescence.
78
+ */
79
+ dispose(): Promise<void>;
80
+ private enqueue;
81
+ private derive;
82
+ private decode;
83
+ }
84
+ declare module '@hydraharness/cordis' {
85
+ interface Context {
86
+ settingsScope: SettingsScopeBinder;
87
+ }
88
+ }
89
+ /**
90
+ * The settings domain's base service. Features that own a preference reach the
91
+ * settings transport through this service rather than a shared function: the
92
+ * client bundle purity gate forbids cross-plugin value imports and directs
93
+ * cross-plugin collaboration through cordis services
94
+ * (`packages/client/tsdown.client.ts`).
95
+ */
96
+ export declare class SettingsScopeBinder extends Service {
97
+ private readonly mirror;
98
+ private readonly schema;
99
+ /**
100
+ * @param ctx - the providing plugin's context.
101
+ * @param config - the shared describe mirror every bound scope derives from,
102
+ * plus the settings-owned schema operations.
103
+ */
104
+ constructor(ctx: Context, config: {
105
+ mirror: SettingsDescribeMirror;
106
+ schema: SettingsSchemaService;
107
+ });
108
+ /**
109
+ * The shared mirror's read/fold face for cross-namespace surfaces (schema
110
+ * introspection, the served-namespace directory). Per-namespace consumers
111
+ * use {@link bind}; both derive from the same snapshot, so they can never
112
+ * disagree about the document.
113
+ * @returns the describe face over the shared mirror.
114
+ */
115
+ describe(): SettingsDescribeFace;
116
+ /**
117
+ * Bind one namespace scope on the CALLER's plugin lifecycle — the service
118
+ * proxy binds `this.ctx` to the caller at call time, so the scope's disposer
119
+ * belongs to the calling fiber. The scope derives from the shared mirror
120
+ * (whose invalidation subscriptions live with the providing plugin), so
121
+ * binding adds no wire read of its own and activation never blocks on the
122
+ * settings transport.
123
+ * @param spec - domain-owned namespace contract.
124
+ * @returns the bound scope consumed by the domain's services and rows.
125
+ */
126
+ bind<T>(spec: SettingsScopeSpec<T>): SettingsScope<T>;
127
+ }
128
+ export {};
129
+ //# sourceMappingURL=settings-scope.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Host loader entry for the browser implementation exported from `./client`. */
2
+ /** Host plugin body — no host-side behavior for the settings domain base plugin. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hydraharness/harness-client-ui-settings`.
3
+ * @module @hydraharness/harness-client-ui-settings/invariant
4
+ */
5
+ import type { Context } from '@hydraharness/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-ui-settings-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