dsh-input-traffic 0.7.0 → 0.7.3

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 CHANGED
@@ -1,5 +1,197 @@
1
+ //#region lib/types/bridge.js
2
+ /** Route pair for one settings ns — client and server must derive identically. */
3
+ function bridgeRoutesFor(ns) {
4
+ return {
5
+ describe: `/api/${ns}/settings/describe`,
6
+ mutate: `/api/${ns}/settings/mutate`
7
+ };
8
+ }
9
+ function readJsonBody(reqRaw, limit = 262144) {
10
+ const req = reqRaw;
11
+ return new Promise((resolve, reject) => {
12
+ const chunks = [];
13
+ let size = 0;
14
+ req.on("data", (chunk) => {
15
+ const buf = chunk;
16
+ size += buf.length;
17
+ if (size > limit) {
18
+ reject(/* @__PURE__ */ new Error("bridge body too large"));
19
+ req.resume?.();
20
+ return;
21
+ }
22
+ chunks.push(buf);
23
+ });
24
+ req.on("end", () => {
25
+ const raw = Buffer.concat(chunks).toString("utf8");
26
+ if (raw === "") {
27
+ resolve({});
28
+ return;
29
+ }
30
+ try {
31
+ resolve(JSON.parse(raw));
32
+ } catch (e) {
33
+ reject(e instanceof Error ? e : new Error(String(e)));
34
+ }
35
+ });
36
+ req.on("error", reject);
37
+ });
38
+ }
39
+ function sendJson(res, code, body) {
40
+ res.writeHead(code, {
41
+ "content-type": "application/json; charset=utf-8",
42
+ "cache-control": "no-cache"
43
+ });
44
+ res.end(JSON.stringify(body));
45
+ }
46
+ function isConflict(e) {
47
+ return e instanceof Error && /conflict|revision/i.test(e.message);
48
+ }
49
+ function registerSettingsBridgeRoutes(server, getSettingsSvc, ns) {
50
+ if (typeof server !== "object" || server === null) return [];
51
+ const ws = typeof server.register === "function" ? server : void 0;
52
+ if (ws === void 0) return [];
53
+ const offs = [];
54
+ const routes = bridgeRoutesFor(ns);
55
+ offs.push(ws.register({
56
+ kind: "exact",
57
+ path: routes.describe,
58
+ handler: (rawReq, rawRes) => {
59
+ const req = rawReq;
60
+ const res = rawRes;
61
+ if (req.method !== "POST") {
62
+ sendJson(res, 405, {
63
+ ok: false,
64
+ code: "method-not-allowed"
65
+ });
66
+ return;
67
+ }
68
+ const svc = getSettingsSvc();
69
+ if (svc === void 0 || typeof svc.describe !== "function") {
70
+ sendJson(res, 200, {
71
+ ok: false,
72
+ code: "settings-unavailable",
73
+ message: "settings service not resolved yet"
74
+ });
75
+ return;
76
+ }
77
+ try {
78
+ const descriptor = svc.describe({ redactSecrets: true }).find((d) => d.ns === ns);
79
+ if (descriptor === void 0) {
80
+ sendJson(res, 200, {
81
+ ok: false,
82
+ code: "ns-not-registered",
83
+ message: `settings namespace "${ns}" is not registered`
84
+ });
85
+ return;
86
+ }
87
+ sendJson(res, 200, {
88
+ ok: true,
89
+ value: {
90
+ descriptor,
91
+ writable: svc.writable !== false
92
+ }
93
+ });
94
+ } catch (e) {
95
+ sendJson(res, 200, {
96
+ ok: false,
97
+ code: "internal",
98
+ message: e instanceof Error ? e.message : String(e)
99
+ });
100
+ }
101
+ }
102
+ }));
103
+ offs.push(ws.register({
104
+ kind: "exact",
105
+ path: routes.mutate,
106
+ handler: (rawReq, rawRes) => {
107
+ const req = rawReq;
108
+ const res = rawRes;
109
+ if (req.method !== "POST") {
110
+ sendJson(res, 405, {
111
+ ok: false,
112
+ code: "method-not-allowed"
113
+ });
114
+ return;
115
+ }
116
+ const svc = getSettingsSvc();
117
+ const mutate = svc === void 0 ? void 0 : svc.mutate;
118
+ if (typeof mutate !== "function") {
119
+ sendJson(res, 200, {
120
+ ok: false,
121
+ code: "settings-unavailable",
122
+ message: "settings service mutate not available"
123
+ });
124
+ return;
125
+ }
126
+ readJsonBody(req).then((body) => {
127
+ const ops = body.ops;
128
+ if (!Array.isArray(ops) || ops.some((op) => !op || typeof op !== "object" || !(op.op in {
129
+ set: 1,
130
+ unset: 1
131
+ }))) {
132
+ sendJson(res, 200, {
133
+ ok: false,
134
+ code: "settings-rejected",
135
+ message: "malformed bridge mutate ops"
136
+ });
137
+ return;
138
+ }
139
+ const expectedRevision = typeof body.expectedRevision === "number" ? body.expectedRevision : void 0;
140
+ mutate.call(svc, ns, ops, expectedRevision).then(() => {
141
+ const descriptor = svc.describe({ redactSecrets: true }).find((d) => d.ns === ns);
142
+ if (descriptor === void 0) {
143
+ sendJson(res, 200, {
144
+ ok: false,
145
+ code: "internal",
146
+ message: `namespace "${ns}" disposed after mutate`
147
+ });
148
+ return;
149
+ }
150
+ sendJson(res, 200, {
151
+ ok: true,
152
+ value: { descriptor }
153
+ });
154
+ }).catch((e) => {
155
+ sendJson(res, 200, isConflict(e) ? {
156
+ ok: false,
157
+ code: "settings-conflict",
158
+ message: e instanceof Error ? e.message : String(e)
159
+ } : {
160
+ ok: false,
161
+ code: "internal",
162
+ message: e instanceof Error ? e.message : String(e)
163
+ });
164
+ });
165
+ }).catch((e) => {
166
+ sendJson(res, 200, {
167
+ ok: false,
168
+ code: "bad-request",
169
+ message: e instanceof Error ? e.message : String(e)
170
+ });
171
+ });
172
+ }
173
+ }));
174
+ return offs;
175
+ }
176
+ //#endregion
1
177
  //#region lib/types/index.js
