@naeemo/capnp 0.2.0 → 0.4.0

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.
@@ -0,0 +1,870 @@
1
+ //#region src/rpc/four-tables.ts
2
+ /** Manages the question table for outbound calls */
3
+ var QuestionTable = class {
4
+ questions = /* @__PURE__ */ new Map();
5
+ nextId = 1;
6
+ /** Create a new question entry */
7
+ create() {
8
+ const id = this.allocateId();
9
+ let resolveCompletion;
10
+ let rejectCompletion;
11
+ const question = {
12
+ id,
13
+ isComplete: false,
14
+ finishSent: false,
15
+ completionPromise: new Promise((resolve, reject) => {
16
+ resolveCompletion = resolve;
17
+ rejectCompletion = reject;
18
+ }),
19
+ resolveCompletion,
20
+ rejectCompletion
21
+ };
22
+ this.questions.set(id, question);
23
+ return question;
24
+ }
25
+ /** Get a question by ID */
26
+ get(id) {
27
+ return this.questions.get(id);
28
+ }
29
+ /** Mark a question as complete */
30
+ complete(id, result) {
31
+ const question = this.questions.get(id);
32
+ if (question && !question.isComplete) {
33
+ question.isComplete = true;
34
+ question.resolveCompletion(result);
35
+ }
36
+ }
37
+ /** Mark a question as canceled */
38
+ cancel(id, error) {
39
+ const question = this.questions.get(id);
40
+ if (question && !question.isComplete) {
41
+ question.isComplete = true;
42
+ question.rejectCompletion(error);
43
+ }
44
+ }
45
+ /** Mark that Finish has been sent for a question */
46
+ markFinishSent(id) {
47
+ const question = this.questions.get(id);
48
+ if (question) question.finishSent = true;
49
+ }
50
+ /** Remove a question from the table (when both sides are done) */
51
+ remove(id) {
52
+ const question = this.questions.get(id);
53
+ if (question?.isComplete && question.finishSent) this.questions.delete(id);
54
+ }
55
+ /** Clean up all questions (e.g., on disconnect) */
56
+ clear() {
57
+ for (const question of this.questions.values()) if (!question.isComplete) question.rejectCompletion(/* @__PURE__ */ new Error("Connection closed"));
58
+ this.questions.clear();
59
+ this.nextId = 1;
60
+ }
61
+ allocateId() {
62
+ return this.nextId++;
63
+ }
64
+ };
65
+ /** Manages the answer table for inbound calls */
66
+ var AnswerTable = class {
67
+ answers = /* @__PURE__ */ new Map();
68
+ /** Create a new answer entry */
69
+ create(id) {
70
+ const answer = {
71
+ id,
72
+ isComplete: false,
73
+ returnSent: false,
74
+ finishReceived: false
75
+ };
76
+ this.answers.set(id, answer);
77
+ return answer;
78
+ }
79
+ /** Get an answer by ID */
80
+ get(id) {
81
+ return this.answers.get(id);
82
+ }
83
+ /** Mark that Return has been sent */
84
+ markReturnSent(id) {
85
+ const answer = this.answers.get(id);
86
+ if (answer) answer.returnSent = true;
87
+ }
88
+ /** Mark that Finish has been received */
89
+ markFinishReceived(id) {
90
+ const answer = this.answers.get(id);
91
+ if (answer) answer.finishReceived = true;
92
+ }
93
+ /** Remove an answer from the table (when both sides are done) */
94
+ remove(id) {
95
+ const answer = this.answers.get(id);
96
+ if (answer?.returnSent && answer.finishReceived) this.answers.delete(id);
97
+ }
98
+ /** Clean up all answers (e.g., on disconnect) */
99
+ clear() {
100
+ this.answers.clear();
101
+ }
102
+ };
103
+ /** Manages the import table for capabilities received from remote */
104
+ var ImportTable = class {
105
+ imports = /* @__PURE__ */ new Map();
106
+ /** Add a new import */
107
+ add(id, isPromise) {
108
+ const importEntry = {
109
+ id,
110
+ refCount: 1,
111
+ isPromise
112
+ };
113
+ this.imports.set(id, importEntry);
114
+ return importEntry;
115
+ }
116
+ /** Get an import by ID */
117
+ get(id) {
118
+ return this.imports.get(id);
119
+ }
120
+ /** Increment reference count */
121
+ addRef(id) {
122
+ const importEntry = this.imports.get(id);
123
+ if (importEntry) importEntry.refCount++;
124
+ }
125
+ /** Decrement reference count, returns true if refCount reached 0 */
126
+ release(id, count) {
127
+ const importEntry = this.imports.get(id);
128
+ if (importEntry) {
129
+ importEntry.refCount -= count;
130
+ if (importEntry.refCount <= 0) {
131
+ this.imports.delete(id);
132
+ return true;
133
+ }
134
+ }
135
+ return false;
136
+ }
137
+ /** Mark a promise as resolved */
138
+ markResolved(id) {
139
+ const importEntry = this.imports.get(id);
140
+ if (importEntry) importEntry.isPromise = false;
141
+ }
142
+ /** Clean up all imports (e.g., on disconnect) */
143
+ clear() {
144
+ this.imports.clear();
145
+ }
146
+ };
147
+ /** Manages the export table for capabilities sent to remote */
148
+ var ExportTable = class {
149
+ exports = /* @__PURE__ */ new Map();
150
+ nextId = 1;
151
+ /** Add a new export */
152
+ add(capability, isPromise) {
153
+ const id = this.allocateId();
154
+ const exportEntry = {
155
+ id,
156
+ refCount: 1,
157
+ isPromise,
158
+ capability
159
+ };
160
+ this.exports.set(id, exportEntry);
161
+ return exportEntry;
162
+ }
163
+ /** Get an export by ID */
164
+ get(id) {
165
+ return this.exports.get(id);
166
+ }
167
+ /** Increment reference count */
168
+ addRef(id) {
169
+ const exportEntry = this.exports.get(id);
170
+ if (exportEntry) exportEntry.refCount++;
171
+ }
172
+ /** Decrement reference count, returns true if refCount reached 0 */
173
+ release(id, count) {
174
+ const exportEntry = this.exports.get(id);
175
+ if (exportEntry) {
176
+ exportEntry.refCount -= count;
177
+ if (exportEntry.refCount <= 0) {
178
+ this.exports.delete(id);
179
+ return true;
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ /** Mark a promise as resolved */
185
+ markResolved(id) {
186
+ const exportEntry = this.exports.get(id);
187
+ if (exportEntry) exportEntry.isPromise = false;
188
+ }
189
+ /** Clean up all exports (e.g., on disconnect) */
190
+ clear() {
191
+ this.exports.clear();
192
+ this.nextId = 1;
193
+ }
194
+ allocateId() {
195
+ return this.nextId++;
196
+ }
197
+ };
198
+
199
+ //#endregion
200
+ //#region src/rpc/pipeline.ts
201
+ /**
202
+ * Tracks a chain of operations to apply to a promised answer.
203
+ * This forms the "transform" field in PromisedAnswer.
204
+ */
205
+ var PipelineOpTracker = class PipelineOpTracker {
206
+ ops = [];
207
+ /**
208
+ * Add a no-op (use the result as-is)
209
+ */
210
+ addNoop() {
211
+ this.ops.push({ type: "noop" });
212
+ }
213
+ /**
214
+ * Add a pointer field access operation
215
+ */
216
+ addGetPointerField(fieldIndex) {
217
+ this.ops.push({
218
+ type: "getPointerField",
219
+ fieldIndex
220
+ });
221
+ }
222
+ /**
223
+ * Get the current transform chain
224
+ */
225
+ getTransform() {
226
+ return [...this.ops];
227
+ }
228
+ /**
229
+ * Clone this tracker (for creating derived pipelines)
230
+ */
231
+ clone() {
232
+ const cloned = new PipelineOpTracker();
233
+ cloned.ops = [...this.ops];
234
+ return cloned;
235
+ }
236
+ };
237
+ /**
238
+ * Symbol used to identify pipeline clients internally
239
+ */
240
+ const PIPELINE_CLIENT_SYMBOL = Symbol("PipelineClient");
241
+ /**
242
+ * Creates a PipelineClient using JavaScript Proxy.
243
+ * The proxy intercepts property accesses to build up the transform chain.
244
+ */
245
+ function createPipelineClient(options) {
246
+ const { connection, questionId, opTracker = new PipelineOpTracker() } = options;
247
+ return {
248
+ [PIPELINE_CLIENT_SYMBOL]: true,
249
+ connection,
250
+ questionId,
251
+ opTracker,
252
+ call(interfaceId, methodId, params) {
253
+ return makePipelinedCall(connection, questionId, opTracker.getTransform(), interfaceId, methodId, params);
254
+ },
255
+ getPointerField(fieldIndex) {
256
+ const newTracker = opTracker.clone();
257
+ newTracker.addGetPointerField(fieldIndex);
258
+ return createPipelineClient({
259
+ connection,
260
+ questionId,
261
+ opTracker: newTracker
262
+ });
263
+ }
264
+ };
265
+ }
266
+ /**
267
+ * Check if a value is a PipelineClient
268
+ */
269
+ function isPipelineClient(value) {
270
+ return typeof value === "object" && value !== null && PIPELINE_CLIENT_SYMBOL in value;
271
+ }
272
+ /**
273
+ * Makes a call on a promised answer (pipeline call).
274
+ * This sends a Call message with target.type = 'promisedAnswer'.
275
+ */
276
+ async function makePipelinedCall(connection, questionId, transform, interfaceId, methodId, params) {
277
+ const newQuestionId = connection.createQuestion();
278
+ const call = {
279
+ questionId: newQuestionId,
280
+ target: {
281
+ type: "promisedAnswer",
282
+ promisedAnswer: {
283
+ questionId,
284
+ transform
285
+ }
286
+ },
287
+ interfaceId,
288
+ methodId,
289
+ allowThirdPartyTailCall: false,
290
+ noPromisePipelining: false,
291
+ onlyPromisePipeline: false,
292
+ params,
293
+ sendResultsTo: { type: "caller" }
294
+ };
295
+ await connection.sendCall(call);
296
+ return connection.waitForAnswer(newQuestionId);
297
+ }
298
+ /**
299
+ * Manages calls that were made on a pipeline client before the answer arrived.
300
+ * When the answer arrives, these calls are dispatched to the actual capability.
301
+ */
302
+ var QueuedCallManager = class {
303
+ queuedCalls = /* @__PURE__ */ new Map();
304
+ /**
305
+ * Queue a call for when the promise resolves
306
+ */
307
+ queueCall(questionId, call) {
308
+ const calls = this.queuedCalls.get(questionId) ?? [];
309
+ calls.push(call);
310
+ this.queuedCalls.set(questionId, calls);
311
+ }
312
+ /**
313
+ * Get and clear all queued calls for a question
314
+ */
315
+ dequeueCalls(questionId) {
316
+ const calls = this.queuedCalls.get(questionId) ?? [];
317
+ this.queuedCalls.delete(questionId);
318
+ return calls;
319
+ }
320
+ /**
321
+ * Check if there are queued calls for a question
322
+ */
323
+ hasQueuedCalls(questionId) {
324
+ return (this.queuedCalls.get(questionId)?.length ?? 0) > 0;
325
+ }
326
+ /**
327
+ * Clear all queued calls (e.g., on disconnect)
328
+ */
329
+ clear() {
330
+ for (const calls of this.queuedCalls.values()) for (const call of calls) call.reject(/* @__PURE__ */ new Error("Connection closed"));
331
+ this.queuedCalls.clear();
332
+ }
333
+ };
334
+ /**
335
+ * Tracks pending pipeline resolutions
336
+ */
337
+ var PipelineResolutionTracker = class {
338
+ pendingResolutions = /* @__PURE__ */ new Map();
339
+ /**
340
+ * Mark a question as resolved to a capability
341
+ */
342
+ resolveToCapability(questionId, importId) {
343
+ this.pendingResolutions.set(questionId, {
344
+ type: "capability",
345
+ importId
346
+ });
347
+ }
348
+ /**
349
+ * Mark a question as resolved to an exception
350
+ */
351
+ resolveToException(questionId, reason) {
352
+ this.pendingResolutions.set(questionId, {
353
+ type: "exception",
354
+ reason
355
+ });
356
+ }
357
+ /**
358
+ * Get the resolution for a question (if available)
359
+ */
360
+ getResolution(questionId) {
361
+ return this.pendingResolutions.get(questionId);
362
+ }
363
+ /**
364
+ * Check if a question has been resolved
365
+ */
366
+ isResolved(questionId) {
367
+ return this.pendingResolutions.has(questionId);
368
+ }
369
+ /**
370
+ * Remove a resolution entry
371
+ */
372
+ remove(questionId) {
373
+ this.pendingResolutions.delete(questionId);
374
+ }
375
+ /**
376
+ * Clear all resolutions
377
+ */
378
+ clear() {
379
+ this.pendingResolutions.clear();
380
+ }
381
+ };
382
+
383
+ //#endregion
384
+ //#region src/rpc/rpc-connection.ts
385
+ var RpcConnection = class {
386
+ transport;
387
+ options;
388
+ questions = new QuestionTable();
389
+ answers = new AnswerTable();
390
+ imports = new ImportTable();
391
+ exports = new ExportTable();
392
+ queuedCalls = new QueuedCallManager();
393
+ pipelineResolutions = new PipelineResolutionTracker();
394
+ running = false;
395
+ messageHandler;
396
+ level3Handlers;
397
+ level4Handlers;
398
+ constructor(transport, options = {}) {
399
+ this.transport = transport;
400
+ this.options = options;
401
+ this.level3Handlers = options.level3Handlers;
402
+ this.transport.onClose = (reason) => {
403
+ this.handleDisconnect(reason);
404
+ };
405
+ this.transport.onError = (error) => {
406
+ this.handleError(error);
407
+ };
408
+ this.level3Handlers = options.level3Handlers;
409
+ this.level4Handlers = options.level4Handlers;
410
+ }
411
+ /** Start processing messages */
412
+ async start() {
413
+ if (this.running) return;
414
+ this.running = true;
415
+ this.messageHandler = this.messageLoop();
416
+ }
417
+ /** Stop the connection */
418
+ async stop() {
419
+ this.running = false;
420
+ this.transport.close();
421
+ if (this.messageHandler) try {
422
+ await this.messageHandler;
423
+ } catch {}
424
+ }
425
+ /** Send a bootstrap request and return the bootstrap capability */
426
+ async bootstrap() {
427
+ const question = this.questions.create();
428
+ const bootstrapMsg = {
429
+ type: "bootstrap",
430
+ bootstrap: { questionId: question.id }
431
+ };
432
+ await this.transport.send(bootstrapMsg);
433
+ await question.completionPromise;
434
+ return {};
435
+ }
436
+ /** Make a call to a remote capability */
437
+ async call(target, interfaceId, methodId, params) {
438
+ if (isPipelineClient(target)) return target.call(interfaceId, methodId, params);
439
+ const question = this.questions.create();
440
+ const callMsg = {
441
+ type: "call",
442
+ call: {
443
+ questionId: question.id,
444
+ target: {
445
+ type: "importedCap",
446
+ importId: target
447
+ },
448
+ interfaceId,
449
+ methodId,
450
+ allowThirdPartyTailCall: false,
451
+ noPromisePipelining: false,
452
+ onlyPromisePipeline: false,
453
+ params,
454
+ sendResultsTo: { type: "caller" }
455
+ }
456
+ };
457
+ await this.transport.send(callMsg);
458
+ return question.completionPromise;
459
+ }
460
+ /**
461
+ * Make a call that returns a PipelineClient for promise pipelining.
462
+ * This allows making calls on the result before it arrives.
463
+ */
464
+ async callPipelined(target, interfaceId, methodId, params) {
465
+ const question = this.questions.create();
466
+ const callMsg = {
467
+ type: "call",
468
+ call: {
469
+ questionId: question.id,
470
+ target: {
471
+ type: "importedCap",
472
+ importId: target
473
+ },
474
+ interfaceId,
475
+ methodId,
476
+ allowThirdPartyTailCall: false,
477
+ noPromisePipelining: false,
478
+ onlyPromisePipeline: false,
479
+ params,
480
+ sendResultsTo: { type: "caller" }
481
+ }
482
+ };
483
+ await this.transport.send(callMsg);
484
+ return createPipelineClient({
485
+ connection: this,
486
+ questionId: question.id
487
+ });
488
+ }
489
+ /** Send a finish message to release a question */
490
+ async finish(questionId, releaseResultCaps = true) {
491
+ if (!this.questions.get(questionId)) return;
492
+ const finishMsg = {
493
+ type: "finish",
494
+ finish: {
495
+ questionId,
496
+ releaseResultCaps,
497
+ requireEarlyCancellationWorkaround: false
498
+ }
499
+ };
500
+ await this.transport.send(finishMsg);
501
+ this.questions.markFinishSent(questionId);
502
+ this.questions.remove(questionId);
503
+ }
504
+ /** Send a release message for an imported capability */
505
+ async release(importId, referenceCount = 1) {
506
+ const releaseMsg = {
507
+ type: "release",
508
+ release: {
509
+ id: importId,
510
+ referenceCount
511
+ }
512
+ };
513
+ await this.transport.send(releaseMsg);
514
+ }
515
+ /** Send a resolve message to indicate a promise has resolved */
516
+ async resolve(promiseId, cap) {
517
+ const resolveMsg = {
518
+ type: "resolve",
519
+ resolve: {
520
+ promiseId,
521
+ resolution: {
522
+ type: "cap",
523
+ cap
524
+ }
525
+ }
526
+ };
527
+ await this.transport.send(resolveMsg);
528
+ }
529
+ /** Send a resolve message indicating a promise was broken */
530
+ async resolveException(promiseId, reason) {
531
+ const resolveMsg = {
532
+ type: "resolve",
533
+ resolve: {
534
+ promiseId,
535
+ resolution: {
536
+ type: "exception",
537
+ exception: {
538
+ reason,
539
+ type: "failed"
540
+ }
541
+ }
542
+ }
543
+ };
544
+ await this.transport.send(resolveMsg);
545
+ }
546
+ /** Send a return message (internal use) */
547
+ async sendReturn(ret) {
548
+ const returnMsg = {
549
+ type: "return",
550
+ return: ret
551
+ };
552
+ await this.transport.send(returnMsg);
553
+ }
554
+ /** Send a disembargo message (internal use) */
555
+ async sendDisembargo(disembargo) {
556
+ const disembargoMsg = {
557
+ type: "disembargo",
558
+ disembargo
559
+ };
560
+ await this.transport.send(disembargoMsg);
561
+ }
562
+ /** Internal method: Create a new question (used by pipeline) */
563
+ createQuestion() {
564
+ return this.questions.create().id;
565
+ }
566
+ /** Internal method: Send a call message (used by pipeline) */
567
+ async sendCall(call) {
568
+ const callMsg = {
569
+ type: "call",
570
+ call
571
+ };
572
+ await this.transport.send(callMsg);
573
+ }
574
+ /** Internal method: Wait for an answer (used by pipeline) */
575
+ async waitForAnswer(questionId) {
576
+ const question = this.questions.get(questionId);
577
+ if (!question) throw new Error(`Question ${questionId} not found`);
578
+ return question.completionPromise;
579
+ }
580
+ /** Main message processing loop */
581
+ async messageLoop() {
582
+ while (this.running) try {
583
+ const message = await this.transport.receive();
584
+ if (message === null) break;
585
+ await this.handleMessage(message);
586
+ } catch (error) {
587
+ if (this.running) this.handleError(error);
588
+ }
589
+ }
590
+ /** Handle incoming messages */
591
+ async handleMessage(message) {
592
+ switch (message.type) {
593
+ case "bootstrap":
594
+ await this.handleBootstrap(message.bootstrap);
595
+ break;
596
+ case "call":
597
+ await this.handleCall(message.call);
598
+ break;
599
+ case "return":
600
+ await this.handleReturn(message.return);
601
+ break;
602
+ case "finish":
603
+ await this.handleFinish(message.finish);
604
+ break;
605
+ case "resolve":
606
+ await this.handleResolve(message.resolve);
607
+ break;
608
+ case "release":
609
+ await this.handleRelease(message.release);
610
+ break;
611
+ case "disembargo":
612
+ await this.handleDisembargo(message.disembargo);
613
+ break;
614
+ case "provide":
615
+ await this.handleProvide(message.provide);
616
+ break;
617
+ case "accept":
618
+ await this.handleAccept(message.accept);
619
+ break;
620
+ case "join":
621
+ await this.handleJoin(message.join);
622
+ break;
623
+ case "abort":
624
+ this.handleAbort(message.exception.reason);
625
+ break;
626
+ case "unimplemented": break;
627
+ default: await this.sendUnimplemented(message);
628
+ }
629
+ }
630
+ /** Handle bootstrap request */
631
+ async handleBootstrap(bootstrap) {
632
+ this.answers.create(bootstrap.questionId);
633
+ const returnMsg = {
634
+ type: "return",
635
+ return: {
636
+ answerId: bootstrap.questionId,
637
+ releaseParamCaps: true,
638
+ noFinishNeeded: false,
639
+ result: {
640
+ type: "results",
641
+ payload: {
642
+ content: new Uint8Array(0),
643
+ capTable: []
644
+ }
645
+ }
646
+ }
647
+ };
648
+ await this.transport.send(returnMsg);
649
+ this.answers.markReturnSent(bootstrap.questionId);
650
+ }
651
+ /** Handle incoming call */
652
+ async handleCall(call) {
653
+ this.answers.create(call.questionId);
654
+ const returnMsg = {
655
+ type: "return",
656
+ return: {
657
+ answerId: call.questionId,
658
+ releaseParamCaps: true,
659
+ noFinishNeeded: false,
660
+ result: {
661
+ type: "exception",
662
+ exception: {
663
+ reason: "Method not implemented",
664
+ type: "unimplemented"
665
+ }
666
+ }
667
+ }
668
+ };
669
+ await this.transport.send(returnMsg);
670
+ this.answers.markReturnSent(call.questionId);
671
+ }
672
+ /** Handle return message */
673
+ async handleReturn(ret) {
674
+ if (!this.questions.get(ret.answerId)) return;
675
+ if (ret.result.type === "results") {
676
+ const capTable = ret.result.payload.capTable;
677
+ if (capTable.length > 0) {
678
+ const cap = capTable[0];
679
+ if (cap.type === "receiverHosted") this.pipelineResolutions.resolveToCapability(ret.answerId, cap.importId);
680
+ else if (cap.type === "thirdPartyHosted") await this.handleThirdPartyCapability(ret.answerId, cap.thirdPartyCapId);
681
+ }
682
+ } else if (ret.result.type === "exception") this.pipelineResolutions.resolveToException(ret.answerId, ret.result.exception.reason);
683
+ switch (ret.result.type) {
684
+ case "results":
685
+ this.questions.complete(ret.answerId, ret.result.payload);
686
+ break;
687
+ case "exception":
688
+ this.questions.cancel(ret.answerId, new Error(ret.result.exception.reason));
689
+ break;
690
+ case "canceled":
691
+ this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Call canceled"));
692
+ break;
693
+ case "acceptFromThirdParty":
694
+ await this.handleAcceptFromThirdParty(ret.answerId, ret.result.thirdPartyCapId);
695
+ break;
696
+ default: this.questions.cancel(ret.answerId, /* @__PURE__ */ new Error("Unknown return type"));
697
+ }
698
+ }
699
+ /** Handle finish message */
700
+ async handleFinish(finish) {
701
+ this.answers.markFinishReceived(finish.questionId);
702
+ this.answers.remove(finish.questionId);
703
+ }
704
+ /** Handle resolve message (Level 1) */
705
+ async handleResolve(resolve) {
706
+ const { promiseId, resolution } = resolve;
707
+ switch (resolution.type) {
708
+ case "cap":
709
+ this.imports.markResolved(promiseId);
710
+ break;
711
+ case "exception":
712
+ console.warn(`Promise ${promiseId} broken: ${resolution.exception.reason}`);
713
+ break;
714
+ }
715
+ }
716
+ /** Handle release message (Level 1) */
717
+ async handleRelease(release) {
718
+ const { id, referenceCount } = release;
719
+ if (this.exports.release(id, referenceCount)) console.log(`Export ${id} fully released`);
720
+ }
721
+ /** Handle disembargo message (Level 1) */
722
+ async handleDisembargo(disembargo) {
723
+ const { target, context } = disembargo;
724
+ if (this.level3Handlers) {
725
+ await this.level3Handlers.handleDisembargo(disembargo);
726
+ return;
727
+ }
728
+ if (context.type === "senderLoopback") {
729
+ const echoMsg = {
730
+ type: "disembargo",
731
+ disembargo: {
732
+ target,
733
+ context: {
734
+ type: "receiverLoopback",
735
+ embargoId: context.embargoId
736
+ }
737
+ }
738
+ };
739
+ await this.transport.send(echoMsg);
740
+ }
741
+ }
742
+ /** Handle provide message (Level 3) */
743
+ async handleProvide(provide) {
744
+ if (this.level3Handlers) await this.level3Handlers.handleProvide(provide);
745
+ else await this.sendReturnException(provide.questionId, "Level 3 RPC (Provide) not implemented");
746
+ }
747
+ /** Handle accept message (Level 3) */
748
+ async handleAccept(accept) {
749
+ if (this.level3Handlers) await this.level3Handlers.handleAccept(accept);
750
+ else await this.sendReturnException(accept.questionId, "Level 3 RPC (Accept) not implemented");
751
+ }
752
+ /** Handle third-party capability in return results (Level 3) */
753
+ async handleThirdPartyCapability(_questionId, thirdPartyCapId) {
754
+ if (this.level3Handlers) {
755
+ if (await this.level3Handlers.handleThirdPartyCapability(thirdPartyCapId) !== void 0) {}
756
+ }
757
+ }
758
+ /** Handle acceptFromThirdParty return type (Level 3) */
759
+ async handleAcceptFromThirdParty(questionId, thirdPartyCapId) {
760
+ if (this.level3Handlers) {
761
+ const importId = await this.level3Handlers.handleThirdPartyCapability(thirdPartyCapId);
762
+ if (importId !== void 0) this.questions.complete(questionId, { importId });
763
+ else this.questions.cancel(questionId, /* @__PURE__ */ new Error("Failed to resolve third-party capability"));
764
+ } else this.questions.cancel(questionId, /* @__PURE__ */ new Error("Level 3 RPC not enabled"));
765
+ }
766
+ /** Handle abort message */
767
+ handleAbort(_reason) {
768
+ this.running = false;
769
+ this.questions.clear();
770
+ this.answers.clear();
771
+ this.imports.clear();
772
+ this.exports.clear();
773
+ this.queuedCalls.clear();
774
+ this.pipelineResolutions.clear();
775
+ }
776
+ /** Handle disconnect */
777
+ handleDisconnect(_reason) {
778
+ this.running = false;
779
+ this.questions.clear();
780
+ this.answers.clear();
781
+ this.imports.clear();
782
+ this.exports.clear();
783
+ this.queuedCalls.clear();
784
+ this.pipelineResolutions.clear();
785
+ }
786
+ /** Handle error */
787
+ handleError(error) {
788
+ console.error("RPC error:", error);
789
+ }
790
+ /** Send unimplemented response */
791
+ async sendUnimplemented(originalMessage) {
792
+ const msg = {
793
+ type: "unimplemented",
794
+ message: originalMessage
795
+ };
796
+ await this.transport.send(msg);
797
+ }
798
+ /** Send return exception (helper) */
799
+ async sendReturnException(questionId, reason) {
800
+ const returnMsg = {
801
+ type: "return",
802
+ return: {
803
+ answerId: questionId,
804
+ releaseParamCaps: true,
805
+ noFinishNeeded: false,
806
+ result: {
807
+ type: "exception",
808
+ exception: {
809
+ reason,
810
+ type: "unimplemented"
811
+ }
812
+ }
813
+ }
814
+ };
815
+ await this.transport.send(returnMsg);
816
+ }
817
+ /**
818
+ * Set the Level 4 handlers for this connection.
819
+ * This enables reference equality verification support.
820
+ */
821
+ setLevel4Handlers(handlers) {
822
+ this.level4Handlers = handlers;
823
+ }
824
+ /**
825
+ * Send a Join message to verify that two capabilities point to the same object.
826
+ * Requires Level 4 handlers to be set.
827
+ */
828
+ async join(_target1, _target2) {
829
+ if (!this.level4Handlers) throw new Error("Level 4 handlers not set");
830
+ }
831
+ /** Handle join message (Level 4) */
832
+ async handleJoin(join) {
833
+ if (this.level4Handlers) await this.level4Handlers.handleJoin(join);
834
+ else await this.sendReturnException(join.questionId, "Level 4 RPC (Join) not implemented");
835
+ }
836
+ /** Import a capability from the remote peer */
837
+ importCapability(importId, isPromise = false) {
838
+ this.imports.add(importId, isPromise);
839
+ }
840
+ /** Export a capability to the remote peer */
841
+ exportCapability(capability, isPromise = false) {
842
+ return this.exports.add(capability, isPromise).id;
843
+ }
844
+ /** Get an imported capability */
845
+ getImport(importId) {
846
+ return this.imports.get(importId);
847
+ }
848
+ /** Get an exported capability */
849
+ getExport(exportId) {
850
+ return this.exports.get(exportId);
851
+ }
852
+ /**
853
+ * Set the Level 3 handlers for this connection.
854
+ * This enables three-way introduction support.
855
+ */
856
+ setLevel3Handlers(handlers) {
857
+ this.level3Handlers = handlers;
858
+ }
859
+ /**
860
+ * Send a Provide message to offer a capability to a third party.
861
+ * Requires Level 3 handlers to be set.
862
+ */
863
+ async provideToThirdParty(_target, _recipient) {
864
+ if (!this.level3Handlers) throw new Error("Level 3 handlers not set");
865
+ }
866
+ };
867
+
868
+ //#endregion
869
+ export { QueuedCallManager as a, AnswerTable as c, QuestionTable as d, PipelineResolutionTracker as i, ExportTable as l, PIPELINE_CLIENT_SYMBOL as n, createPipelineClient as o, PipelineOpTracker as r, isPipelineClient as s, RpcConnection as t, ImportTable as u };
870
+ //# sourceMappingURL=rpc-connection-Dz3rYT1P.js.map