@qlzt/dsh-api-remote-access 0.0.0-stage → 0.2.1-rc.2

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,733 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@qlzt/dsh-api-remote-access",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let zod = require("zod");
8
+ let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
9
+ let _deepseek_ai_dsh_api_gateway_client = require("@qlzt/dsh-api-gateway/client");
10
+ //#region ../../typert/protocol/lib/types/remote-info.js
11
+ /** Lazy assembly of generated Remote definitions using caller-supplied Zod; no runtime imports. */
12
+ /**
13
+ * Retain one generated definition per endpoint without constructing parsers or projections.
14
+ * The class instance builds its shared descriptor/method index on first access;
15
+ * input schemas and JSON Schema projections are cached only when requested.
16
+ * @param spec - compiler-produced method facts and Zod factories.
17
+ * @param zod - the generated artifact's Zod runtime for named-input parsers.
18
+ * @returns a lazy, mountable info instance with no registration or transport effects.
19
+ */
20
+ function createTypertRemoteInfo(spec, zod) {
21
+ return new RemoteInfo(spec, zod);
22
+ }
23
+ var RemoteInfo = class {
24
+ spec;
25
+ zod;
26
+ index;
27
+ constructor(spec, zod) {
28
+ this.spec = spec;
29
+ this.zod = zod;
30
+ }
31
+ get package() {
32
+ return this.spec.package;
33
+ }
34
+ get descriptors() {
35
+ return this.resolve().descriptors;
36
+ }
37
+ get methods() {
38
+ return this.resolve().methods;
39
+ }
40
+ resolve() {
41
+ if (this.index !== void 0) return this.index;
42
+ const zod = this.zod;
43
+ const descriptors = [];
44
+ const methods = Object.create(null);
45
+ for (const [endpoint, definition] of Object.entries(this.spec.methods)) {
46
+ const separator = endpoint.indexOf("/");
47
+ const namespace = endpoint.slice(0, separator);
48
+ const method = endpoint.slice(separator + 1);
49
+ const parameters = definition.parameters.map((parameter) => ({
50
+ ...parameter,
51
+ wire: parameter.wire ?? parameter.name
52
+ }));
53
+ const descriptor = {
54
+ ...definition,
55
+ id: `${this.spec.package}#${endpoint}`,
56
+ namespace,
57
+ method,
58
+ parameters,
59
+ result: definition.result.description === void 0 ? definition.result : new ResultValue(definition.result)
60
+ };
61
+ if (definition.invocation.kind === "direct") {
62
+ const inputs = [];
63
+ methods[endpoint] = Object.assign(descriptor, {
64
+ input(options) {
65
+ const strict = options?.strict === true;
66
+ return inputs[strict ? 1 : 0] ??= new NamedInput(parameters, strict, zod);
67
+ },
68
+ invoke(remote, input, signal) {
69
+ const named = input;
70
+ const values = parameters.map((parameter) => named[parameter.wire]);
71
+ if (definition.cancellation !== void 0) values.push(signal);
72
+ const receiver = Reflect.get(remote, namespace);
73
+ return Reflect.get(receiver, method).apply(receiver, values);
74
+ }
75
+ });
76
+ }
77
+ descriptors.push(descriptor);
78
+ }
79
+ return this.index = {
80
+ descriptors,
81
+ methods
82
+ };
83
+ }
84
+ };
85
+ function optional(parameter) {
86
+ return parameter.source === "json" && parameter.acceptsUndefined === true;
87
+ }
88
+ var NamedInput = class {
89
+ parameters;
90
+ strict;
91
+ zod;
92
+ schema;
93
+ projection;
94
+ constructor(parameters, strict, zod) {
95
+ this.parameters = parameters;
96
+ this.strict = strict;
97
+ this.zod = zod;
98
+ }
99
+ codec(parameter) {
100
+ return this.strict ? parameter.strictCodec : parameter.codec;
101
+ }
102
+ create = () => this.schema ??= this.zod.strictObject(Object.fromEntries(this.parameters.map((parameter) => {
103
+ const value = this.codec(parameter).create();
104
+ return [parameter.wire, optional(parameter) ? value.optional() : value.nonoptional()];
105
+ })));
106
+ get jsonSchema() {
107
+ return this.project().jsonSchema;
108
+ }
109
+ get jsonSchemaError() {
110
+ return this.project().jsonSchemaError;
111
+ }
112
+ project() {
113
+ if (this.projection !== void 0) return this.projection;
114
+ const unavailable = this.parameters.find((parameter) => this.codec(parameter).jsonSchema === void 0);
115
+ if (unavailable !== void 0) return this.projection = { jsonSchemaError: this.codec(unavailable).jsonSchemaError ?? `No JSON Schema for parameter ${unavailable.wire}` };
116
+ return this.projection = { jsonSchema: {
117
+ type: "object",
118
+ properties: Object.fromEntries(this.parameters.map((parameter) => [parameter.wire, {
119
+ ...this.codec(parameter).jsonSchema,
120
+ ...parameter.description === void 0 ? {} : { description: parameter.description }
121
+ }])),
122
+ required: this.parameters.filter((parameter) => !optional(parameter)).map((parameter) => parameter.wire),
123
+ additionalProperties: false
124
+ } };
125
+ }
126
+ };
127
+ var ResultValue = class {
128
+ spec;
129
+ projection;
130
+ constructor(spec) {
131
+ this.spec = spec;
132
+ }
133
+ get mode() {
134
+ return this.spec.mode;
135
+ }
136
+ get typeSymbol() {
137
+ return this.spec.typeSymbol;
138
+ }
139
+ get create() {
140
+ return this.spec.create;
141
+ }
142
+ get decode() {
143
+ return this.spec.decode;
144
+ }
145
+ get encode() {
146
+ return this.spec.encode;
147
+ }
148
+ get description() {
149
+ return this.spec.description;
150
+ }
151
+ get jsonSchemaError() {
152
+ return this.spec.jsonSchemaError;
153
+ }
154
+ get jsonSchema() {
155
+ if (this.projection === void 0) {
156
+ const schema = this.spec.jsonSchema;
157
+ this.projection = { value: schema === void 0 ? void 0 : {
158
+ ...schema,
159
+ description: this.spec.description
160
+ } };
161
+ }
162
+ return this.projection.value;
163
+ }
164
+ };
165
+ //#endregion
166
+ //#region lib/typert.remote-client.js
167
+ let _deepseek_ai_dsh_api_remote_access_remoteAccess_follow_result$schema$value;
168
+ const _deepseek_ai_dsh_api_remote_access_remoteAccess_follow_result$schema = () => _deepseek_ai_dsh_api_remote_access_remoteAccess_follow_result$schema$value ??= zod.z.union([
169
+ zod.z.object({
170
+ "type": zod.z.literal("baseline").readonly(),
171
+ "owner": zod.z.literal(true).readonly()
172
+ }),
173
+ zod.z.object({
174
+ "type": zod.z.literal("baseline").readonly(),
175
+ "owner": zod.z.literal(false).readonly(),
176
+ "grants": zod.z.array(zod.z.object({
177
+ "resource": zod.z.union([
178
+ zod.z.object({ "kind": zod.z.literal("global").readonly() }),
179
+ zod.z.object({
180
+ "kind": zod.z.literal("session").readonly(),
181
+ "id": zod.z.string().readonly()
182
+ }),
183
+ zod.z.object({
184
+ "kind": zod.z.literal("workspace").readonly(),
185
+ "id": zod.z.string().readonly()
186
+ })
187
+ ]).readonly(),
188
+ "actions": zod.z.array(zod.z.string()).readonly().describe("Endpoint actions allowed on this resource without call arguments, sorted; plain strings because Typert generates no codec for template literal types.")
189
+ })).readonly()
190
+ }),
191
+ zod.z.object({
192
+ "type": zod.z.literal("upsert").readonly(),
193
+ "grant": zod.z.object({
194
+ "resource": zod.z.union([
195
+ zod.z.object({ "kind": zod.z.literal("global").readonly() }),
196
+ zod.z.object({
197
+ "kind": zod.z.literal("session").readonly(),
198
+ "id": zod.z.string().readonly()
199
+ }),
200
+ zod.z.object({
201
+ "kind": zod.z.literal("workspace").readonly(),
202
+ "id": zod.z.string().readonly()
203
+ })
204
+ ]).readonly(),
205
+ "actions": zod.z.array(zod.z.string()).readonly().describe("Endpoint actions allowed on this resource without call arguments, sorted; plain strings because Typert generates no codec for template literal types.")
206
+ }).readonly()
207
+ }),
208
+ zod.z.object({
209
+ "type": zod.z.literal("remove").readonly(),
210
+ "resource": zod.z.union([
211
+ zod.z.object({ "kind": zod.z.literal("global").readonly() }),
212
+ zod.z.object({
213
+ "kind": zod.z.literal("session").readonly(),
214
+ "id": zod.z.string().readonly()
215
+ }),
216
+ zod.z.object({
217
+ "kind": zod.z.literal("workspace").readonly(),
218
+ "id": zod.z.string().readonly()
219
+ })
220
+ ]).readonly()
221
+ })
222
+ ]);
223
+ const TYPERT_REMOTE = createTypertRemoteInfo({
224
+ package: "@qlzt/dsh-api-remote-access",
225
+ methods: { "remoteAccess/follow": {
226
+ service: "remoteAccess",
227
+ description: "Stream the calling Peer's access snapshot: one baseline, then `upsert` and `remove` frames after each `invalidate()` and each policy registration or removal that changes a grant. The owner receives only `{ owner: true }`. The generation ends when `signal` aborts or this service unloads, so a reloaded service starts every reader from a fresh baseline.",
228
+ mode: "stream",
229
+ invocation: { kind: "direct" },
230
+ parameters: [],
231
+ cancellation: { parameter: "signal" },
232
+ result: {
233
+ mode: "strict",
234
+ typeSymbol: "@qlzt/dsh-api-remote-access/types#RemoteAccessFrame",
235
+ create: _deepseek_ai_dsh_api_remote_access_remoteAccess_follow_result$schema,
236
+ description: "the access frames of one generation.",
237
+ jsonSchema: { "oneOf": [
238
+ {
239
+ "type": "object",
240
+ "properties": {
241
+ "type": {
242
+ "type": "string",
243
+ "const": "baseline"
244
+ },
245
+ "owner": {
246
+ "type": "boolean",
247
+ "const": true
248
+ }
249
+ },
250
+ "required": ["type", "owner"],
251
+ "additionalProperties": true
252
+ },
253
+ {
254
+ "type": "object",
255
+ "properties": {
256
+ "type": {
257
+ "type": "string",
258
+ "const": "baseline"
259
+ },
260
+ "owner": {
261
+ "type": "boolean",
262
+ "const": false
263
+ },
264
+ "grants": {
265
+ "type": "array",
266
+ "items": {
267
+ "type": "object",
268
+ "properties": {
269
+ "resource": { "oneOf": [
270
+ {
271
+ "type": "object",
272
+ "properties": { "kind": {
273
+ "type": "string",
274
+ "const": "global"
275
+ } },
276
+ "required": ["kind"],
277
+ "additionalProperties": true
278
+ },
279
+ {
280
+ "type": "object",
281
+ "properties": {
282
+ "kind": {
283
+ "type": "string",
284
+ "const": "session"
285
+ },
286
+ "id": { "type": "string" }
287
+ },
288
+ "required": ["kind", "id"],
289
+ "additionalProperties": true
290
+ },
291
+ {
292
+ "type": "object",
293
+ "properties": {
294
+ "kind": {
295
+ "type": "string",
296
+ "const": "workspace"
297
+ },
298
+ "id": { "type": "string" }
299
+ },
300
+ "required": ["kind", "id"],
301
+ "additionalProperties": true
302
+ }
303
+ ] },
304
+ "actions": {
305
+ "type": "array",
306
+ "items": { "type": "string" },
307
+ "description": "Endpoint actions allowed on this resource without call arguments, sorted; plain strings because Typert generates no codec for template literal types."
308
+ }
309
+ },
310
+ "required": ["resource", "actions"],
311
+ "additionalProperties": true
312
+ }
313
+ }
314
+ },
315
+ "required": [
316
+ "type",
317
+ "owner",
318
+ "grants"
319
+ ],
320
+ "additionalProperties": true
321
+ },
322
+ {
323
+ "type": "object",
324
+ "properties": {
325
+ "type": {
326
+ "type": "string",
327
+ "const": "upsert"
328
+ },
329
+ "grant": {
330
+ "type": "object",
331
+ "properties": {
332
+ "resource": { "oneOf": [
333
+ {
334
+ "type": "object",
335
+ "properties": { "kind": {
336
+ "type": "string",
337
+ "const": "global"
338
+ } },
339
+ "required": ["kind"],
340
+ "additionalProperties": true
341
+ },
342
+ {
343
+ "type": "object",
344
+ "properties": {
345
+ "kind": {
346
+ "type": "string",
347
+ "const": "session"
348
+ },
349
+ "id": { "type": "string" }
350
+ },
351
+ "required": ["kind", "id"],
352
+ "additionalProperties": true
353
+ },
354
+ {
355
+ "type": "object",
356
+ "properties": {
357
+ "kind": {
358
+ "type": "string",
359
+ "const": "workspace"
360
+ },
361
+ "id": { "type": "string" }
362
+ },
363
+ "required": ["kind", "id"],
364
+ "additionalProperties": true
365
+ }
366
+ ] },
367
+ "actions": {
368
+ "type": "array",
369
+ "items": { "type": "string" },
370
+ "description": "Endpoint actions allowed on this resource without call arguments, sorted; plain strings because Typert generates no codec for template literal types."
371
+ }
372
+ },
373
+ "required": ["resource", "actions"],
374
+ "additionalProperties": true
375
+ }
376
+ },
377
+ "required": ["type", "grant"],
378
+ "additionalProperties": true
379
+ },
380
+ {
381
+ "type": "object",
382
+ "properties": {
383
+ "type": {
384
+ "type": "string",
385
+ "const": "remove"
386
+ },
387
+ "resource": { "oneOf": [
388
+ {
389
+ "type": "object",
390
+ "properties": { "kind": {
391
+ "type": "string",
392
+ "const": "global"
393
+ } },
394
+ "required": ["kind"],
395
+ "additionalProperties": true
396
+ },
397
+ {
398
+ "type": "object",
399
+ "properties": {
400
+ "kind": {
401
+ "type": "string",
402
+ "const": "session"
403
+ },
404
+ "id": { "type": "string" }
405
+ },
406
+ "required": ["kind", "id"],
407
+ "additionalProperties": true
408
+ },
409
+ {
410
+ "type": "object",
411
+ "properties": {
412
+ "kind": {
413
+ "type": "string",
414
+ "const": "workspace"
415
+ },
416
+ "id": { "type": "string" }
417
+ },
418
+ "required": ["kind", "id"],
419
+ "additionalProperties": true
420
+ }
421
+ ] }
422
+ },
423
+ "required": ["type", "resource"],
424
+ "additionalProperties": true
425
+ }
426
+ ] }
427
+ },
428
+ sourceLocation: {
429
+ "file": "packages/api/remote-access/src/index.ts",
430
+ "line": 271,
431
+ "column": 10
432
+ }
433
+ } }
434
+ }, zod.z);
435
+ //#endregion
436
+ //#region lib/types/snapshot.js
437
+ /**
438
+ * The index global carrying a page's first access snapshot, shared by the
439
+ * Host row that writes it and the Client mount that reads it.
440
+ * @module @qlzt/dsh-api-remote-access/src/snapshot
441
+ */
442
+ /** `globalThis` property an index injection row assigns to the page's opening baseline. */
443
+ const REMOTE_ACCESS_SNAPSHOT_KEY = "__DSH_REMOTE_ACCESS__";
444
+ //#endregion
445
+ //#region lib/types/client/state.js
446
+ /**
447
+ * React-free Client access state: the latest `remoteAccess/follow` snapshot
448
+ * and identity-stable boolean sources per resource and action.
449
+ * @module @qlzt/dsh-api-remote-access/src/client/state
450
+ */
451
+ const CLOSED = Object.freeze({ kind: "closed" });
452
+ const OWNER_SNAPSHOT = Object.freeze({ kind: "owner" });
453
+ const GLOBAL_KEY = "\0global";
454
+ /**
455
+ * Validate the opening baseline an index injection row assigned to the page.
456
+ * @param value - the injected global; `undefined` when the page carries none.
457
+ * @returns the baseline, or `undefined` when none was injected.
458
+ * @throws TypeError when the injected value is not a baseline frame.
459
+ */
460
+ function parseInjectedBaseline(value) {
461
+ if (value === void 0) return void 0;
462
+ if (!isRecord(value) || value["type"] !== "baseline") throw new TypeError("remote access: the injected access snapshot is not a baseline frame");
463
+ if (value["owner"] === true) return {
464
+ type: "baseline",
465
+ owner: true
466
+ };
467
+ const grants = value["grants"];
468
+ if (value["owner"] !== false || !Array.isArray(grants) || !grants.every(isGrant)) throw new TypeError("remote access: the injected access snapshot carries malformed grants");
469
+ return {
470
+ type: "baseline",
471
+ owner: false,
472
+ grants
473
+ };
474
+ }
475
+ function isGrant(value) {
476
+ if (!isRecord(value) || !isResource(value["resource"])) return false;
477
+ const actions = value["actions"];
478
+ return Array.isArray(actions) && actions.every((action) => typeof action === "string");
479
+ }
480
+ function isResource(value) {
481
+ if (!isRecord(value) || typeof value["kind"] !== "string") return false;
482
+ return value["kind"] === "global" || typeof value["id"] === "string";
483
+ }
484
+ function isRecord(value) {
485
+ return typeof value === "object" && value !== null && !Array.isArray(value);
486
+ }
487
+ /**
488
+ * Snapshot owner for one Client generation. Sources are cached per
489
+ * `(kind, id, action)` so Hook bindings keep their identity; each source
490
+ * computes its boolean from the current snapshot.
491
+ */
492
+ var ClientAccessState = class {
493
+ snapshot = CLOSED;
494
+ /** @param initial - the page's injected opening baseline; without one every answer is false until a baseline arrives. */
495
+ constructor(initial) {
496
+ if (initial !== void 0) this.replace(initial);
497
+ }
498
+ listeners = /* @__PURE__ */ new Set();
499
+ sources = /* @__PURE__ */ new Map();
500
+ /**
501
+ * Answer for one action on the global resource.
502
+ * @param action - endpoint action the caller may invoke.
503
+ * @returns identity-stable boolean source.
504
+ */
505
+ global(action) {
506
+ return this.cached(GLOBAL_KEY, "", action);
507
+ }
508
+ /**
509
+ * Answer for one action on one resource of a declared kind.
510
+ * @param kind - declared resource kind.
511
+ * @param id - resource identity.
512
+ * @param action - endpoint action the caller may invoke.
513
+ * @returns identity-stable boolean source.
514
+ */
515
+ resource(kind, id, action) {
516
+ return this.cached(kind, id, action);
517
+ }
518
+ /**
519
+ * Replace the snapshot from one generation's opening frame.
520
+ * @param frame - owner or non-owner baseline.
521
+ */
522
+ replace(frame) {
523
+ if (frame.owner) {
524
+ this.publish(OWNER_SNAPSHOT);
525
+ return;
526
+ }
527
+ let global = /* @__PURE__ */ new Set();
528
+ const resources = /* @__PURE__ */ new Map();
529
+ for (const { resource, actions } of frame.grants) {
530
+ if (resource.kind === "global") {
531
+ global = new Set(actions);
532
+ continue;
533
+ }
534
+ let grants = resources.get(resource.kind);
535
+ if (grants === void 0) {
536
+ grants = /* @__PURE__ */ new Map();
537
+ resources.set(resource.kind, grants);
538
+ }
539
+ grants.set(resource.id, new Set(actions));
540
+ }
541
+ this.publish({
542
+ kind: "granted",
543
+ global,
544
+ resources
545
+ });
546
+ }
547
+ /**
548
+ * Apply one increment of the current generation.
549
+ * @param frame - `upsert` or `remove`.
550
+ */
551
+ update(frame) {
552
+ const current = this.snapshot;
553
+ if (current.kind !== "granted") return;
554
+ switch (frame.type) {
555
+ case "upsert":
556
+ this.publish(withGrant(current, frame.grant));
557
+ return;
558
+ case "remove":
559
+ this.publish(withGrant(current, {
560
+ resource: frame.resource,
561
+ actions: []
562
+ }, true));
563
+ return;
564
+ /* v8 ignore next -- the generated Remote codec validates this closed union. */
565
+ default: return assertNever(frame);
566
+ }
567
+ }
568
+ /** Whether the current snapshot is the owner's, whose answers never depend on the stream. */
569
+ get owner() {
570
+ return this.snapshot.kind === "owner";
571
+ }
572
+ /** The stream failed terminally: the owner keeps every answer; a non-owner's answers become false. */
573
+ unavailable() {
574
+ if (this.snapshot.kind !== "owner") this.publish(CLOSED);
575
+ }
576
+ cached(kind, id, action) {
577
+ const key = `${kind}\u0000${id}\u0000${action}`;
578
+ let source = this.sources.get(key);
579
+ if (source === void 0) {
580
+ source = {
581
+ getSnapshot: () => answer(this.snapshot, kind, id, action),
582
+ subscribe: (listener) => {
583
+ this.listeners.add(listener);
584
+ return () => {
585
+ this.listeners.delete(listener);
586
+ };
587
+ }
588
+ };
589
+ this.sources.set(key, source);
590
+ }
591
+ return source;
592
+ }
593
+ publish(next) {
594
+ this.snapshot = next;
595
+ for (const listener of [...this.listeners]) listener();
596
+ }
597
+ };
598
+ function answer(snapshot, kind, id, action) {
599
+ switch (snapshot.kind) {
600
+ case "closed": return false;
601
+ case "owner": return true;
602
+ case "granted": return (kind === GLOBAL_KEY ? snapshot.global : snapshot.resources.get(kind)?.get(id))?.has(action) ?? false;
603
+ /* v8 ignore next -- closed union. */
604
+ default: return assertNever(snapshot);
605
+ }
606
+ }
607
+ function withGrant(snapshot, grant, remove = false) {
608
+ const resource = grant.resource;
609
+ if (resource.kind === "global") return {
610
+ ...snapshot,
611
+ global: new Set(grant.actions)
612
+ };
613
+ const resources = new Map(snapshot.resources);
614
+ const grants = new Map(resources.get(resource.kind));
615
+ if (remove) grants.delete(resource.id);
616
+ else grants.set(resource.id, new Set(grant.actions));
617
+ resources.set(resource.kind, grants);
618
+ return {
619
+ ...snapshot,
620
+ resources
621
+ };
622
+ }
623
+ /* v8 ignore next 3 -- closed-union backstop. */
624
+ function assertNever(value) {
625
+ throw new Error(`unreachable access frame: ${JSON.stringify(value)}`);
626
+ }
627
+ //#endregion
628
+ //#region lib/types/client/mount.js
629
+ /**
630
+ * Client access mount: follows the calling Peer's access snapshot, provides
631
+ * the `remoteAccess` Client service, and publishes the global-resource `useRemoteAccess` Hook.
632
+ * The entry module supplies the generated `remoteAccess` contribution, so
633
+ * specs mount a fake one.
634
+ * @module @qlzt/dsh-api-remote-access/src/client/mount
635
+ */
636
+ /** The `remoteAccess` Client service over one snapshot owner. */
637
+ var ClientRemoteAccessService = class extends _deepseek_ai_cordis.Service {
638
+ state;
639
+ /**
640
+ * @param ctx - Client Context the service is provided on.
641
+ * @param state - snapshot owner answering every source.
642
+ */
643
+ constructor(ctx, state) {
644
+ super(ctx, "remoteAccess");
645
+ this.state = state;
646
+ }
647
+ source(kind, id, action) {
648
+ return this.state.resource(kind, id, action);
649
+ }
650
+ };
651
+ /**
652
+ * Mount the `remoteAccess` namespace, follow the access snapshot, provide the
653
+ * `remoteAccess` Client service, and publish the global-resource `useRemoteAccess` Hook.
654
+ * Answers start from the page's injected baseline, or false without one,
655
+ * and follow the stream's frames from then on.
656
+ * @param ctx - Client root Context injecting `remote` and `slots`.
657
+ * @param contribution - generated `remoteAccess` Remote contribution.
658
+ * @param initial - the page's injected opening baseline, read before the stream connects.
659
+ * @returns disposer that withdraws the namespace after the stream stops.
660
+ */
661
+ async function mountAccess(ctx, contribution, initial) {
662
+ const state = new ClientAccessState(initial);
663
+ new ClientRemoteAccessService(ctx, state);
664
+ ctx.slots.provideRoot({ keyedHooks: { remoteAccess: (action) => state.global(action) } });
665
+ const unmount = await ctx.remote.$mount(contribution);
666
+ const follow = ctx.inject(["remote.remoteAccess"], (remoteCtx) => {
667
+ const control = createAccessStream(remoteCtx.remote, state, remoteCtx.logger);
668
+ control.start();
669
+ remoteCtx.effect(() => async () => {
670
+ await control.dispose();
671
+ }, "remote-access.client.follow");
672
+ });
673
+ return async () => {
674
+ await follow.dispose();
675
+ await unmount();
676
+ };
677
+ }
678
+ /**
679
+ * Create the reconnecting access snapshot stream. A carrier loss keeps the
680
+ * last snapshot while the stream reconnects; a terminal failure keeps the
681
+ * owner's answers and turns a non-owner's answers false.
682
+ */
683
+ function createAccessStream(remote, state, logger) {
684
+ return new _deepseek_ai_dsh_api_gateway_client.RemoteSnapshotStream(remote.$stream({
685
+ name: "Remote access stream",
686
+ open: (signal) => remote.remoteAccess.follow(signal),
687
+ ended: (accepted) => accepted ? new _deepseek_ai_dsh_api_gateway_client.RemoteStreamCarrierError("Remote access stream ended without a terminal result") : /* @__PURE__ */ new Error("Remote access stream ended before its opening snapshot")
688
+ }), {
689
+ name: "Remote access stream",
690
+ isSnapshot: (frame) => frame.type === "baseline",
691
+ replace: (frame) => {
692
+ state.replace(frame);
693
+ },
694
+ update: (frame) => {
695
+ state.update(frame);
696
+ },
697
+ failed: (error) => {
698
+ if (state.owner) logger.error("access snapshot stream failed; owner access answers are unaffected", error);
699
+ else logger.error("access snapshot stream failed; reload the page to reconnect", error);
700
+ state.unavailable();
701
+ }
702
+ });
703
+ }
704
+ //#endregion
705
+ //#region lib/types/client/index.js
706
+ /**
707
+ * Client face: starts from the access snapshot the index injected, follows
708
+ * the calling Peer's snapshot stream, provides the `remoteAccess` Client
709
+ * service whose `source(kind, id, action)` answers for any declared-kind
710
+ * resource, and publishes `useRemoteAccess(action)` on the global resource.
711
+ * Domain UI adapters publish their own resource Hooks over `source()`. Every
712
+ * answer is a boolean: true for the owner, false without a snapshot or after
713
+ * a non-owner stream fails; the Host enforces every call regardless.
714
+ * @module @qlzt/dsh-api-remote-access/client
715
+ */
716
+ /** Required Client services: the Remote mount face and the Slot registry. */
717
+ const inject = ["remote", "slots"];
718
+ /**
719
+ * Mount the generated `remoteAccess` namespace and publish the access Hooks.
720
+ * @param ctx - Client root Context.
721
+ * @returns disposer that withdraws the namespace after the stream stops.
722
+ */
723
+ function apply(ctx) {
724
+ return mountAccess(ctx, TYPERT_REMOTE, parseInjectedBaseline(Reflect.get(globalThis, REMOTE_ACCESS_SNAPSHOT_KEY)));
725
+ }
726
+ //#endregion
727
+ exports.apply = apply;
728
+ exports.inject = inject;
729
+ return module.exports;
730
+ }
731
+ });
732
+
733
+ //# sourceMappingURL=client.js.map