2
- /** @param ctx - host-side context; intentionally unused (client-plugin shape). */
3
- function apply(ctx) {}
178
+ /** The settings namespace the browser half pins (`ui-conversation.busyEnter`). */
179
+ const BRIDGE_NS = "ui-conversation";
180
+ /** webServer hosts the bridge route pair. */
181
+ const inject = ["webServer"];
182
+ /** @param ctx - host-side context; registers the bridge route pair. */
183
+ function apply(ctx) {
184
+ const settingsRef = {};
185
+ ctx.inject(["settings"], (child) => {
186
+ settingsRef.svc = child.settings;
187
+ });
188
+ ctx.effect(() => {
189
+ const injected = ctx;
190
+ const offs = registerSettingsBridgeRoutes(injected.webServer ?? injected.get?.("webServer"), () => settingsRef.svc, BRIDGE_NS);
191
+ return () => {
192
+ for (const off of offs) off();
193
+ };
194
+ }, "dsh-input-traffic: settings bridge routes");
195
+ }
4
196
  //#endregion
5
- export { apply };
197
+ export { apply, inject };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * settings bridge — T20-b port from dsh-perm-gate (5260fe6 generalized form).
3
+ *
4
+ * The plugin is a pure client takeover on every host line, but its durable
5
+ * data plane (the `ui-conversation` busy-Enter pin) rides the client settings
6
+ * handles that are generation-exclusive: `configForms` (0.1.7+) works,
7
+ * `settingsScope` (≤0.1.5) resolves to nothing (the A2 dead end), so on old
8
+ * lines both the family card and the pin itself silently no-op. This bridge
9
+ * gives the browser half a same-origin HTTP data plane backed by the HOST's
10
+ * settings service — reads = svc.describe of our ns, writes = svc.mutate ops.
11
+ * No second copy of state: the service stays the single source of truth.
12
+ * Guards: POST-only, ns fixed at registration, optimistic concurrency via
13
+ * expectedRevision. The 0.1.0 host settings service carries the full
14
+ * describe/mutate/redactSecrets surface (lib/index.js verified), so the
15
+ * oldest line needs no fallback path.
16
+ *
17
+ * Data consumer = client BridgeDocHandle (src/client/bridge-scope.ts) — the
18
+ * T13b dual-track: native handle first (0.1.7+), this bridge underneath.
19
+ * Routes derive from the ns (bridgeRoutesFor) so the URL contract cannot
20
+ * drift between the two halves; the parity test pins both derivations
21
+ * character-for-character.
22
+ */
23
+ interface SettingsServiceLike {
24
+ update?(ns: string, patch: object): Promise<void>;
25
+ describe?(o?: {
26
+ redactSecrets?: boolean;
27
+ }): Array<{
28
+ ns: unknown;
29
+ [k: string]: unknown;
30
+ }>;
31
+ mutate?(ns: string, ops: Array<{
32
+ op: string;
33
+ path: string[];
34
+ value?: unknown;
35
+ }>, expectedRevision?: number): Promise<unknown>;
36
+ writable?: boolean;
37
+ }
38
+ /** Route pair for one settings ns — client and server must derive identically. */
39
+ export declare function bridgeRoutesFor(ns: string): {
40
+ describe: string;
41
+ mutate: string;
42
+ };
43
+ export declare function registerSettingsBridgeRoutes(server: unknown, getSettingsSvc: () => SettingsServiceLike | undefined, ns: string): Array<() => void>;
44
+ export {};
45
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,UAAU,mBAAmB;IAC3B,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,KAAK,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAA;IACxF,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7H,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAEhF;AAuCD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EACf,cAAc,EAAE,MAAM,mBAAmB,GAAG,SAAS,EACrD,EAAE,EAAE,MAAM,GACT,KAAK,CAAC,MAAM,IAAI,CAAC,CA6EnB"}
@@ -0,0 +1,130 @@
1
+ /** Route pair for one settings ns — client and server must derive identically. */
2
+ export function bridgeRoutesFor(ns) {
3
+ return { describe: `/api/${ns}/settings/describe`, mutate: `/api/${ns}/settings/mutate` };
4
+ }
5
+ function readJsonBody(reqRaw, limit = 256 * 1024) {
6
+ const req = reqRaw;
7
+ return new Promise((resolve, reject) => {
8
+ const chunks = [];
9
+ let size = 0;
10
+ req.on('data', (chunk) => {
11
+ const buf = chunk;
12
+ size += buf.length;
13
+ if (size > limit) {
14
+ reject(new Error('bridge body too large'));
15
+ req.resume?.();
16
+ return;
17
+ }
18
+ chunks.push(buf);
19
+ });
20
+ req.on('end', () => {
21
+ const raw = Buffer.concat(chunks).toString('utf8');
22
+ if (raw === '') {
23
+ resolve({});
24
+ return;
25
+ }
26
+ try {
27
+ resolve(JSON.parse(raw));
28
+ }
29
+ catch (e) {
30
+ reject(e instanceof Error ? e : new Error(String(e)));
31
+ }
32
+ });
33
+ req.on('error', reject);
34
+ });
35
+ }
36
+ function sendJson(res, code, body) {
37
+ res.writeHead(code, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-cache' });
38
+ res.end(JSON.stringify(body));
39
+ }
40
+ function isConflict(e) {
41
+ return e instanceof Error && /conflict|revision/i.test(e.message);
42
+ }
43
+ export function registerSettingsBridgeRoutes(server, getSettingsSvc, ns) {
44
+ if (typeof server !== 'object' || server === null)
45
+ return [];
46
+ const ws = typeof server.register === 'function' ? server : undefined;
47
+ if (ws === undefined)
48
+ return [];
49
+ const offs = [];
50
+ const routes = bridgeRoutesFor(ns);
51
+ offs.push(ws.register({
52
+ kind: 'exact',
53
+ path: routes.describe,
54
+ handler: (rawReq, rawRes) => {
55
+ const req = rawReq;
56
+ const res = rawRes;
57
+ if (req.method !== 'POST') {
58
+ sendJson(res, 405, { ok: false, code: 'method-not-allowed' });
59
+ return;
60
+ }
61
+ const svc = getSettingsSvc();
62
+ if (svc === undefined || typeof svc.describe !== 'function') {
63
+ sendJson(res, 200, { ok: false, code: 'settings-unavailable', message: 'settings service not resolved yet' });
64
+ return;
65
+ }
66
+ try {
67
+ const descriptor = svc
68
+ .describe({ redactSecrets: true })
69
+ .find((d) => d.ns === ns);
70
+ if (descriptor === undefined) {
71
+ sendJson(res, 200, { ok: false, code: 'ns-not-registered', message: `settings namespace "${ns}" is not registered` });
72
+ return;
73
+ }
74
+ sendJson(res, 200, { ok: true, value: { descriptor, writable: svc.writable !== false } });
75
+ }
76
+ catch (e) {
77
+ sendJson(res, 200, { ok: false, code: 'internal', message: e instanceof Error ? e.message : String(e) });
78
+ }
79
+ },
80
+ }));
81
+ offs.push(ws.register({
82
+ kind: 'exact',
83
+ path: routes.mutate,
84
+ handler: (rawReq, rawRes) => {
85
+ const req = rawReq;
86
+ const res = rawRes;
87
+ if (req.method !== 'POST') {
88
+ sendJson(res, 405, { ok: false, code: 'method-not-allowed' });
89
+ return;
90
+ }
91
+ const svc = getSettingsSvc();
92
+ const mutate = svc === undefined ? undefined : svc.mutate;
93
+ if (typeof mutate !== 'function') {
94
+ sendJson(res, 200, { ok: false, code: 'settings-unavailable', message: 'settings service mutate not available' });
95
+ return;
96
+ }
97
+ readJsonBody(req).then((body) => {
98
+ const ops = body.ops;
99
+ if (!Array.isArray(ops) || ops.some((op) => !op || typeof op !== 'object' || !(op.op in { set: 1, unset: 1 }))) {
100
+ sendJson(res, 200, { ok: false, code: 'settings-rejected', message: 'malformed bridge mutate ops' });
101
+ return;
102
+ }
103
+ const expectedRevision = typeof body.expectedRevision === 'number' ? body.expectedRevision : undefined;
104
+ mutate
105
+ .call(svc, ns, ops, expectedRevision)
106
+ .then(() => {
107
+ // Successful mutate re-describes: writes stay funneled through the
108
+ // host service, no shadow state anywhere.
109
+ const descriptor = svc
110
+ .describe({ redactSecrets: true })
111
+ .find((d) => d.ns === ns);
112
+ if (descriptor === undefined) {
113
+ sendJson(res, 200, { ok: false, code: 'internal', message: `namespace "${ns}" disposed after mutate` });
114
+ return;
115
+ }
116
+ sendJson(res, 200, { ok: true, value: { descriptor } });
117
+ })
118
+ .catch((e) => {
119
+ sendJson(res, 200, isConflict(e)
120
+ ? { ok: false, code: 'settings-conflict', message: e instanceof Error ? e.message : String(e) }
121
+ : { ok: false, code: 'internal', message: e instanceof Error ? e.message : String(e) });
122
+ });
123
+ }).catch((e) => {
124
+ sendJson(res, 200, { ok: false, code: 'bad-request', message: e instanceof Error ? e.message : String(e) });
125
+ });
126
+ },
127
+ }));
128
+ return offs;
129
+ }
130
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * BridgeDocHandle — T20-b port from dsh-perm-gate (bridge-scope.ts, the
3
+ * client half of the settings bridge).
4
+ *
5
+ * Shape-compatible with the native durable-settings handle (configForms.get /
6
+ * settingsScope.bind subset): getSnapshot/subscribe/set. Reads POST
7
+ * /api/<ns>/settings/describe, writes POST …/mutate (ops forwarded to the
8
+ * host settings service); a successful write re-describes and refreshes the
9
+ * snapshot (optimistic refresh, no local second copy of state — the service
10
+ * stays authoritative). The initial describe fetch starts at construction;
11
+ * the snapshot is `pending` until it lands, and the family card renders its
12
+ * pinned default meanwhile.
13
+ *
14
+ * The route derivation must stay character-identical to the server side
15
+ * (src/bridge.ts bridgeRoutesFor); the client tsconfig only includes
16
+ * src/client, so this half holds its own copy and the parity test pins both.
17
+ */
18
+ import type { SettingsDocHandle } from './compat.js';
19
+ /** The native handle's snapshot shape — the bridge payload cast at the boundary. */
20
+ type DocSnapshot = ReturnType<SettingsDocHandle['getSnapshot']>;
21
+ export declare class BridgeDocHandle implements SettingsDocHandle {
22
+ private snapshot;
23
+ private listeners;
24
+ private revision;
25
+ private inflight;
26
+ private readonly routes;
27
+ constructor(ns: string);
28
+ /** The bridge transport is shape-agnostic; the ns payload narrows at this boundary. */
29
+ getSnapshot(): DocSnapshot;
30
+ subscribe(listener: () => void): () => void;
31
+ set(field: string, value: unknown): Promise<void>;
32
+ private notify;
33
+ private request;
34
+ private refresh;
35
+ private mutate;
36
+ }
37
+ export {};
38
+ //# sourceMappingURL=bridge-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-scope.d.ts","sourceRoot":"","sources":["../../../src/client/bridge-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAOpD,oFAAoF;AACpF,KAAK,WAAW,GAAG,UAAU,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAA;AAM/D,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,OAAO,CAAC,QAAQ,CAAoE;IACpF,OAAO,CAAC,SAAS,CAAwB;IACzC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;gBAEjD,EAAE,EAAE,MAAM;IAKtB,uFAAuF;IACvF,WAAW,IAAI,WAAW;IAI1B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKrC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,OAAO,CAAC,MAAM;YAIA,OAAO;YAUP,OAAO;YAsBP,MAAM;CAarB"}
@@ -0,0 +1,81 @@
1
+ /** Must match src/bridge.ts bridgeRoutesFor character for character. */
2
+ function bridgeRoutesFor(ns) {
3
+ return { describe: `/api/${ns}/settings/describe`, mutate: `/api/${ns}/settings/mutate` };
4
+ }
5
+ export class BridgeDocHandle {
6
+ snapshot = { status: 'pending', value: undefined, writable: true };
7
+ listeners = new Set();
8
+ revision;
9
+ inflight;
10
+ routes;
11
+ constructor(ns) {
12
+ this.routes = bridgeRoutesFor(ns);
13
+ void this.refresh();
14
+ }
15
+ /** The bridge transport is shape-agnostic; the ns payload narrows at this boundary. */
16
+ getSnapshot() {
17
+ return this.snapshot;
18
+ }
19
+ subscribe(listener) {
20
+ this.listeners.add(listener);
21
+ return () => { this.listeners.delete(listener); };
22
+ }
23
+ async set(field, value) {
24
+ await this.mutate([{ op: 'set', path: [field], value }]);
25
+ }
26
+ notify() {
27
+ for (const l of [...this.listeners])
28
+ l();
29
+ }
30
+ async request(path, body) {
31
+ const response = await fetch(path, {
32
+ method: 'POST',
33
+ headers: { 'content-type': 'application/json' },
34
+ body: JSON.stringify(body ?? {}),
35
+ });
36
+ if (!response.ok)
37
+ throw new Error(`bridge ${path} HTTP ${response.status}`);
38
+ return await response.json();
39
+ }
40
+ async refresh() {
41
+ if (this.inflight !== undefined)
42
+ return this.inflight;
43
+ this.inflight = (async () => {
44
+ try {
45
+ const body = await this.request(this.routes.describe);
46
+ if (body.ok && body.value?.descriptor !== undefined) {
47
+ const d = body.value.descriptor;
48
+ this.revision = typeof d.revision === 'number' ? d.revision : undefined;
49
+ this.snapshot = { status: 'ready', value: d.value, writable: body.value.writable !== false };
50
+ }
51
+ else {
52
+ this.snapshot = { status: 'error', value: undefined, writable: false };
53
+ }
54
+ }
55
+ catch {
56
+ this.snapshot = { status: 'error', value: undefined, writable: false };
57
+ }
58
+ finally {
59
+ this.inflight = undefined;
60
+ }
61
+ this.notify();
62
+ })();
63
+ return this.inflight;
64
+ }
65
+ async mutate(ops) {
66
+ const body = await this.request(this.routes.mutate, { ops, expectedRevision: this.revision });
67
+ if (body.ok && body.value?.descriptor !== undefined) {
68
+ const d = body.value.descriptor;
69
+ this.revision = typeof d.revision === 'number' ? d.revision : undefined;
70
+ this.snapshot = { status: 'ready', value: d.value, writable: this.snapshot.writable };
71
+ }
72
+ else if (body.code === 'settings-conflict') {
73
+ await this.refresh(); // optimistic concurrency lost: pull the authoritative value so the next write carries a fresh revision
74
+ }
75
+ else {
76
+ throw new Error(`bridge mutate failed: ${body.code ?? 'unknown'} ${body.message ?? ''}`);
77
+ }
78
+ this.notify();
79
+ }
80
+ }
81
+ //# sourceMappingURL=bridge-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-scope.js","sourceRoot":"","sources":["../../../src/client/bridge-scope.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../../../src/client/compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/F,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB;IACpB,KAAK,EAAE,OAAO,CAAA;IACd;gFAC4E;IAC5E,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAC5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAevF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE;IAAE,KAAK,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAA;CAAE,EACxD,IAAI,EAAE,OAAO,GACZ,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CASxB"}
1
+ {"version":3,"file":"compat.d.ts","sourceRoot":"","sources":["../../../src/client/compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/F,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB;IACpB,KAAK,EAAE,OAAO,CAAA;IACd;gFAC4E;IAC5E,OAAO,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAC5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAyBvF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE;IAAE,KAAK,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAA;CAAE,EACxD,IAAI,EAAE,OAAO,GACZ,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CASxB"}
@@ -7,17 +7,27 @@
7
7
  * as the family tab's slot-inject when the family holder is absent).
8
8
  */
9
9
  export function registerSettingsFaces(ctx, deps) {
10
+ // cordis inject 回调收到的是 (ctx, config)——服务要按名从 scope 对象上读
11
+ // (thinking-levels 腰的同款形态;0.7.1 曾把参数当裸服务 .get/.bind 调用,
12
+ // 每一代宿主上都是 TypeError,纤维暗死 → onScope 永不回调 → 卡永远降级)。
13
+ const scopeInject = ctx.inject;
10
14
  // Modern hosts (0.1.7+): configForms owns cross-entry durable writes.
11
- ctx.inject(['configForms'], (configForms) => {
12
- const scope = configForms.get(deps.namespace);
13
- void scope.set(deps.field, deps.value);
14
- deps.onScope(scope);
15
+ scopeInject(['configForms'], (scope) => {
16
+ const forms = scope.configForms;
17
+ if (forms === undefined)
18
+ return;
19
+ const handle = forms.get(deps.namespace);
20
+ void handle.set(deps.field, deps.value);
21
+ deps.onScope(handle);
15
22
  });
16
23
  // Old hosts (≤0.1.5): namespaced durable scope via bind().
17
- ctx.inject(['settingsScope'], (settingsScope) => {
18
- const scope = settingsScope.bind({ namespace: deps.namespace });
19
- void scope.set(deps.field, deps.value);
20
- deps.onScope(scope);
24
+ scopeInject(['settingsScope'], (scope) => {
25
+ const settingsScope = scope.settingsScope;
26
+ if (settingsScope === undefined)
27
+ return;
28
+ const handle = settingsScope.bind({ namespace: deps.namespace });
29
+ void handle.set(deps.field, deps.value);
30
+ deps.onScope(handle);
21
31
  });
22
32
  }
23
33
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"compat.js","sourceRoot":"","sources":["../../../src/client/compat.ts"],"names":[],"mappings":"AA6CA;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,IAAuB;IAC/E,sEAAsE;IACtE,GAAG,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,WAAoB,EAAE,EAAE;QACnD,MAAM,KAAK,GAAI,WAA6D,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAChG,KAAK,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;IACF,2DAA2D;IAC3D,GAAG,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,aAAsB,EAAE,EAAE;QACvD,MAAM,KAAK,GAAI,aAEb,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QACtC,KAAK,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,YAAwD,EACxD,IAAa;IAEb,OAAO,CAAC,IAAY,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAGtC,CAAA;QACD,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU;YAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;;YACrD,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"compat.js","sourceRoot":"","sources":["../../../src/client/compat.ts"],"names":[],"mappings":"AA6CA;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,IAAuB;IAC/E,wDAAwD;IACxD,wDAAwD;IACxD,mDAAmD;IACnD,MAAM,WAAW,GAAI,GAEnB,CAAC,MAAM,CAAA;IACT,sEAAsE;IACtE,WAAW,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAwE,CAAA;QAC5F,IAAI,KAAK,KAAK,SAAS;YAAE,OAAM;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACxC,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IACF,2DAA2D;IAC3D,WAAW,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACvC,MAAM,aAAa,GAAG,KAAK,CAAC,aAEf,CAAA;QACb,IAAI,aAAa,KAAK,SAAS;YAAE,OAAM;QACvC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QAChE,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,YAAwD,EACxD,IAAa;IAEb,OAAO,CAAC,IAAY,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAGtC,CAAA;QACD,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU;YAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;;YACrD,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC,CAAA;AACH,CAAC"}
@@ -22,7 +22,8 @@ export interface FamilyCardScope {
22
22
  subscribe(listener: () => void): () => void;
23
23
  }
24
24
  export interface FamilyCardProps {
25
- scope: FamilyCardScope;
25
+ /** 活引用:≤0.1.5 settingsScope 永不 resolve 时保持 undefined(降级渲染)。 */
26
+ scope?: FamilyCardScope;
26
27
  t?: (key: string, params?: Record<string, unknown>) => string;
27
28
  }
28
29
  export declare function InputTrafficFamilyCard({ scope, t }: FamilyCardProps): JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"family-card.d.ts","sourceRoot":"","sources":["../../../src/client/family-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAA2D,KAAK,GAAG,EAAE,MAAM,OAAO,CAAA;AAEzF,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,WAAW,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/F,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,CAAA;IACtB,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAA;CAC9D;AAMD,wBAAgB,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CAcjF"}
1
+ {"version":3,"file":"family-card.d.ts","sourceRoot":"","sources":["../../../src/client/family-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAA2D,KAAK,GAAG,EAAE,MAAM,OAAO,CAAA;AAEzF,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,WAAW,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC/F,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAA;CAC9D;AASD,wBAAgB,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,eAAe,GAAG,GAAG,CAAC,OAAO,CAwBjF"}
@@ -13,10 +13,20 @@ import { createElement, useSyncExternalStore } from 'react';
13
13
  const row = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '8px 0', fontSize: 13 };
14
14
  const label = { color: 'var(--dsw-alias-label-primary, inherit)' };
15
15
  const dim = { color: 'var(--dsw-alias-label-tertiary, rgba(127,127,127,.8))', fontSize: 12, lineHeight: 1.5 };
16
+ /** 无 scope 时的降级快照(模块级常量:useSyncExternalStore 要求稳定引用)。 */
17
+ const DEGRADED_SNAPSHOT = { status: 'unavailable', value: undefined, writable: false };
16
18
  export function InputTrafficFamilyCard({ scope, t }) {
17
- const snapshot = useSyncExternalStore((listener) => scope.subscribe(listener), () => scope.getSnapshot());
18
- const pinned = snapshot.value?.busyEnter ?? 'queue';
19
+ // hooks 无条件调用(rules-of-hooks):无 scope 时订阅为 no-op、快照取模块级
20
+ // 降级常量;scope 缺席与否在挂载期内是稳定的(双轨后 scope 恒有,本分支只
21
+ // 是防御位),不会触发 hook 序翻转。
22
+ const snapshot = useSyncExternalStore((listener) => scope?.subscribe(listener) ?? (() => { }), () => scope?.getSnapshot() ?? DEGRADED_SNAPSHOT);
19
23
  const tr = (key) => (t ? t(key) : FALLBACK_ZH[key] ?? key);
24
+ // scope 缺席(防御位,双轨后常态不可达)的降级态:卡仍在账本里
25
+ // (导航/接管节可见),数据缺席呈现不可用说明而不是崩/空白。
26
+ if (!scope) {
27
+ return createElement('div', { style: { display: 'grid', gap: 6 } }, createElement('p', { style: { ...dim, margin: 0 } }, `${tr('family.desc')}(当前宿主设置服务不可用 — 只读)`));
28
+ }
29
+ const pinned = snapshot.value?.busyEnter ?? 'queue';
20
30
  return createElement('div', { style: { display: 'grid', gap: 6 } }, createElement('div', { style: row }, createElement('span', { style: label }, tr('family.busyEnter')), createElement('span', { style: dim }, `busyEnter = ${pinned}`)), createElement('p', { style: { ...dim, margin: 0 } }, tr('family.desc')));
21
31
  }
22
32
  /** 无 locale 服务时的中文兜底(与 zh 字典键一一对应)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"family-card.js","sourceRoot":"","sources":["../../../src/client/family-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAgC,MAAM,OAAO,CAAA;AAazF,MAAM,GAAG,GAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;AAC9I,MAAM,KAAK,GAAkB,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAA;AACjF,MAAM,GAAG,GAAkB,EAAE,KAAK,EAAE,uDAAuD,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAA;AAE5H,MAAM,UAAU,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAmB;IAClE,MAAM,QAAQ,GAAG,oBAAoB,CACnC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,EACvC,GAAG,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAC1B,CAAA;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,OAAO,CAAA;IACnD,MAAM,EAAE,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;IAC1E,OAAO,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAChE,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EACjC,aAAa,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,EAC/D,aAAa,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,eAAe,MAAM,EAAE,CAAC,CAC/D,EACD,aAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CACxE,CAAA;AACH,CAAC;AAED,uCAAuC;AACvC,MAAM,WAAW,GAA2B;IAC1C,kBAAkB,EAAE,sBAAsB;IAC1C,aAAa,EAAE,gEAAgE;CAChF,CAAA"}
1
+ {"version":3,"file":"family-card.js","sourceRoot":"","sources":["../../../src/client/family-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAgC,MAAM,OAAO,CAAA;AAczF,MAAM,GAAG,GAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;AAC9I,MAAM,KAAK,GAAkB,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAA;AACjF,MAAM,GAAG,GAAkB,EAAE,KAAK,EAAE,uDAAuD,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAA;AAE5H,yDAAyD;AACzD,MAAM,iBAAiB,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;AAEtF,MAAM,UAAU,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAmB;IAClE,wDAAwD;IACxD,6CAA6C;IAC7C,uBAAuB;IACvB,MAAM,QAAQ,GAAG,oBAAoB,CACnC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,EACtD,GAAG,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,iBAAiB,CAChD,CAAA;IACD,MAAM,EAAE,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAA;IAC1E,oCAAoC;IACpC,iCAAiC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAChE,aAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAC/F,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,OAAO,CAAA;IACnD,OAAO,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAChE,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EACjC,aAAa,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,EAC/D,aAAa,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,eAAe,MAAM,EAAE,CAAC,CAC/D,EACD,aAAa,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CACxE,CAAA;AACH,CAAC;AAED,uCAAuC;AACvC,MAAM,WAAW,GAA2B;IAC1C,kBAAkB,EAAE,sBAAsB;IAC1C,aAAa,EAAE,gEAAgE;CAChF,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAmBnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAkBpC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAkD,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAgG9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoBnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAkBpC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAkD,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA0H9C"}