@vastblast/capnweb 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3226 @@
1
+ // src/symbols.ts
2
+ var WORKERS_MODULE_SYMBOL = /* @__PURE__ */ Symbol("workers-module");
3
+
4
+ // src/core.ts
5
+ if (!Symbol.dispose) {
6
+ Symbol.dispose = /* @__PURE__ */ Symbol.for("dispose");
7
+ }
8
+ if (!Symbol.asyncDispose) {
9
+ Symbol.asyncDispose = /* @__PURE__ */ Symbol.for("asyncDispose");
10
+ }
11
+ if (!Promise.withResolvers) {
12
+ Promise.withResolvers = function() {
13
+ let resolve;
14
+ let reject;
15
+ const promise = new Promise((res, rej) => {
16
+ resolve = res;
17
+ reject = rej;
18
+ });
19
+ return { promise, resolve, reject };
20
+ };
21
+ }
22
+ var workersModule = globalThis[WORKERS_MODULE_SYMBOL];
23
+ var RpcTarget = workersModule ? workersModule.RpcTarget : class {
24
+ };
25
+ var AsyncFunction = (async function() {
26
+ }).constructor;
27
+ function typeForRpc(value) {
28
+ switch (typeof value) {
29
+ case "boolean":
30
+ case "number":
31
+ case "string":
32
+ return "primitive";
33
+ case "undefined":
34
+ return "undefined";
35
+ case "object":
36
+ case "function":
37
+ break;
38
+ case "bigint":
39
+ return "bigint";
40
+ default:
41
+ return "unsupported";
42
+ }
43
+ if (value === null) {
44
+ return "primitive";
45
+ }
46
+ let prototype = Object.getPrototypeOf(value);
47
+ switch (prototype) {
48
+ case Object.prototype:
49
+ return "object";
50
+ case Function.prototype:
51
+ case AsyncFunction.prototype:
52
+ return "function";
53
+ case Array.prototype:
54
+ return "array";
55
+ case Date.prototype:
56
+ return "date";
57
+ case Uint8Array.prototype:
58
+ return "bytes";
59
+ case WritableStream.prototype:
60
+ return "writable";
61
+ case ReadableStream.prototype:
62
+ return "readable";
63
+ // TODO: All other structured clone types.
64
+ case RpcStub.prototype:
65
+ return "stub";
66
+ case RpcPromise.prototype:
67
+ return "rpc-promise";
68
+ // TODO: Promise<T> or thenable
69
+ default:
70
+ if (workersModule) {
71
+ if (prototype == workersModule.RpcStub.prototype || value instanceof workersModule.ServiceStub) {
72
+ return "rpc-target";
73
+ } else if (prototype == workersModule.RpcPromise.prototype || prototype == workersModule.RpcProperty.prototype) {
74
+ return "rpc-thenable";
75
+ }
76
+ }
77
+ if (value instanceof RpcTarget) {
78
+ return "rpc-target";
79
+ }
80
+ if (value instanceof Error) {
81
+ return "error";
82
+ }
83
+ return "unsupported";
84
+ }
85
+ }
86
+ function mapNotLoaded() {
87
+ throw new Error("RPC map() implementation was not loaded.");
88
+ }
89
+ var mapImpl = { applyMap: mapNotLoaded, sendMap: mapNotLoaded };
90
+ function streamNotLoaded() {
91
+ throw new Error("Stream implementation was not loaded.");
92
+ }
93
+ var streamImpl = {
94
+ createWritableStreamHook: streamNotLoaded,
95
+ createWritableStreamFromHook: streamNotLoaded,
96
+ createReadableStreamHook: streamNotLoaded
97
+ };
98
+ var StubHook = class {
99
+ // Like call(), but designed for streaming calls (e.g. WritableStream writes). Returns:
100
+ // - promise: A Promise<void> for the completion of the call.
101
+ // - size: If the call was remote, the byte size of the serialized message. For local calls,
102
+ // undefined is returned, indicating the caller should await the promise to serialize writes
103
+ // (no overlapping).
104
+ stream(path, args) {
105
+ let hook = this.call(path, args);
106
+ let pulled = hook.pull();
107
+ let promise;
108
+ if (pulled instanceof Promise) {
109
+ promise = pulled.then((p) => {
110
+ p.dispose();
111
+ });
112
+ } else {
113
+ pulled.dispose();
114
+ promise = Promise.resolve();
115
+ }
116
+ return { promise };
117
+ }
118
+ };
119
+ var ErrorStubHook = class extends StubHook {
120
+ constructor(error) {
121
+ super();
122
+ this.error = error;
123
+ }
124
+ call(path, args) {
125
+ return this;
126
+ }
127
+ map(path, captures, instructions) {
128
+ return this;
129
+ }
130
+ get(path) {
131
+ return this;
132
+ }
133
+ dup() {
134
+ return this;
135
+ }
136
+ pull() {
137
+ return Promise.reject(this.error);
138
+ }
139
+ ignoreUnhandledRejections() {
140
+ }
141
+ dispose() {
142
+ }
143
+ onBroken(callback) {
144
+ try {
145
+ callback(this.error);
146
+ } catch (err) {
147
+ Promise.resolve(err);
148
+ }
149
+ }
150
+ };
151
+ var DISPOSED_HOOK = new ErrorStubHook(
152
+ new Error("Attempted to use RPC stub after it has been disposed.")
153
+ );
154
+ var doCall = (hook, path, params) => {
155
+ return hook.call(path, params);
156
+ };
157
+ function withCallInterceptor(interceptor, callback) {
158
+ let oldValue = doCall;
159
+ doCall = interceptor;
160
+ try {
161
+ return callback();
162
+ } finally {
163
+ doCall = oldValue;
164
+ }
165
+ }
166
+ var RAW_STUB = /* @__PURE__ */ Symbol("realStub");
167
+ var PROXY_HANDLERS = {
168
+ apply(target, thisArg, argumentsList) {
169
+ let stub = target.raw;
170
+ return new RpcPromise(doCall(
171
+ stub.hook,
172
+ stub.pathIfPromise || [],
173
+ RpcPayload.fromAppParams(argumentsList)
174
+ ), []);
175
+ },
176
+ get(target, prop, receiver) {
177
+ let stub = target.raw;
178
+ if (prop === RAW_STUB) {
179
+ return stub;
180
+ } else if (prop in RpcPromise.prototype) {
181
+ return stub[prop];
182
+ } else if (typeof prop === "string") {
183
+ return new RpcPromise(
184
+ stub.hook,
185
+ stub.pathIfPromise ? [...stub.pathIfPromise, prop] : [prop]
186
+ );
187
+ } else if (prop === Symbol.dispose && (!stub.pathIfPromise || stub.pathIfPromise.length == 0)) {
188
+ return () => {
189
+ stub.hook.dispose();
190
+ stub.hook = DISPOSED_HOOK;
191
+ };
192
+ } else {
193
+ return void 0;
194
+ }
195
+ },
196
+ has(target, prop) {
197
+ let stub = target.raw;
198
+ if (prop === RAW_STUB) {
199
+ return true;
200
+ } else if (prop in RpcPromise.prototype) {
201
+ return prop in stub;
202
+ } else if (typeof prop === "string") {
203
+ return true;
204
+ } else if (prop === Symbol.dispose && (!stub.pathIfPromise || stub.pathIfPromise.length == 0)) {
205
+ return true;
206
+ } else {
207
+ return false;
208
+ }
209
+ },
210
+ construct(target, args) {
211
+ throw new Error("An RPC stub cannot be used as a constructor.");
212
+ },
213
+ defineProperty(target, property, attributes) {
214
+ throw new Error("Can't define properties on RPC stubs.");
215
+ },
216
+ deleteProperty(target, p) {
217
+ throw new Error("Can't delete properties on RPC stubs.");
218
+ },
219
+ getOwnPropertyDescriptor(target, p) {
220
+ return void 0;
221
+ },
222
+ getPrototypeOf(target) {
223
+ return Object.getPrototypeOf(target.raw);
224
+ },
225
+ isExtensible(target) {
226
+ return false;
227
+ },
228
+ ownKeys(target) {
229
+ return [];
230
+ },
231
+ preventExtensions(target) {
232
+ return true;
233
+ },
234
+ set(target, p, newValue, receiver) {
235
+ throw new Error("Can't assign properties on RPC stubs.");
236
+ },
237
+ setPrototypeOf(target, v) {
238
+ throw new Error("Can't override prototype of RPC stubs.");
239
+ }
240
+ };
241
+ var RpcStub = class _RpcStub extends RpcTarget {
242
+ // Although `hook` and `path` are declared `public` here, they are effectively hidden by the
243
+ // proxy.
244
+ constructor(hook, pathIfPromise) {
245
+ super();
246
+ if (!(hook instanceof StubHook)) {
247
+ let value = hook;
248
+ if (value instanceof RpcTarget || value instanceof Function) {
249
+ hook = TargetStubHook.create(value, void 0);
250
+ } else {
251
+ hook = new PayloadStubHook(RpcPayload.fromAppReturn(value));
252
+ }
253
+ if (pathIfPromise) {
254
+ throw new TypeError("RpcStub constructor expected one argument, received two.");
255
+ }
256
+ }
257
+ this.hook = hook;
258
+ this.pathIfPromise = pathIfPromise;
259
+ let func = () => {
260
+ };
261
+ func.raw = this;
262
+ return new Proxy(func, PROXY_HANDLERS);
263
+ }
264
+ hook;
265
+ pathIfPromise;
266
+ dup() {
267
+ let target = this[RAW_STUB];
268
+ if (target.pathIfPromise) {
269
+ return new _RpcStub(target.hook.get(target.pathIfPromise));
270
+ } else {
271
+ return new _RpcStub(target.hook.dup());
272
+ }
273
+ }
274
+ onRpcBroken(callback) {
275
+ this[RAW_STUB].hook.onBroken(callback);
276
+ }
277
+ map(func) {
278
+ let { hook, pathIfPromise } = this[RAW_STUB];
279
+ return mapImpl.sendMap(hook, pathIfPromise || [], func);
280
+ }
281
+ toString() {
282
+ return "[object RpcStub]";
283
+ }
284
+ };
285
+ var RpcPromise = class extends RpcStub {
286
+ // TODO: Support passing target value or promise to constructor.
287
+ constructor(hook, pathIfPromise) {
288
+ super(hook, pathIfPromise);
289
+ }
290
+ then(onfulfilled, onrejected) {
291
+ return pullPromise(this).then(...arguments);
292
+ }
293
+ catch(onrejected) {
294
+ return pullPromise(this).catch(...arguments);
295
+ }
296
+ finally(onfinally) {
297
+ return pullPromise(this).finally(...arguments);
298
+ }
299
+ toString() {
300
+ return "[object RpcPromise]";
301
+ }
302
+ };
303
+ function unwrapStubTakingOwnership(stub) {
304
+ let { hook, pathIfPromise } = stub[RAW_STUB];
305
+ if (pathIfPromise && pathIfPromise.length > 0) {
306
+ return hook.get(pathIfPromise);
307
+ } else {
308
+ return hook;
309
+ }
310
+ }
311
+ function unwrapStubAndDup(stub) {
312
+ let { hook, pathIfPromise } = stub[RAW_STUB];
313
+ if (pathIfPromise) {
314
+ return hook.get(pathIfPromise);
315
+ } else {
316
+ return hook.dup();
317
+ }
318
+ }
319
+ function unwrapStubNoProperties(stub) {
320
+ let { hook, pathIfPromise } = stub[RAW_STUB];
321
+ if (pathIfPromise && pathIfPromise.length > 0) {
322
+ return void 0;
323
+ }
324
+ return hook;
325
+ }
326
+ function unwrapStubOrParent(stub) {
327
+ return stub[RAW_STUB].hook;
328
+ }
329
+ function unwrapStubAndPath(stub) {
330
+ return stub[RAW_STUB];
331
+ }
332
+ async function pullPromise(promise) {
333
+ let { hook, pathIfPromise } = promise[RAW_STUB];
334
+ if (pathIfPromise.length > 0) {
335
+ hook = hook.get(pathIfPromise);
336
+ }
337
+ let payload = await hook.pull();
338
+ return payload.deliverResolve();
339
+ }
340
+ var RpcPayload = class _RpcPayload {
341
+ // Private constructor; use factory functions above to construct.
342
+ constructor(value, source, hooks, promises) {
343
+ this.value = value;
344
+ this.source = source;
345
+ this.hooks = hooks;
346
+ this.promises = promises;
347
+ }
348
+ // Create a payload from a value passed as params to an RPC from the app.
349
+ //
350
+ // The payload does NOT take ownership of any stubs in `value`, and but promises not to modify
351
+ // `value`. If the payload is delivered locally, `value` will be deep-copied first, so as not
352
+ // to have the sender and recipient end up sharing the same mutable object. `value` will not be
353
+ // touched again after the call returns synchronously (returns a promise) -- by that point,
354
+ // the value has either been copied or serialized to the wire.
355
+ static fromAppParams(value) {
356
+ return new _RpcPayload(value, "params");
357
+ }
358
+ // Create a payload from a value return from an RPC implementation by the app.
359
+ //
360
+ // Unlike fromAppParams(), in this case the payload takes ownership of all stubs in `value`, and
361
+ // may hold onto `value` for an arbitrarily long time (e.g. to serve pipelined requests). It
362
+ // will still avoid modifying `value` and will make a deep copy if it is delivered locally.
363
+ static fromAppReturn(value) {
364
+ return new _RpcPayload(value, "return");
365
+ }
366
+ // Combine an array of payloads into a single payload whose value is an array. Ownership of all
367
+ // stubs is transferred from the inputs to the outputs, hence if the output is disposed, the
368
+ // inputs should not be. (In case of exception, nothing is disposed, though.)
369
+ static fromArray(array) {
370
+ let hooks = [];
371
+ let promises = [];
372
+ let resultArray = [];
373
+ for (let payload of array) {
374
+ payload.ensureDeepCopied();
375
+ for (let hook of payload.hooks) {
376
+ hooks.push(hook);
377
+ }
378
+ for (let promise of payload.promises) {
379
+ if (promise.parent === payload) {
380
+ promise = {
381
+ parent: resultArray,
382
+ property: resultArray.length,
383
+ promise: promise.promise
384
+ };
385
+ }
386
+ promises.push(promise);
387
+ }
388
+ resultArray.push(payload.value);
389
+ }
390
+ return new _RpcPayload(resultArray, "owned", hooks, promises);
391
+ }
392
+ // Create a payload from a value parsed off the wire using Evaluator.evaluate().
393
+ //
394
+ // A payload is constructed with a null value and the given hooks and promises arrays. The value
395
+ // is expected to be filled in by the evaluator, and the hooks and promises arrays are expected
396
+ // to be extended with stubs found during parsing. (This weird usage model is necessary so that
397
+ // if the root value turns out to be a promise, its `parent` in `promises` can be the payload
398
+ // object itself.)
399
+ //
400
+ // When done, the payload takes ownership of the final value and all the stubs within. It may
401
+ // modify the value in preparation for delivery, and may deliver the value directly to the app
402
+ // without copying.
403
+ static forEvaluate(hooks, promises) {
404
+ return new _RpcPayload(null, "owned", hooks, promises);
405
+ }
406
+ // Deep-copy the given value, including dup()ing all stubs.
407
+ //
408
+ // If `value` is a function, it should be bound to `oldParent` as its `this`.
409
+ //
410
+ // If deep-copying from a branch of some other RpcPayload, it must be provided, to make sure
411
+ // RpcTargets found within don't get duplicate stubs.
412
+ static deepCopyFrom(value, oldParent, owner) {
413
+ let result = new _RpcPayload(null, "owned", [], []);
414
+ result.value = result.deepCopy(
415
+ value,
416
+ oldParent,
417
+ "value",
418
+ result,
419
+ /*dupStubs=*/
420
+ true,
421
+ owner
422
+ );
423
+ return result;
424
+ }
425
+ // For `source === "return"` payloads only, this tracks any StubHooks created around RpcTargets
426
+ // or WritableStreams found in the payload at the time that it is serialized (or deep-copied) for
427
+ // return, so that we can make sure they are not disposed before the pipeline ends.
428
+ //
429
+ // This is initialized on first use.
430
+ rpcTargets;
431
+ // Get the StubHook representing the given RpcTarget found inside this payload.
432
+ getHookForRpcTarget(target, parent, dupStubs = true) {
433
+ if (this.source === "params") {
434
+ if (dupStubs) {
435
+ let dupable = target;
436
+ if (typeof dupable.dup === "function") {
437
+ target = dupable.dup();
438
+ }
439
+ }
440
+ return TargetStubHook.create(target, parent);
441
+ } else if (this.source === "return") {
442
+ let hook = this.rpcTargets?.get(target);
443
+ if (hook) {
444
+ if (dupStubs) {
445
+ return hook.dup();
446
+ } else {
447
+ this.rpcTargets?.delete(target);
448
+ return hook;
449
+ }
450
+ } else {
451
+ hook = TargetStubHook.create(target, parent);
452
+ if (dupStubs) {
453
+ if (!this.rpcTargets) {
454
+ this.rpcTargets = /* @__PURE__ */ new Map();
455
+ }
456
+ this.rpcTargets.set(target, hook);
457
+ return hook.dup();
458
+ } else {
459
+ return hook;
460
+ }
461
+ }
462
+ } else {
463
+ throw new Error("owned payload shouldn't contain raw RpcTargets");
464
+ }
465
+ }
466
+ // Get the StubHook representing the given WritableStream found inside this payload.
467
+ getHookForWritableStream(stream, parent, dupStubs = true) {
468
+ if (this.source === "params") {
469
+ return streamImpl.createWritableStreamHook(stream);
470
+ } else if (this.source === "return") {
471
+ let hook = this.rpcTargets?.get(stream);
472
+ if (hook) {
473
+ if (dupStubs) {
474
+ return hook.dup();
475
+ } else {
476
+ this.rpcTargets?.delete(stream);
477
+ return hook;
478
+ }
479
+ } else {
480
+ hook = streamImpl.createWritableStreamHook(stream);
481
+ if (dupStubs) {
482
+ if (!this.rpcTargets) {
483
+ this.rpcTargets = /* @__PURE__ */ new Map();
484
+ }
485
+ this.rpcTargets.set(stream, hook);
486
+ return hook.dup();
487
+ } else {
488
+ return hook;
489
+ }
490
+ }
491
+ } else {
492
+ throw new Error("owned payload shouldn't contain raw WritableStreams");
493
+ }
494
+ }
495
+ // Get the StubHook representing the given ReadableStream found inside this payload.
496
+ getHookForReadableStream(stream, parent, dupStubs = true) {
497
+ if (this.source === "params") {
498
+ return streamImpl.createReadableStreamHook(stream);
499
+ } else if (this.source === "return") {
500
+ let hook = this.rpcTargets?.get(stream);
501
+ if (hook) {
502
+ if (dupStubs) {
503
+ return hook.dup();
504
+ } else {
505
+ this.rpcTargets?.delete(stream);
506
+ return hook;
507
+ }
508
+ } else {
509
+ hook = streamImpl.createReadableStreamHook(stream);
510
+ if (dupStubs) {
511
+ if (!this.rpcTargets) {
512
+ this.rpcTargets = /* @__PURE__ */ new Map();
513
+ }
514
+ this.rpcTargets.set(stream, hook);
515
+ return hook.dup();
516
+ } else {
517
+ return hook;
518
+ }
519
+ }
520
+ } else {
521
+ throw new Error("owned payload shouldn't contain raw ReadableStreams");
522
+ }
523
+ }
524
+ deepCopy(value, oldParent, property, parent, dupStubs, owner) {
525
+ let kind = typeForRpc(value);
526
+ switch (kind) {
527
+ case "unsupported":
528
+ return value;
529
+ case "primitive":
530
+ case "bigint":
531
+ case "date":
532
+ case "bytes":
533
+ case "error":
534
+ case "undefined":
535
+ return value;
536
+ case "array": {
537
+ let array = value;
538
+ let len = array.length;
539
+ let result = new Array(len);
540
+ for (let i = 0; i < len; i++) {
541
+ result[i] = this.deepCopy(array[i], array, i, result, dupStubs, owner);
542
+ }
543
+ return result;
544
+ }
545
+ case "object": {
546
+ let result = {};
547
+ let object = value;
548
+ for (let i in object) {
549
+ result[i] = this.deepCopy(object[i], object, i, result, dupStubs, owner);
550
+ }
551
+ return result;
552
+ }
553
+ case "stub":
554
+ case "rpc-promise": {
555
+ let stub = value;
556
+ let hook;
557
+ if (dupStubs) {
558
+ hook = unwrapStubAndDup(stub);
559
+ } else {
560
+ hook = unwrapStubTakingOwnership(stub);
561
+ }
562
+ if (stub instanceof RpcPromise) {
563
+ let promise = new RpcPromise(hook, []);
564
+ this.promises.push({ parent, property, promise });
565
+ return promise;
566
+ } else {
567
+ this.hooks.push(hook);
568
+ return new RpcStub(hook);
569
+ }
570
+ }
571
+ case "function":
572
+ case "rpc-target": {
573
+ let target = value;
574
+ let hook;
575
+ if (owner) {
576
+ hook = owner.getHookForRpcTarget(target, oldParent, dupStubs);
577
+ } else {
578
+ hook = TargetStubHook.create(target, oldParent);
579
+ }
580
+ this.hooks.push(hook);
581
+ return new RpcStub(hook);
582
+ }
583
+ case "rpc-thenable": {
584
+ let target = value;
585
+ let promise;
586
+ if (owner) {
587
+ promise = new RpcPromise(owner.getHookForRpcTarget(target, oldParent, dupStubs), []);
588
+ } else {
589
+ promise = new RpcPromise(TargetStubHook.create(target, oldParent), []);
590
+ }
591
+ this.promises.push({ parent, property, promise });
592
+ return promise;
593
+ }
594
+ case "writable": {
595
+ let stream = value;
596
+ let hook;
597
+ if (owner) {
598
+ hook = owner.getHookForWritableStream(stream, oldParent, dupStubs);
599
+ } else {
600
+ hook = streamImpl.createWritableStreamHook(stream);
601
+ }
602
+ this.hooks.push(hook);
603
+ return stream;
604
+ }
605
+ case "readable": {
606
+ let stream = value;
607
+ let hook;
608
+ if (owner) {
609
+ hook = owner.getHookForReadableStream(stream, oldParent, dupStubs);
610
+ } else {
611
+ hook = streamImpl.createReadableStreamHook(stream);
612
+ }
613
+ this.hooks.push(hook);
614
+ return stream;
615
+ }
616
+ default:
617
+ throw new Error("unreachable");
618
+ }
619
+ }
620
+ // Ensures that if the value originally came from an unowned source, we have replaced it with a
621
+ // deep copy.
622
+ ensureDeepCopied() {
623
+ if (this.source !== "owned") {
624
+ let dupStubs = this.source === "params";
625
+ this.hooks = [];
626
+ this.promises = [];
627
+ try {
628
+ this.value = this.deepCopy(this.value, void 0, "value", this, dupStubs, this);
629
+ } catch (err) {
630
+ this.hooks = void 0;
631
+ this.promises = void 0;
632
+ throw err;
633
+ }
634
+ this.source = "owned";
635
+ if (this.rpcTargets && this.rpcTargets.size > 0) {
636
+ throw new Error("Not all rpcTargets were accounted for in deep-copy?");
637
+ }
638
+ this.rpcTargets = void 0;
639
+ }
640
+ }
641
+ // Resolve all promises in this payload and then assign the final value into `parent[property]`.
642
+ deliverTo(parent, property, promises) {
643
+ this.ensureDeepCopied();
644
+ if (this.value instanceof RpcPromise) {
645
+ _RpcPayload.deliverRpcPromiseTo(this.value, parent, property, promises);
646
+ } else {
647
+ parent[property] = this.value;
648
+ for (let record of this.promises) {
649
+ _RpcPayload.deliverRpcPromiseTo(record.promise, record.parent, record.property, promises);
650
+ }
651
+ }
652
+ }
653
+ static deliverRpcPromiseTo(promise, parent, property, promises) {
654
+ let hook = unwrapStubNoProperties(promise);
655
+ if (!hook) {
656
+ throw new Error("property promises should have been resolved earlier");
657
+ }
658
+ let inner = hook.pull();
659
+ if (inner instanceof _RpcPayload) {
660
+ inner.deliverTo(parent, property, promises);
661
+ } else {
662
+ promises.push(inner.then((payload) => {
663
+ let subPromises = [];
664
+ payload.deliverTo(parent, property, subPromises);
665
+ if (subPromises.length > 0) {
666
+ return Promise.all(subPromises);
667
+ }
668
+ }));
669
+ }
670
+ }
671
+ // Call the given function with the payload as an argument. The call is made synchronously if
672
+ // possible, in order to maintain e-order. However, if any RpcPromises exist in the payload,
673
+ // they are awaited and substituted before calling the function. The result of the call is
674
+ // wrapped into another payload.
675
+ //
676
+ // The payload is automatically disposed after the call completes. The caller should not call
677
+ // dispose().
678
+ async deliverCall(func, thisArg) {
679
+ try {
680
+ let promises = [];
681
+ this.deliverTo(this, "value", promises);
682
+ if (promises.length > 0) {
683
+ await Promise.all(promises);
684
+ }
685
+ let result = Function.prototype.apply.call(func, thisArg, this.value);
686
+ if (result instanceof RpcPromise) {
687
+ return _RpcPayload.fromAppReturn(result);
688
+ } else {
689
+ return _RpcPayload.fromAppReturn(await result);
690
+ }
691
+ } finally {
692
+ this.dispose();
693
+ }
694
+ }
695
+ // Produce a promise for this payload for return to the application. Any RpcPromises in the
696
+ // payload are awaited and substituted with their results first.
697
+ //
698
+ // The returned object will have a disposer which disposes the payload. The caller should not
699
+ // separately dispose it.
700
+ async deliverResolve() {
701
+ try {
702
+ let promises = [];
703
+ this.deliverTo(this, "value", promises);
704
+ if (promises.length > 0) {
705
+ await Promise.all(promises);
706
+ }
707
+ let result = this.value;
708
+ if (result instanceof Object) {
709
+ if (!(Symbol.dispose in result)) {
710
+ Object.defineProperty(result, Symbol.dispose, {
711
+ // NOTE: Using `this.dispose.bind(this)` here causes Playwright's build of
712
+ // Chromium 140.0.7339.16 to fail when the object is assigned to a `using` variable,
713
+ // with the error:
714
+ // TypeError: Symbol(Symbol.dispose) is not a function
715
+ // I cannot reproduce this problem in Chrome 140.0.7339.127 nor in Node or workerd,
716
+ // so maybe it was a short-lived V8 bug or something. To be safe, though, we use
717
+ // `() => this.dispose()`, which seems to always work.
718
+ value: () => this.dispose(),
719
+ writable: true,
720
+ enumerable: false,
721
+ configurable: true
722
+ });
723
+ }
724
+ }
725
+ return result;
726
+ } catch (err) {
727
+ this.dispose();
728
+ throw err;
729
+ }
730
+ }
731
+ dispose() {
732
+ if (this.source === "owned") {
733
+ this.hooks.forEach((hook) => hook.dispose());
734
+ this.promises.forEach((promise) => promise.promise[Symbol.dispose]());
735
+ } else if (this.source === "return") {
736
+ this.disposeImpl(this.value, void 0);
737
+ if (this.rpcTargets && this.rpcTargets.size > 0) {
738
+ throw new Error("Not all rpcTargets were accounted for in disposeImpl()?");
739
+ }
740
+ } else ;
741
+ this.source = "owned";
742
+ this.hooks = [];
743
+ this.promises = [];
744
+ }
745
+ // Recursive dispose, called only when `source` is "return".
746
+ disposeImpl(value, parent) {
747
+ let kind = typeForRpc(value);
748
+ switch (kind) {
749
+ case "unsupported":
750
+ case "primitive":
751
+ case "bigint":
752
+ case "bytes":
753
+ case "date":
754
+ case "error":
755
+ case "undefined":
756
+ return;
757
+ case "array": {
758
+ let array = value;
759
+ let len = array.length;
760
+ for (let i = 0; i < len; i++) {
761
+ this.disposeImpl(array[i], array);
762
+ }
763
+ return;
764
+ }
765
+ case "object": {
766
+ let object = value;
767
+ for (let i in object) {
768
+ this.disposeImpl(object[i], object);
769
+ }
770
+ return;
771
+ }
772
+ case "stub":
773
+ case "rpc-promise": {
774
+ let stub = value;
775
+ let hook = unwrapStubNoProperties(stub);
776
+ if (hook) {
777
+ hook.dispose();
778
+ }
779
+ return;
780
+ }
781
+ case "function":
782
+ case "rpc-target": {
783
+ let target = value;
784
+ let hook = this.rpcTargets?.get(target);
785
+ if (hook) {
786
+ hook.dispose();
787
+ this.rpcTargets.delete(target);
788
+ } else {
789
+ disposeRpcTarget(target);
790
+ }
791
+ return;
792
+ }
793
+ case "rpc-thenable":
794
+ return;
795
+ case "writable": {
796
+ let stream = value;
797
+ let hook = this.rpcTargets?.get(stream);
798
+ if (hook) {
799
+ this.rpcTargets.delete(stream);
800
+ } else {
801
+ hook = streamImpl.createWritableStreamHook(stream);
802
+ }
803
+ hook.dispose();
804
+ return;
805
+ }
806
+ case "readable": {
807
+ let stream = value;
808
+ let hook = this.rpcTargets?.get(stream);
809
+ if (hook) {
810
+ this.rpcTargets.delete(stream);
811
+ } else {
812
+ hook = streamImpl.createReadableStreamHook(stream);
813
+ }
814
+ hook.dispose();
815
+ return;
816
+ }
817
+ default:
818
+ return;
819
+ }
820
+ }
821
+ // Ignore unhandled rejections in all promises in this payload -- that is, all promises that
822
+ // *would* be awaited if this payload were to be delivered. See the similarly-named method of
823
+ // StubHook for explanation.
824
+ ignoreUnhandledRejections() {
825
+ if (this.hooks) {
826
+ this.hooks.forEach((hook) => {
827
+ hook.ignoreUnhandledRejections();
828
+ });
829
+ this.promises.forEach(
830
+ (promise) => unwrapStubOrParent(promise.promise).ignoreUnhandledRejections()
831
+ );
832
+ } else {
833
+ this.ignoreUnhandledRejectionsImpl(this.value);
834
+ }
835
+ }
836
+ ignoreUnhandledRejectionsImpl(value) {
837
+ let kind = typeForRpc(value);
838
+ switch (kind) {
839
+ case "unsupported":
840
+ case "primitive":
841
+ case "bigint":
842
+ case "bytes":
843
+ case "date":
844
+ case "error":
845
+ case "undefined":
846
+ case "function":
847
+ case "rpc-target":
848
+ case "writable":
849
+ case "readable":
850
+ return;
851
+ case "array": {
852
+ let array = value;
853
+ let len = array.length;
854
+ for (let i = 0; i < len; i++) {
855
+ this.ignoreUnhandledRejectionsImpl(array[i]);
856
+ }
857
+ return;
858
+ }
859
+ case "object": {
860
+ let object = value;
861
+ for (let i in object) {
862
+ this.ignoreUnhandledRejectionsImpl(object[i]);
863
+ }
864
+ return;
865
+ }
866
+ case "stub":
867
+ case "rpc-promise":
868
+ unwrapStubOrParent(value).ignoreUnhandledRejections();
869
+ return;
870
+ case "rpc-thenable":
871
+ value.then((_) => {
872
+ }, (_) => {
873
+ });
874
+ return;
875
+ default:
876
+ return;
877
+ }
878
+ }
879
+ };
880
+ function followPath(value, parent, path, owner) {
881
+ for (let i = 0; i < path.length; i++) {
882
+ parent = value;
883
+ let part = path[i];
884
+ if (part in Object.prototype) {
885
+ value = void 0;
886
+ continue;
887
+ }
888
+ let kind = typeForRpc(value);
889
+ switch (kind) {
890
+ case "object":
891
+ case "function":
892
+ if (Object.hasOwn(value, part)) {
893
+ value = value[part];
894
+ } else {
895
+ value = void 0;
896
+ }
897
+ break;
898
+ case "array":
899
+ if (Number.isInteger(part) && part >= 0) {
900
+ value = value[part];
901
+ } else {
902
+ value = void 0;
903
+ }
904
+ break;
905
+ case "rpc-target":
906
+ case "rpc-thenable": {
907
+ if (Object.hasOwn(value, part)) {
908
+ throw new TypeError(
909
+ `Attempted to access property '${part}', which is an instance property of the RpcTarget. To avoid leaking private internals, instance properties cannot be accessed over RPC. If you want to make this property available over RPC, define it as a method or getter on the class, instead of an instance property.`
910
+ );
911
+ } else {
912
+ value = value[part];
913
+ }
914
+ owner = null;
915
+ break;
916
+ }
917
+ case "stub":
918
+ case "rpc-promise": {
919
+ let { hook, pathIfPromise } = unwrapStubAndPath(value);
920
+ return { hook, remainingPath: pathIfPromise ? pathIfPromise.concat(path.slice(i)) : path.slice(i) };
921
+ }
922
+ case "writable":
923
+ value = void 0;
924
+ break;
925
+ case "readable":
926
+ value = void 0;
927
+ break;
928
+ case "primitive":
929
+ case "bigint":
930
+ case "bytes":
931
+ case "date":
932
+ case "error":
933
+ value = void 0;
934
+ break;
935
+ case "undefined":
936
+ value = value[part];
937
+ break;
938
+ case "unsupported": {
939
+ if (i === 0) {
940
+ throw new TypeError(`RPC stub points at a non-serializable type.`);
941
+ } else {
942
+ let prefix = path.slice(0, i).join(".");
943
+ let remainder = path.slice(0, i).join(".");
944
+ throw new TypeError(
945
+ `'${prefix}' is not a serializable type, so property ${remainder} cannot be accessed.`
946
+ );
947
+ }
948
+ }
949
+ default:
950
+ throw new TypeError("unreachable");
951
+ }
952
+ }
953
+ if (value instanceof RpcPromise) {
954
+ let { hook, pathIfPromise } = unwrapStubAndPath(value);
955
+ return { hook, remainingPath: pathIfPromise || [] };
956
+ }
957
+ return {
958
+ value,
959
+ parent,
960
+ owner
961
+ };
962
+ }
963
+ var ValueStubHook = class extends StubHook {
964
+ call(path, args) {
965
+ try {
966
+ let { value, owner } = this.getValue();
967
+ let followResult = followPath(value, void 0, path, owner);
968
+ if (followResult.hook) {
969
+ return followResult.hook.call(followResult.remainingPath, args);
970
+ }
971
+ if (typeof followResult.value != "function") {
972
+ throw new TypeError(`'${path.join(".")}' is not a function.`);
973
+ }
974
+ let promise = args.deliverCall(followResult.value, followResult.parent);
975
+ return new PromiseStubHook(promise.then((payload) => {
976
+ return new PayloadStubHook(payload);
977
+ }));
978
+ } catch (err) {
979
+ return new ErrorStubHook(err);
980
+ }
981
+ }
982
+ map(path, captures, instructions) {
983
+ try {
984
+ let followResult;
985
+ try {
986
+ let { value, owner } = this.getValue();
987
+ followResult = followPath(value, void 0, path, owner);
988
+ ;
989
+ } catch (err) {
990
+ for (let cap of captures) {
991
+ cap.dispose();
992
+ }
993
+ throw err;
994
+ }
995
+ if (followResult.hook) {
996
+ return followResult.hook.map(followResult.remainingPath, captures, instructions);
997
+ }
998
+ return mapImpl.applyMap(
999
+ followResult.value,
1000
+ followResult.parent,
1001
+ followResult.owner,
1002
+ captures,
1003
+ instructions
1004
+ );
1005
+ } catch (err) {
1006
+ return new ErrorStubHook(err);
1007
+ }
1008
+ }
1009
+ get(path) {
1010
+ try {
1011
+ let { value, owner } = this.getValue();
1012
+ if (path.length === 0 && owner === null) {
1013
+ throw new Error("Can't dup an RpcTarget stub as a promise.");
1014
+ }
1015
+ let followResult = followPath(value, void 0, path, owner);
1016
+ if (followResult.hook) {
1017
+ return followResult.hook.get(followResult.remainingPath);
1018
+ }
1019
+ return new PayloadStubHook(RpcPayload.deepCopyFrom(
1020
+ followResult.value,
1021
+ followResult.parent,
1022
+ followResult.owner
1023
+ ));
1024
+ } catch (err) {
1025
+ return new ErrorStubHook(err);
1026
+ }
1027
+ }
1028
+ };
1029
+ var PayloadStubHook = class _PayloadStubHook extends ValueStubHook {
1030
+ constructor(payload) {
1031
+ super();
1032
+ this.payload = payload;
1033
+ }
1034
+ payload;
1035
+ // cleared when disposed
1036
+ getPayload() {
1037
+ if (this.payload) {
1038
+ return this.payload;
1039
+ } else {
1040
+ throw new Error("Attempted to use an RPC StubHook after it was disposed.");
1041
+ }
1042
+ }
1043
+ getValue() {
1044
+ let payload = this.getPayload();
1045
+ return { value: payload.value, owner: payload };
1046
+ }
1047
+ dup() {
1048
+ let thisPayload = this.getPayload();
1049
+ return new _PayloadStubHook(RpcPayload.deepCopyFrom(
1050
+ thisPayload.value,
1051
+ void 0,
1052
+ thisPayload
1053
+ ));
1054
+ }
1055
+ pull() {
1056
+ return this.getPayload();
1057
+ }
1058
+ ignoreUnhandledRejections() {
1059
+ if (this.payload) {
1060
+ this.payload.ignoreUnhandledRejections();
1061
+ }
1062
+ }
1063
+ dispose() {
1064
+ if (this.payload) {
1065
+ this.payload.dispose();
1066
+ this.payload = void 0;
1067
+ }
1068
+ }
1069
+ onBroken(callback) {
1070
+ if (this.payload) {
1071
+ if (this.payload.value instanceof RpcStub) {
1072
+ this.payload.value.onRpcBroken(callback);
1073
+ }
1074
+ }
1075
+ }
1076
+ };
1077
+ function disposeRpcTarget(target) {
1078
+ if (Symbol.dispose in target) {
1079
+ try {
1080
+ target[Symbol.dispose]();
1081
+ } catch (err) {
1082
+ Promise.reject(err);
1083
+ }
1084
+ }
1085
+ }
1086
+ var TargetStubHook = class _TargetStubHook extends ValueStubHook {
1087
+ // Constructs a TargetStubHook that is not duplicated from an existing hook.
1088
+ //
1089
+ // If `value` is a function, `parent` is bound as its "this".
1090
+ static create(value, parent) {
1091
+ if (typeof value !== "function") {
1092
+ parent = void 0;
1093
+ }
1094
+ return new _TargetStubHook(value, parent);
1095
+ }
1096
+ constructor(target, parent, dupFrom) {
1097
+ super();
1098
+ this.target = target;
1099
+ this.parent = parent;
1100
+ if (dupFrom) {
1101
+ if (dupFrom.refcount) {
1102
+ this.refcount = dupFrom.refcount;
1103
+ ++this.refcount.count;
1104
+ }
1105
+ } else if (Symbol.dispose in target) {
1106
+ this.refcount = { count: 1 };
1107
+ }
1108
+ }
1109
+ target;
1110
+ // cleared when disposed
1111
+ parent;
1112
+ // `this` parameter when calling `target`
1113
+ refcount;
1114
+ // undefined if not needed (because target has no disposer)
1115
+ getTarget() {
1116
+ if (this.target) {
1117
+ return this.target;
1118
+ } else {
1119
+ throw new Error("Attempted to use an RPC StubHook after it was disposed.");
1120
+ }
1121
+ }
1122
+ getValue() {
1123
+ return { value: this.getTarget(), owner: null };
1124
+ }
1125
+ dup() {
1126
+ return new _TargetStubHook(this.getTarget(), this.parent, this);
1127
+ }
1128
+ pull() {
1129
+ let target = this.getTarget();
1130
+ if ("then" in target) {
1131
+ return Promise.resolve(target).then((resolution) => {
1132
+ return RpcPayload.fromAppReturn(resolution);
1133
+ });
1134
+ } else {
1135
+ return Promise.reject(new Error("Tried to resolve a non-promise stub."));
1136
+ }
1137
+ }
1138
+ ignoreUnhandledRejections() {
1139
+ }
1140
+ dispose() {
1141
+ if (this.target) {
1142
+ if (this.refcount) {
1143
+ if (--this.refcount.count == 0) {
1144
+ disposeRpcTarget(this.target);
1145
+ }
1146
+ }
1147
+ this.target = void 0;
1148
+ }
1149
+ }
1150
+ onBroken(callback) {
1151
+ }
1152
+ };
1153
+ var PromiseStubHook = class _PromiseStubHook extends StubHook {
1154
+ promise;
1155
+ resolution;
1156
+ constructor(promise) {
1157
+ super();
1158
+ this.promise = promise.then((res) => {
1159
+ this.resolution = res;
1160
+ return res;
1161
+ });
1162
+ }
1163
+ call(path, args) {
1164
+ args.ensureDeepCopied();
1165
+ return new _PromiseStubHook(this.promise.then((hook) => hook.call(path, args)));
1166
+ }
1167
+ stream(path, args) {
1168
+ args.ensureDeepCopied();
1169
+ let promise = this.promise.then((hook) => {
1170
+ let result = hook.stream(path, args);
1171
+ return result.promise;
1172
+ });
1173
+ return { promise };
1174
+ }
1175
+ map(path, captures, instructions) {
1176
+ return new _PromiseStubHook(this.promise.then(
1177
+ (hook) => hook.map(path, captures, instructions),
1178
+ (err) => {
1179
+ for (let cap of captures) {
1180
+ cap.dispose();
1181
+ }
1182
+ throw err;
1183
+ }
1184
+ ));
1185
+ }
1186
+ get(path) {
1187
+ return new _PromiseStubHook(this.promise.then((hook) => hook.get(path)));
1188
+ }
1189
+ dup() {
1190
+ if (this.resolution) {
1191
+ return this.resolution.dup();
1192
+ } else {
1193
+ return new _PromiseStubHook(this.promise.then((hook) => hook.dup()));
1194
+ }
1195
+ }
1196
+ pull() {
1197
+ if (this.resolution) {
1198
+ return this.resolution.pull();
1199
+ } else {
1200
+ return this.promise.then((hook) => hook.pull());
1201
+ }
1202
+ }
1203
+ ignoreUnhandledRejections() {
1204
+ if (this.resolution) {
1205
+ this.resolution.ignoreUnhandledRejections();
1206
+ } else {
1207
+ this.promise.then((res) => {
1208
+ res.ignoreUnhandledRejections();
1209
+ }, (err) => {
1210
+ });
1211
+ }
1212
+ }
1213
+ dispose() {
1214
+ if (this.resolution) {
1215
+ this.resolution.dispose();
1216
+ } else {
1217
+ this.promise.then((hook) => {
1218
+ hook.dispose();
1219
+ }, (err) => {
1220
+ });
1221
+ }
1222
+ }
1223
+ onBroken(callback) {
1224
+ if (this.resolution) {
1225
+ this.resolution.onBroken(callback);
1226
+ } else {
1227
+ this.promise.then((hook) => {
1228
+ hook.onBroken(callback);
1229
+ }, callback);
1230
+ }
1231
+ }
1232
+ };
1233
+
1234
+ // src/serialize.ts
1235
+ var NullExporter = class {
1236
+ exportStub(stub) {
1237
+ throw new Error("Cannot serialize RPC stubs without an RPC session.");
1238
+ }
1239
+ exportPromise(stub) {
1240
+ throw new Error("Cannot serialize RPC stubs without an RPC session.");
1241
+ }
1242
+ getImport(hook) {
1243
+ return void 0;
1244
+ }
1245
+ unexport(ids) {
1246
+ }
1247
+ createPipe(readable) {
1248
+ throw new Error("Cannot create pipes without an RPC session.");
1249
+ }
1250
+ onSendError(error) {
1251
+ }
1252
+ };
1253
+ var NULL_EXPORTER = new NullExporter();
1254
+ var ERROR_TYPES = {
1255
+ Error,
1256
+ EvalError,
1257
+ RangeError,
1258
+ ReferenceError,
1259
+ SyntaxError,
1260
+ TypeError,
1261
+ URIError,
1262
+ AggregateError
1263
+ // TODO: DOMError? Others?
1264
+ };
1265
+ var Devaluator = class _Devaluator {
1266
+ constructor(exporter, source) {
1267
+ this.exporter = exporter;
1268
+ this.source = source;
1269
+ }
1270
+ // Devaluate the given value.
1271
+ // * value: The value to devaluate.
1272
+ // * parent: The value's parent object, which would be used as `this` if the value were called
1273
+ // as a function.
1274
+ // * exporter: Callbacks to the RPC session for exporting capabilities found in this message.
1275
+ // * source: The RpcPayload which contains the value, and therefore owns stubs within.
1276
+ //
1277
+ // Returns: The devaluated value, ready to be JSON-serialized.
1278
+ static devaluate(value, parent, exporter = NULL_EXPORTER, source) {
1279
+ let devaluator = new _Devaluator(exporter, source);
1280
+ try {
1281
+ return devaluator.devaluateImpl(value, parent, 0);
1282
+ } catch (err) {
1283
+ if (devaluator.exports) {
1284
+ try {
1285
+ exporter.unexport(devaluator.exports);
1286
+ } catch (err2) {
1287
+ }
1288
+ }
1289
+ throw err;
1290
+ }
1291
+ }
1292
+ exports;
1293
+ devaluateImpl(value, parent, depth) {
1294
+ if (depth >= 64) {
1295
+ throw new Error(
1296
+ "Serialization exceeded maximum allowed depth. (Does the message contain cycles?)"
1297
+ );
1298
+ }
1299
+ let kind = typeForRpc(value);
1300
+ switch (kind) {
1301
+ case "unsupported": {
1302
+ let msg;
1303
+ try {
1304
+ msg = `Cannot serialize value: ${value}`;
1305
+ } catch (err) {
1306
+ msg = "Cannot serialize value: (couldn't stringify value)";
1307
+ }
1308
+ throw new TypeError(msg);
1309
+ }
1310
+ case "primitive":
1311
+ if (typeof value === "number" && !isFinite(value)) {
1312
+ if (value === Infinity) {
1313
+ return ["inf"];
1314
+ } else if (value === -Infinity) {
1315
+ return ["-inf"];
1316
+ } else {
1317
+ return ["nan"];
1318
+ }
1319
+ } else {
1320
+ return value;
1321
+ }
1322
+ case "object": {
1323
+ let object = value;
1324
+ let result = {};
1325
+ for (let key in object) {
1326
+ result[key] = this.devaluateImpl(object[key], object, depth + 1);
1327
+ }
1328
+ return result;
1329
+ }
1330
+ case "array": {
1331
+ let array = value;
1332
+ let len = array.length;
1333
+ let result = new Array(len);
1334
+ for (let i = 0; i < len; i++) {
1335
+ result[i] = this.devaluateImpl(array[i], array, depth + 1);
1336
+ }
1337
+ return [result];
1338
+ }
1339
+ case "bigint":
1340
+ return ["bigint", value.toString()];
1341
+ case "date":
1342
+ return ["date", value.getTime()];
1343
+ case "bytes": {
1344
+ let bytes = value;
1345
+ if (bytes.toBase64) {
1346
+ return ["bytes", bytes.toBase64({ omitPadding: true })];
1347
+ } else {
1348
+ return [
1349
+ "bytes",
1350
+ btoa(String.fromCharCode.apply(null, bytes).replace(/=*$/, ""))
1351
+ ];
1352
+ }
1353
+ }
1354
+ case "error": {
1355
+ let e = value;
1356
+ let rewritten = this.exporter.onSendError(e);
1357
+ if (rewritten) {
1358
+ e = rewritten;
1359
+ }
1360
+ let result = ["error", e.name, e.message];
1361
+ if (rewritten && rewritten.stack) {
1362
+ result.push(rewritten.stack);
1363
+ }
1364
+ return result;
1365
+ }
1366
+ case "undefined":
1367
+ return ["undefined"];
1368
+ case "stub":
1369
+ case "rpc-promise": {
1370
+ if (!this.source) {
1371
+ throw new Error("Can't serialize RPC stubs in this context.");
1372
+ }
1373
+ let { hook, pathIfPromise } = unwrapStubAndPath(value);
1374
+ let importId = this.exporter.getImport(hook);
1375
+ if (importId !== void 0) {
1376
+ if (pathIfPromise) {
1377
+ if (pathIfPromise.length > 0) {
1378
+ return ["pipeline", importId, pathIfPromise];
1379
+ } else {
1380
+ return ["pipeline", importId];
1381
+ }
1382
+ } else {
1383
+ return ["import", importId];
1384
+ }
1385
+ }
1386
+ if (pathIfPromise) {
1387
+ hook = hook.get(pathIfPromise);
1388
+ } else {
1389
+ hook = hook.dup();
1390
+ }
1391
+ return this.devaluateHook(pathIfPromise ? "promise" : "export", hook);
1392
+ }
1393
+ case "function":
1394
+ case "rpc-target": {
1395
+ if (!this.source) {
1396
+ throw new Error("Can't serialize RPC stubs in this context.");
1397
+ }
1398
+ let hook = this.source.getHookForRpcTarget(value, parent);
1399
+ return this.devaluateHook("export", hook);
1400
+ }
1401
+ case "rpc-thenable": {
1402
+ if (!this.source) {
1403
+ throw new Error("Can't serialize RPC stubs in this context.");
1404
+ }
1405
+ let hook = this.source.getHookForRpcTarget(value, parent);
1406
+ return this.devaluateHook("promise", hook);
1407
+ }
1408
+ case "writable": {
1409
+ if (!this.source) {
1410
+ throw new Error("Can't serialize WritableStream in this context.");
1411
+ }
1412
+ let hook = this.source.getHookForWritableStream(value, parent);
1413
+ return this.devaluateHook("writable", hook);
1414
+ }
1415
+ case "readable": {
1416
+ if (!this.source) {
1417
+ throw new Error("Can't serialize ReadableStream in this context.");
1418
+ }
1419
+ let ws = value;
1420
+ let hook = this.source.getHookForReadableStream(ws, parent);
1421
+ let importId = this.exporter.createPipe(ws, hook);
1422
+ return ["readable", importId];
1423
+ }
1424
+ default:
1425
+ throw new Error("unreachable");
1426
+ }
1427
+ }
1428
+ devaluateHook(type, hook) {
1429
+ if (!this.exports) this.exports = [];
1430
+ let exportId = type === "promise" ? this.exporter.exportPromise(hook) : this.exporter.exportStub(hook);
1431
+ this.exports.push(exportId);
1432
+ return [type, exportId];
1433
+ }
1434
+ };
1435
+ function serialize(value) {
1436
+ return JSON.stringify(Devaluator.devaluate(value));
1437
+ }
1438
+ var NullImporter = class {
1439
+ importStub(idx) {
1440
+ throw new Error("Cannot deserialize RPC stubs without an RPC session.");
1441
+ }
1442
+ importPromise(idx) {
1443
+ throw new Error("Cannot deserialize RPC stubs without an RPC session.");
1444
+ }
1445
+ getExport(idx) {
1446
+ return void 0;
1447
+ }
1448
+ getPipeReadable(exportId) {
1449
+ throw new Error("Cannot retrieve pipe readable without an RPC session.");
1450
+ }
1451
+ };
1452
+ var NULL_IMPORTER = new NullImporter();
1453
+ var Evaluator = class _Evaluator {
1454
+ constructor(importer) {
1455
+ this.importer = importer;
1456
+ }
1457
+ hooks = [];
1458
+ promises = [];
1459
+ evaluate(value) {
1460
+ let payload = RpcPayload.forEvaluate(this.hooks, this.promises);
1461
+ try {
1462
+ payload.value = this.evaluateImpl(value, payload, "value");
1463
+ return payload;
1464
+ } catch (err) {
1465
+ payload.dispose();
1466
+ throw err;
1467
+ }
1468
+ }
1469
+ // Evaluate the value without destroying it.
1470
+ evaluateCopy(value) {
1471
+ return this.evaluate(structuredClone(value));
1472
+ }
1473
+ evaluateImpl(value, parent, property) {
1474
+ if (value instanceof Array) {
1475
+ if (value.length == 1 && value[0] instanceof Array) {
1476
+ let result = value[0];
1477
+ for (let i = 0; i < result.length; i++) {
1478
+ result[i] = this.evaluateImpl(result[i], result, i);
1479
+ }
1480
+ return result;
1481
+ } else switch (value[0]) {
1482
+ case "bigint":
1483
+ if (typeof value[1] == "string") {
1484
+ return BigInt(value[1]);
1485
+ }
1486
+ break;
1487
+ case "date":
1488
+ if (typeof value[1] == "number") {
1489
+ return new Date(value[1]);
1490
+ }
1491
+ break;
1492
+ case "bytes": {
1493
+ let b64 = Uint8Array;
1494
+ if (typeof value[1] == "string") {
1495
+ if (b64.fromBase64) {
1496
+ return b64.fromBase64(value[1]);
1497
+ } else {
1498
+ let bs = atob(value[1]);
1499
+ let len = bs.length;
1500
+ let bytes = new Uint8Array(len);
1501
+ for (let i = 0; i < len; i++) {
1502
+ bytes[i] = bs.charCodeAt(i);
1503
+ }
1504
+ return bytes;
1505
+ }
1506
+ }
1507
+ break;
1508
+ }
1509
+ case "error":
1510
+ if (value.length >= 3 && typeof value[1] === "string" && typeof value[2] === "string") {
1511
+ let cls = ERROR_TYPES[value[1]] || Error;
1512
+ let result = new cls(value[2]);
1513
+ if (typeof value[3] === "string") {
1514
+ result.stack = value[3];
1515
+ }
1516
+ return result;
1517
+ }
1518
+ break;
1519
+ case "undefined":
1520
+ if (value.length === 1) {
1521
+ return void 0;
1522
+ }
1523
+ break;
1524
+ case "inf":
1525
+ return Infinity;
1526
+ case "-inf":
1527
+ return -Infinity;
1528
+ case "nan":
1529
+ return NaN;
1530
+ case "import":
1531
+ case "pipeline": {
1532
+ if (value.length < 2 || value.length > 4) {
1533
+ break;
1534
+ }
1535
+ if (typeof value[1] != "number") {
1536
+ break;
1537
+ }
1538
+ let hook = this.importer.getExport(value[1]);
1539
+ if (!hook) {
1540
+ throw new Error(`no such entry on exports table: ${value[1]}`);
1541
+ }
1542
+ let isPromise = value[0] == "pipeline";
1543
+ let addStub = (hook2) => {
1544
+ if (isPromise) {
1545
+ let promise = new RpcPromise(hook2, []);
1546
+ this.promises.push({ promise, parent, property });
1547
+ return promise;
1548
+ } else {
1549
+ this.hooks.push(hook2);
1550
+ return new RpcPromise(hook2, []);
1551
+ }
1552
+ };
1553
+ if (value.length == 2) {
1554
+ if (isPromise) {
1555
+ return addStub(hook.get([]));
1556
+ } else {
1557
+ return addStub(hook.dup());
1558
+ }
1559
+ }
1560
+ let path = value[2];
1561
+ if (!(path instanceof Array)) {
1562
+ break;
1563
+ }
1564
+ if (!path.every(
1565
+ (part) => {
1566
+ return typeof part == "string" || typeof part == "number";
1567
+ }
1568
+ )) {
1569
+ break;
1570
+ }
1571
+ if (value.length == 3) {
1572
+ return addStub(hook.get(path));
1573
+ }
1574
+ let args = value[3];
1575
+ if (!(args instanceof Array)) {
1576
+ break;
1577
+ }
1578
+ let subEval = new _Evaluator(this.importer);
1579
+ args = subEval.evaluate([args]);
1580
+ return addStub(hook.call(path, args));
1581
+ }
1582
+ case "remap": {
1583
+ if (value.length !== 5 || typeof value[1] !== "number" || !(value[2] instanceof Array) || !(value[3] instanceof Array) || !(value[4] instanceof Array)) {
1584
+ break;
1585
+ }
1586
+ let hook = this.importer.getExport(value[1]);
1587
+ if (!hook) {
1588
+ throw new Error(`no such entry on exports table: ${value[1]}`);
1589
+ }
1590
+ let path = value[2];
1591
+ if (!path.every(
1592
+ (part) => {
1593
+ return typeof part == "string" || typeof part == "number";
1594
+ }
1595
+ )) {
1596
+ break;
1597
+ }
1598
+ let captures = value[3].map((cap) => {
1599
+ if (!(cap instanceof Array) || cap.length !== 2 || cap[0] !== "import" && cap[0] !== "export" || typeof cap[1] !== "number") {
1600
+ throw new TypeError(`unknown map capture: ${JSON.stringify(cap)}`);
1601
+ }
1602
+ if (cap[0] === "export") {
1603
+ return this.importer.importStub(cap[1]);
1604
+ } else {
1605
+ let exp = this.importer.getExport(cap[1]);
1606
+ if (!exp) {
1607
+ throw new Error(`no such entry on exports table: ${cap[1]}`);
1608
+ }
1609
+ return exp.dup();
1610
+ }
1611
+ });
1612
+ let instructions = value[4];
1613
+ let resultHook = hook.map(path, captures, instructions);
1614
+ let promise = new RpcPromise(resultHook, []);
1615
+ this.promises.push({ promise, parent, property });
1616
+ return promise;
1617
+ }
1618
+ case "export":
1619
+ case "promise":
1620
+ if (typeof value[1] == "number") {
1621
+ if (value[0] == "promise") {
1622
+ let hook = this.importer.importPromise(value[1]);
1623
+ let promise = new RpcPromise(hook, []);
1624
+ this.promises.push({ parent, property, promise });
1625
+ return promise;
1626
+ } else {
1627
+ let hook = this.importer.importStub(value[1]);
1628
+ this.hooks.push(hook);
1629
+ return new RpcStub(hook);
1630
+ }
1631
+ }
1632
+ break;
1633
+ case "writable":
1634
+ if (typeof value[1] == "number") {
1635
+ let hook = this.importer.importStub(value[1]);
1636
+ let stream = streamImpl.createWritableStreamFromHook(hook);
1637
+ this.hooks.push(hook);
1638
+ return stream;
1639
+ }
1640
+ break;
1641
+ case "readable":
1642
+ if (typeof value[1] == "number") {
1643
+ let stream = this.importer.getPipeReadable(value[1]);
1644
+ let hook = streamImpl.createReadableStreamHook(stream);
1645
+ this.hooks.push(hook);
1646
+ return stream;
1647
+ }
1648
+ break;
1649
+ }
1650
+ throw new TypeError(`unknown special value: ${JSON.stringify(value)}`);
1651
+ } else if (value instanceof Object) {
1652
+ let result = value;
1653
+ for (let key in result) {
1654
+ if (key in Object.prototype || key === "toJSON") {
1655
+ this.evaluateImpl(result[key], result, key);
1656
+ delete result[key];
1657
+ } else {
1658
+ result[key] = this.evaluateImpl(result[key], result, key);
1659
+ }
1660
+ }
1661
+ return result;
1662
+ } else {
1663
+ return value;
1664
+ }
1665
+ }
1666
+ };
1667
+ function deserialize(value) {
1668
+ let payload = new Evaluator(NULL_IMPORTER).evaluate(JSON.parse(value));
1669
+ payload.dispose();
1670
+ return payload.value;
1671
+ }
1672
+
1673
+ // src/rpc.ts
1674
+ var ImportTableEntry = class {
1675
+ constructor(session, importId, pulling) {
1676
+ this.session = session;
1677
+ this.importId = importId;
1678
+ if (pulling) {
1679
+ this.activePull = Promise.withResolvers();
1680
+ }
1681
+ }
1682
+ localRefcount = 0;
1683
+ remoteRefcount = 1;
1684
+ activePull;
1685
+ resolution;
1686
+ // List of integer indexes into session.onBrokenCallbacks which are callbacks registered on
1687
+ // this import. Initialized on first use (so `undefined` is the same as an empty list).
1688
+ onBrokenRegistrations;
1689
+ resolve(resolution) {
1690
+ if (this.localRefcount == 0) {
1691
+ resolution.dispose();
1692
+ return;
1693
+ }
1694
+ this.resolution = resolution;
1695
+ this.sendRelease();
1696
+ if (this.onBrokenRegistrations) {
1697
+ for (let i of this.onBrokenRegistrations) {
1698
+ let callback = this.session.onBrokenCallbacks[i];
1699
+ let endIndex = this.session.onBrokenCallbacks.length;
1700
+ resolution.onBroken(callback);
1701
+ if (this.session.onBrokenCallbacks[endIndex] === callback) {
1702
+ delete this.session.onBrokenCallbacks[endIndex];
1703
+ } else {
1704
+ delete this.session.onBrokenCallbacks[i];
1705
+ }
1706
+ }
1707
+ this.onBrokenRegistrations = void 0;
1708
+ }
1709
+ if (this.activePull) {
1710
+ this.activePull.resolve();
1711
+ this.activePull = void 0;
1712
+ }
1713
+ }
1714
+ async awaitResolution() {
1715
+ if (!this.activePull) {
1716
+ this.session.sendPull(this.importId);
1717
+ this.activePull = Promise.withResolvers();
1718
+ }
1719
+ await this.activePull.promise;
1720
+ return this.resolution.pull();
1721
+ }
1722
+ dispose() {
1723
+ if (this.resolution) {
1724
+ this.resolution.dispose();
1725
+ } else {
1726
+ this.abort(new Error("RPC was canceled because the RpcPromise was disposed."));
1727
+ this.sendRelease();
1728
+ }
1729
+ }
1730
+ abort(error) {
1731
+ if (!this.resolution) {
1732
+ this.resolution = new ErrorStubHook(error);
1733
+ if (this.activePull) {
1734
+ this.activePull.reject(error);
1735
+ this.activePull = void 0;
1736
+ }
1737
+ this.onBrokenRegistrations = void 0;
1738
+ }
1739
+ }
1740
+ onBroken(callback) {
1741
+ if (this.resolution) {
1742
+ this.resolution.onBroken(callback);
1743
+ } else {
1744
+ let index = this.session.onBrokenCallbacks.length;
1745
+ this.session.onBrokenCallbacks.push(callback);
1746
+ if (!this.onBrokenRegistrations) this.onBrokenRegistrations = [];
1747
+ this.onBrokenRegistrations.push(index);
1748
+ }
1749
+ }
1750
+ sendRelease() {
1751
+ if (this.remoteRefcount > 0) {
1752
+ this.session.sendRelease(this.importId, this.remoteRefcount);
1753
+ this.remoteRefcount = 0;
1754
+ }
1755
+ }
1756
+ };
1757
+ var RpcImportHook = class _RpcImportHook extends StubHook {
1758
+ // undefined when we're disposed
1759
+ // `pulling` is true if we already expect that this import is going to be resolved later, and
1760
+ // null if this import is not allowed to be pulled (i.e. it's a stub not a promise).
1761
+ constructor(isPromise, entry) {
1762
+ super();
1763
+ this.isPromise = isPromise;
1764
+ ++entry.localRefcount;
1765
+ this.entry = entry;
1766
+ }
1767
+ entry;
1768
+ collectPath(path) {
1769
+ return this;
1770
+ }
1771
+ getEntry() {
1772
+ if (this.entry) {
1773
+ return this.entry;
1774
+ } else {
1775
+ throw new Error("This RpcImportHook was already disposed.");
1776
+ }
1777
+ }
1778
+ // -------------------------------------------------------------------------------------
1779
+ // implements StubHook
1780
+ call(path, args) {
1781
+ let entry = this.getEntry();
1782
+ if (entry.resolution) {
1783
+ return entry.resolution.call(path, args);
1784
+ } else {
1785
+ return entry.session.sendCall(entry.importId, path, args);
1786
+ }
1787
+ }
1788
+ stream(path, args) {
1789
+ let entry = this.getEntry();
1790
+ if (entry.resolution) {
1791
+ return entry.resolution.stream(path, args);
1792
+ } else {
1793
+ return entry.session.sendStream(entry.importId, path, args);
1794
+ }
1795
+ }
1796
+ map(path, captures, instructions) {
1797
+ let entry;
1798
+ try {
1799
+ entry = this.getEntry();
1800
+ } catch (err) {
1801
+ for (let cap of captures) {
1802
+ cap.dispose();
1803
+ }
1804
+ throw err;
1805
+ }
1806
+ if (entry.resolution) {
1807
+ return entry.resolution.map(path, captures, instructions);
1808
+ } else {
1809
+ return entry.session.sendMap(entry.importId, path, captures, instructions);
1810
+ }
1811
+ }
1812
+ get(path) {
1813
+ let entry = this.getEntry();
1814
+ if (entry.resolution) {
1815
+ return entry.resolution.get(path);
1816
+ } else {
1817
+ return entry.session.sendCall(entry.importId, path);
1818
+ }
1819
+ }
1820
+ dup() {
1821
+ return new _RpcImportHook(false, this.getEntry());
1822
+ }
1823
+ pull() {
1824
+ let entry = this.getEntry();
1825
+ if (!this.isPromise) {
1826
+ throw new Error("Can't pull this hook because it's not a promise hook.");
1827
+ }
1828
+ if (entry.resolution) {
1829
+ return entry.resolution.pull();
1830
+ }
1831
+ return entry.awaitResolution();
1832
+ }
1833
+ ignoreUnhandledRejections() {
1834
+ }
1835
+ dispose() {
1836
+ let entry = this.entry;
1837
+ this.entry = void 0;
1838
+ if (entry) {
1839
+ if (--entry.localRefcount === 0) {
1840
+ entry.dispose();
1841
+ }
1842
+ }
1843
+ }
1844
+ onBroken(callback) {
1845
+ if (this.entry) {
1846
+ this.entry.onBroken(callback);
1847
+ }
1848
+ }
1849
+ };
1850
+ var RpcMainHook = class extends RpcImportHook {
1851
+ session;
1852
+ constructor(entry) {
1853
+ super(false, entry);
1854
+ this.session = entry.session;
1855
+ }
1856
+ dispose() {
1857
+ if (this.session) {
1858
+ let session = this.session;
1859
+ this.session = void 0;
1860
+ session.shutdown();
1861
+ }
1862
+ }
1863
+ };
1864
+ var RpcSessionImpl = class {
1865
+ constructor(transport, mainHook, options) {
1866
+ this.transport = transport;
1867
+ this.options = options;
1868
+ this.exports.push({ hook: mainHook, refcount: 1 });
1869
+ this.imports.push(new ImportTableEntry(this, 0, false));
1870
+ let rejectFunc;
1871
+ let abortPromise = new Promise((resolve, reject) => {
1872
+ rejectFunc = reject;
1873
+ });
1874
+ this.cancelReadLoop = rejectFunc;
1875
+ this.readLoop(abortPromise).catch((err) => this.abort(err));
1876
+ }
1877
+ exports = [];
1878
+ reverseExports = /* @__PURE__ */ new Map();
1879
+ imports = [];
1880
+ abortReason;
1881
+ cancelReadLoop;
1882
+ // We assign positive numbers to imports we initiate, and negative numbers to exports we
1883
+ // initiate. So the next import ID is just `imports.length`, but the next export ID needs
1884
+ // to be tracked explicitly.
1885
+ nextExportId = -1;
1886
+ // If set, call this when all incoming calls are complete.
1887
+ onBatchDone;
1888
+ // How many promises is our peer expecting us to resolve?
1889
+ pullCount = 0;
1890
+ // Sparse array of onBrokenCallback registrations. Items are strictly appended to the end but
1891
+ // may be deleted from the middle (hence leaving the array sparse).
1892
+ onBrokenCallbacks = [];
1893
+ // Should only be called once immediately after construction.
1894
+ getMainImport() {
1895
+ return new RpcMainHook(this.imports[0]);
1896
+ }
1897
+ shutdown() {
1898
+ this.abort(new Error("RPC session was shut down by disposing the main stub"), false);
1899
+ }
1900
+ exportStub(hook) {
1901
+ if (this.abortReason) throw this.abortReason;
1902
+ let existingExportId = this.reverseExports.get(hook);
1903
+ if (existingExportId !== void 0) {
1904
+ ++this.exports[existingExportId].refcount;
1905
+ return existingExportId;
1906
+ } else {
1907
+ let exportId = this.nextExportId--;
1908
+ this.exports[exportId] = { hook, refcount: 1 };
1909
+ this.reverseExports.set(hook, exportId);
1910
+ return exportId;
1911
+ }
1912
+ }
1913
+ exportPromise(hook) {
1914
+ if (this.abortReason) throw this.abortReason;
1915
+ let exportId = this.nextExportId--;
1916
+ this.exports[exportId] = { hook, refcount: 1 };
1917
+ this.reverseExports.set(hook, exportId);
1918
+ this.ensureResolvingExport(exportId);
1919
+ return exportId;
1920
+ }
1921
+ unexport(ids) {
1922
+ for (let id of ids) {
1923
+ this.releaseExport(id, 1);
1924
+ }
1925
+ }
1926
+ releaseExport(exportId, refcount) {
1927
+ let entry = this.exports[exportId];
1928
+ if (!entry) {
1929
+ throw new Error(`no such export ID: ${exportId}`);
1930
+ }
1931
+ if (entry.refcount < refcount) {
1932
+ throw new Error(`refcount would go negative: ${entry.refcount} < ${refcount}`);
1933
+ }
1934
+ entry.refcount -= refcount;
1935
+ if (entry.refcount === 0) {
1936
+ delete this.exports[exportId];
1937
+ this.reverseExports.delete(entry.hook);
1938
+ entry.hook.dispose();
1939
+ }
1940
+ }
1941
+ onSendError(error) {
1942
+ if (this.options.onSendError) {
1943
+ return this.options.onSendError(error);
1944
+ }
1945
+ }
1946
+ ensureResolvingExport(exportId) {
1947
+ let exp = this.exports[exportId];
1948
+ if (!exp) {
1949
+ throw new Error(`no such export ID: ${exportId}`);
1950
+ }
1951
+ if (!exp.pull) {
1952
+ let resolve = async () => {
1953
+ let hook = exp.hook;
1954
+ for (; ; ) {
1955
+ let payload = await hook.pull();
1956
+ if (payload.value instanceof RpcStub) {
1957
+ let { hook: inner, pathIfPromise } = unwrapStubAndPath(payload.value);
1958
+ if (pathIfPromise && pathIfPromise.length == 0) {
1959
+ if (this.getImport(hook) === void 0) {
1960
+ hook = inner;
1961
+ continue;
1962
+ }
1963
+ }
1964
+ }
1965
+ return payload;
1966
+ }
1967
+ };
1968
+ let autoRelease = exp.autoRelease;
1969
+ ++this.pullCount;
1970
+ exp.pull = resolve().then(
1971
+ (payload) => {
1972
+ let value = Devaluator.devaluate(payload.value, void 0, this, payload);
1973
+ this.send(["resolve", exportId, value]);
1974
+ if (autoRelease) this.releaseExport(exportId, 1);
1975
+ },
1976
+ (error) => {
1977
+ this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
1978
+ if (autoRelease) this.releaseExport(exportId, 1);
1979
+ }
1980
+ ).catch(
1981
+ (error) => {
1982
+ try {
1983
+ this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
1984
+ if (autoRelease) this.releaseExport(exportId, 1);
1985
+ } catch (error2) {
1986
+ this.abort(error2);
1987
+ }
1988
+ }
1989
+ ).finally(() => {
1990
+ if (--this.pullCount === 0) {
1991
+ if (this.onBatchDone) {
1992
+ this.onBatchDone.resolve();
1993
+ }
1994
+ }
1995
+ });
1996
+ }
1997
+ }
1998
+ getImport(hook) {
1999
+ if (hook instanceof RpcImportHook && hook.entry && hook.entry.session === this) {
2000
+ return hook.entry.importId;
2001
+ } else {
2002
+ return void 0;
2003
+ }
2004
+ }
2005
+ importStub(idx) {
2006
+ if (this.abortReason) throw this.abortReason;
2007
+ let entry = this.imports[idx];
2008
+ if (!entry) {
2009
+ entry = new ImportTableEntry(this, idx, false);
2010
+ this.imports[idx] = entry;
2011
+ }
2012
+ return new RpcImportHook(
2013
+ /*isPromise=*/
2014
+ false,
2015
+ entry
2016
+ );
2017
+ }
2018
+ importPromise(idx) {
2019
+ if (this.abortReason) throw this.abortReason;
2020
+ if (this.imports[idx]) {
2021
+ return new ErrorStubHook(new Error(
2022
+ "Bug in RPC system: The peer sent a promise reusing an existing export ID."
2023
+ ));
2024
+ }
2025
+ let entry = new ImportTableEntry(this, idx, true);
2026
+ this.imports[idx] = entry;
2027
+ return new RpcImportHook(
2028
+ /*isPromise=*/
2029
+ true,
2030
+ entry
2031
+ );
2032
+ }
2033
+ getExport(idx) {
2034
+ return this.exports[idx]?.hook;
2035
+ }
2036
+ getPipeReadable(exportId) {
2037
+ let entry = this.exports[exportId];
2038
+ if (!entry || !entry.pipeReadable) {
2039
+ throw new Error(`Export ${exportId} is not a pipe or its readable end was already consumed.`);
2040
+ }
2041
+ let readable = entry.pipeReadable;
2042
+ entry.pipeReadable = void 0;
2043
+ return readable;
2044
+ }
2045
+ createPipe(readable, readableHook) {
2046
+ if (this.abortReason) throw this.abortReason;
2047
+ this.send(["pipe"]);
2048
+ let importId = this.imports.length;
2049
+ let entry = new ImportTableEntry(this, importId, false);
2050
+ this.imports.push(entry);
2051
+ let hook = new RpcImportHook(
2052
+ /*isPromise=*/
2053
+ false,
2054
+ entry
2055
+ );
2056
+ let writable = streamImpl.createWritableStreamFromHook(hook);
2057
+ readable.pipeTo(writable).catch(() => {
2058
+ }).finally(() => readableHook.dispose());
2059
+ return importId;
2060
+ }
2061
+ // Serializes and sends a message. Returns the byte length of the serialized message.
2062
+ send(msg) {
2063
+ if (this.abortReason !== void 0) {
2064
+ return 0;
2065
+ }
2066
+ let msgText;
2067
+ try {
2068
+ msgText = JSON.stringify(msg);
2069
+ } catch (err) {
2070
+ try {
2071
+ this.abort(err);
2072
+ } catch (err2) {
2073
+ }
2074
+ throw err;
2075
+ }
2076
+ this.transport.send(msgText).catch((err) => this.abort(err, false));
2077
+ return msgText.length;
2078
+ }
2079
+ sendCall(id, path, args) {
2080
+ if (this.abortReason) throw this.abortReason;
2081
+ let value = ["pipeline", id, path];
2082
+ if (args) {
2083
+ let devalue = Devaluator.devaluate(args.value, void 0, this, args);
2084
+ value.push(devalue[0]);
2085
+ }
2086
+ this.send(["push", value]);
2087
+ let entry = new ImportTableEntry(this, this.imports.length, false);
2088
+ this.imports.push(entry);
2089
+ return new RpcImportHook(
2090
+ /*isPromise=*/
2091
+ true,
2092
+ entry
2093
+ );
2094
+ }
2095
+ sendStream(id, path, args) {
2096
+ if (this.abortReason) throw this.abortReason;
2097
+ let value = ["pipeline", id, path];
2098
+ let devalue = Devaluator.devaluate(args.value, void 0, this, args);
2099
+ value.push(devalue[0]);
2100
+ let size = this.send(["stream", value]);
2101
+ let importId = this.imports.length;
2102
+ let entry = new ImportTableEntry(
2103
+ this,
2104
+ importId,
2105
+ /*pulling=*/
2106
+ true
2107
+ );
2108
+ entry.remoteRefcount = 0;
2109
+ entry.localRefcount = 1;
2110
+ this.imports.push(entry);
2111
+ let promise = entry.awaitResolution().then(
2112
+ (p) => {
2113
+ p.dispose();
2114
+ delete this.imports[importId];
2115
+ },
2116
+ (err) => {
2117
+ delete this.imports[importId];
2118
+ throw err;
2119
+ }
2120
+ );
2121
+ return { promise, size };
2122
+ }
2123
+ sendMap(id, path, captures, instructions) {
2124
+ if (this.abortReason) {
2125
+ for (let cap of captures) {
2126
+ cap.dispose();
2127
+ }
2128
+ throw this.abortReason;
2129
+ }
2130
+ let devaluedCaptures = captures.map((hook) => {
2131
+ let importId = this.getImport(hook);
2132
+ if (importId !== void 0) {
2133
+ return ["import", importId];
2134
+ } else {
2135
+ return ["export", this.exportStub(hook)];
2136
+ }
2137
+ });
2138
+ let value = ["remap", id, path, devaluedCaptures, instructions];
2139
+ this.send(["push", value]);
2140
+ let entry = new ImportTableEntry(this, this.imports.length, false);
2141
+ this.imports.push(entry);
2142
+ return new RpcImportHook(
2143
+ /*isPromise=*/
2144
+ true,
2145
+ entry
2146
+ );
2147
+ }
2148
+ sendPull(id) {
2149
+ if (this.abortReason) throw this.abortReason;
2150
+ this.send(["pull", id]);
2151
+ }
2152
+ sendRelease(id, remoteRefcount) {
2153
+ if (this.abortReason) return;
2154
+ this.send(["release", id, remoteRefcount]);
2155
+ delete this.imports[id];
2156
+ }
2157
+ abort(error, trySendAbortMessage = true) {
2158
+ if (this.abortReason !== void 0) return;
2159
+ this.cancelReadLoop(error);
2160
+ if (trySendAbortMessage) {
2161
+ try {
2162
+ this.transport.send(JSON.stringify(["abort", Devaluator.devaluate(error, void 0, this)])).catch((err) => {
2163
+ });
2164
+ } catch (err) {
2165
+ }
2166
+ }
2167
+ if (error === void 0) {
2168
+ error = "undefined";
2169
+ }
2170
+ this.abortReason = error;
2171
+ if (this.onBatchDone) {
2172
+ this.onBatchDone.reject(error);
2173
+ }
2174
+ if (this.transport.abort) {
2175
+ try {
2176
+ this.transport.abort(error);
2177
+ } catch (err) {
2178
+ Promise.resolve(err);
2179
+ }
2180
+ }
2181
+ for (let i in this.onBrokenCallbacks) {
2182
+ try {
2183
+ this.onBrokenCallbacks[i](error);
2184
+ } catch (err) {
2185
+ Promise.resolve(err);
2186
+ }
2187
+ }
2188
+ for (let i in this.imports) {
2189
+ this.imports[i].abort(error);
2190
+ }
2191
+ for (let i in this.exports) {
2192
+ this.exports[i].hook.dispose();
2193
+ }
2194
+ }
2195
+ async readLoop(abortPromise) {
2196
+ while (!this.abortReason) {
2197
+ let msg = JSON.parse(await Promise.race([this.transport.receive(), abortPromise]));
2198
+ if (this.abortReason) break;
2199
+ if (msg instanceof Array) {
2200
+ switch (msg[0]) {
2201
+ case "push":
2202
+ if (msg.length > 1) {
2203
+ let payload = new Evaluator(this).evaluate(msg[1]);
2204
+ let hook = new PayloadStubHook(payload);
2205
+ hook.ignoreUnhandledRejections();
2206
+ this.exports.push({ hook, refcount: 1 });
2207
+ continue;
2208
+ }
2209
+ break;
2210
+ case "stream": {
2211
+ if (msg.length > 1) {
2212
+ let payload = new Evaluator(this).evaluate(msg[1]);
2213
+ let hook = new PayloadStubHook(payload);
2214
+ hook.ignoreUnhandledRejections();
2215
+ let exportId = this.exports.length;
2216
+ this.exports.push({ hook, refcount: 1, autoRelease: true });
2217
+ this.ensureResolvingExport(exportId);
2218
+ continue;
2219
+ }
2220
+ break;
2221
+ }
2222
+ case "pipe": {
2223
+ let { readable, writable } = new TransformStream();
2224
+ let hook = streamImpl.createWritableStreamHook(writable);
2225
+ this.exports.push({ hook, refcount: 1, pipeReadable: readable });
2226
+ continue;
2227
+ }
2228
+ case "pull": {
2229
+ let exportId = msg[1];
2230
+ if (typeof exportId == "number") {
2231
+ this.ensureResolvingExport(exportId);
2232
+ continue;
2233
+ }
2234
+ break;
2235
+ }
2236
+ case "resolve":
2237
+ // ["resolve", ExportId, Expression]
2238
+ case "reject": {
2239
+ let importId = msg[1];
2240
+ if (typeof importId == "number" && msg.length > 2) {
2241
+ let imp = this.imports[importId];
2242
+ if (imp) {
2243
+ if (msg[0] == "resolve") {
2244
+ imp.resolve(new PayloadStubHook(new Evaluator(this).evaluate(msg[2])));
2245
+ } else {
2246
+ let payload = new Evaluator(this).evaluate(msg[2]);
2247
+ payload.dispose();
2248
+ imp.resolve(new ErrorStubHook(payload.value));
2249
+ }
2250
+ } else {
2251
+ if (msg[0] == "resolve") {
2252
+ new Evaluator(this).evaluate(msg[2]).dispose();
2253
+ }
2254
+ }
2255
+ continue;
2256
+ }
2257
+ break;
2258
+ }
2259
+ case "release": {
2260
+ let exportId = msg[1];
2261
+ let refcount = msg[2];
2262
+ if (typeof exportId == "number" && typeof refcount == "number") {
2263
+ this.releaseExport(exportId, refcount);
2264
+ continue;
2265
+ }
2266
+ break;
2267
+ }
2268
+ case "abort": {
2269
+ let payload = new Evaluator(this).evaluate(msg[1]);
2270
+ payload.dispose();
2271
+ this.abort(payload, false);
2272
+ break;
2273
+ }
2274
+ }
2275
+ }
2276
+ throw new Error(`bad RPC message: ${JSON.stringify(msg)}`);
2277
+ }
2278
+ }
2279
+ async drain() {
2280
+ if (this.abortReason) {
2281
+ throw this.abortReason;
2282
+ }
2283
+ if (this.pullCount > 0) {
2284
+ let { promise, resolve, reject } = Promise.withResolvers();
2285
+ this.onBatchDone = { resolve, reject };
2286
+ await promise;
2287
+ }
2288
+ }
2289
+ getStats() {
2290
+ let result = { imports: 0, exports: 0 };
2291
+ for (let i in this.imports) {
2292
+ ++result.imports;
2293
+ }
2294
+ for (let i in this.exports) {
2295
+ ++result.exports;
2296
+ }
2297
+ return result;
2298
+ }
2299
+ };
2300
+ var RpcSession = class {
2301
+ #session;
2302
+ #mainStub;
2303
+ constructor(transport, localMain, options = {}) {
2304
+ let mainHook;
2305
+ if (localMain) {
2306
+ mainHook = new PayloadStubHook(RpcPayload.fromAppReturn(localMain));
2307
+ } else {
2308
+ mainHook = new ErrorStubHook(new Error("This connection has no main object."));
2309
+ }
2310
+ this.#session = new RpcSessionImpl(transport, mainHook, options);
2311
+ this.#mainStub = new RpcStub(this.#session.getMainImport());
2312
+ }
2313
+ getRemoteMain() {
2314
+ return this.#mainStub;
2315
+ }
2316
+ getStats() {
2317
+ return this.#session.getStats();
2318
+ }
2319
+ drain() {
2320
+ return this.#session.drain();
2321
+ }
2322
+ };
2323
+
2324
+ // src/websocket.ts
2325
+ function newWebSocketRpcSession(webSocket, localMain, options) {
2326
+ if (typeof webSocket === "string") {
2327
+ webSocket = new WebSocket(webSocket);
2328
+ }
2329
+ let transport = new WebSocketTransport(webSocket);
2330
+ let rpc = new RpcSession(transport, localMain, options);
2331
+ return rpc.getRemoteMain();
2332
+ }
2333
+ function newWorkersWebSocketRpcResponse(request, localMain, options) {
2334
+ if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") {
2335
+ return new Response("This endpoint only accepts WebSocket requests.", { status: 400 });
2336
+ }
2337
+ let pair = new WebSocketPair();
2338
+ let server = pair[0];
2339
+ server.accept();
2340
+ newWebSocketRpcSession(server, localMain, options);
2341
+ return new Response(null, {
2342
+ status: 101,
2343
+ webSocket: pair[1]
2344
+ });
2345
+ }
2346
+ var WebSocketTransport = class {
2347
+ constructor(webSocket) {
2348
+ this.#webSocket = webSocket;
2349
+ if (webSocket.readyState === WebSocket.CONNECTING) {
2350
+ this.#sendQueue = [];
2351
+ webSocket.addEventListener("open", (event) => {
2352
+ try {
2353
+ for (let message of this.#sendQueue) {
2354
+ webSocket.send(message);
2355
+ }
2356
+ } catch (err) {
2357
+ this.#receivedError(err);
2358
+ }
2359
+ this.#sendQueue = void 0;
2360
+ });
2361
+ }
2362
+ webSocket.addEventListener("message", (event) => {
2363
+ if (this.#error) ; else if (typeof event.data === "string") {
2364
+ if (this.#receiveResolver) {
2365
+ this.#receiveResolver(event.data);
2366
+ this.#receiveResolver = void 0;
2367
+ this.#receiveRejecter = void 0;
2368
+ } else {
2369
+ this.#receiveQueue.push(event.data);
2370
+ }
2371
+ } else {
2372
+ this.#receivedError(new TypeError("Received non-string message from WebSocket."));
2373
+ }
2374
+ });
2375
+ webSocket.addEventListener("close", (event) => {
2376
+ this.#receivedError(new Error(`Peer closed WebSocket: ${event.code} ${event.reason}`));
2377
+ });
2378
+ webSocket.addEventListener("error", (event) => {
2379
+ this.#receivedError(new Error(`WebSocket connection failed.`));
2380
+ });
2381
+ }
2382
+ #webSocket;
2383
+ #sendQueue;
2384
+ // only if not opened yet
2385
+ #receiveResolver;
2386
+ #receiveRejecter;
2387
+ #receiveQueue = [];
2388
+ #error;
2389
+ async send(message) {
2390
+ if (this.#sendQueue === void 0) {
2391
+ this.#webSocket.send(message);
2392
+ } else {
2393
+ this.#sendQueue.push(message);
2394
+ }
2395
+ }
2396
+ async receive() {
2397
+ if (this.#receiveQueue.length > 0) {
2398
+ return this.#receiveQueue.shift();
2399
+ } else if (this.#error) {
2400
+ throw this.#error;
2401
+ } else {
2402
+ return new Promise((resolve, reject) => {
2403
+ this.#receiveResolver = resolve;
2404
+ this.#receiveRejecter = reject;
2405
+ });
2406
+ }
2407
+ }
2408
+ abort(reason) {
2409
+ let message;
2410
+ if (reason instanceof Error) {
2411
+ message = reason.message;
2412
+ } else {
2413
+ message = `${reason}`;
2414
+ }
2415
+ this.#webSocket.close(3e3, message);
2416
+ if (!this.#error) {
2417
+ this.#error = reason;
2418
+ }
2419
+ }
2420
+ #receivedError(reason) {
2421
+ if (!this.#error) {
2422
+ this.#error = reason;
2423
+ if (this.#receiveRejecter) {
2424
+ this.#receiveRejecter(reason);
2425
+ this.#receiveResolver = void 0;
2426
+ this.#receiveRejecter = void 0;
2427
+ }
2428
+ }
2429
+ }
2430
+ };
2431
+
2432
+ // src/batch.ts
2433
+ var BatchClientTransport = class {
2434
+ constructor(sendBatch) {
2435
+ this.#promise = this.#scheduleBatch(sendBatch);
2436
+ }
2437
+ #promise;
2438
+ #aborted;
2439
+ #batchToSend = [];
2440
+ #batchToReceive = null;
2441
+ async send(message) {
2442
+ if (this.#batchToSend !== null) {
2443
+ this.#batchToSend.push(message);
2444
+ }
2445
+ }
2446
+ async receive() {
2447
+ if (!this.#batchToReceive) {
2448
+ await this.#promise;
2449
+ }
2450
+ let msg = this.#batchToReceive.shift();
2451
+ if (msg !== void 0) {
2452
+ return msg;
2453
+ } else {
2454
+ throw new Error("Batch RPC request ended.");
2455
+ }
2456
+ }
2457
+ abort(reason) {
2458
+ this.#aborted = reason;
2459
+ }
2460
+ async #scheduleBatch(sendBatch) {
2461
+ await new Promise((resolve) => setTimeout(resolve, 0));
2462
+ if (this.#aborted !== void 0) {
2463
+ throw this.#aborted;
2464
+ }
2465
+ let batch = this.#batchToSend;
2466
+ this.#batchToSend = null;
2467
+ this.#batchToReceive = await sendBatch(batch);
2468
+ }
2469
+ };
2470
+ function newHttpBatchRpcSession(urlOrRequest, options) {
2471
+ let sendBatch = async (batch) => {
2472
+ let response = await fetch(urlOrRequest, {
2473
+ method: "POST",
2474
+ body: batch.join("\n")
2475
+ });
2476
+ if (!response.ok) {
2477
+ response.body?.cancel();
2478
+ throw new Error(`RPC request failed: ${response.status} ${response.statusText}`);
2479
+ }
2480
+ let body = await response.text();
2481
+ return body == "" ? [] : body.split("\n");
2482
+ };
2483
+ let transport = new BatchClientTransport(sendBatch);
2484
+ let rpc = new RpcSession(transport, void 0, options);
2485
+ return rpc.getRemoteMain();
2486
+ }
2487
+ var BatchServerTransport = class {
2488
+ constructor(batch) {
2489
+ this.#batchToReceive = batch;
2490
+ }
2491
+ #batchToSend = [];
2492
+ #batchToReceive;
2493
+ #allReceived = Promise.withResolvers();
2494
+ async send(message) {
2495
+ this.#batchToSend.push(message);
2496
+ }
2497
+ async receive() {
2498
+ let msg = this.#batchToReceive.shift();
2499
+ if (msg !== void 0) {
2500
+ return msg;
2501
+ } else {
2502
+ this.#allReceived.resolve();
2503
+ return new Promise((r) => {
2504
+ });
2505
+ }
2506
+ }
2507
+ abort(reason) {
2508
+ this.#allReceived.reject(reason);
2509
+ }
2510
+ whenAllReceived() {
2511
+ return this.#allReceived.promise;
2512
+ }
2513
+ getResponseBody() {
2514
+ return this.#batchToSend.join("\n");
2515
+ }
2516
+ };
2517
+ async function newHttpBatchRpcResponse(request, localMain, options) {
2518
+ if (request.method !== "POST") {
2519
+ return new Response("This endpoint only accepts POST requests.", { status: 405 });
2520
+ }
2521
+ let body = await request.text();
2522
+ let batch = body === "" ? [] : body.split("\n");
2523
+ let transport = new BatchServerTransport(batch);
2524
+ let rpc = new RpcSession(transport, localMain, options);
2525
+ await transport.whenAllReceived();
2526
+ await rpc.drain();
2527
+ return new Response(transport.getResponseBody());
2528
+ }
2529
+ async function nodeHttpBatchRpcResponse(request, response, localMain, options) {
2530
+ if (request.method !== "POST") {
2531
+ response.writeHead(405, "This endpoint only accepts POST requests.");
2532
+ }
2533
+ let body = await new Promise((resolve, reject) => {
2534
+ let chunks = [];
2535
+ request.on("data", (chunk) => {
2536
+ chunks.push(chunk);
2537
+ });
2538
+ request.on("end", () => {
2539
+ resolve(Buffer.concat(chunks).toString());
2540
+ });
2541
+ request.on("error", reject);
2542
+ });
2543
+ let batch = body === "" ? [] : body.split("\n");
2544
+ let transport = new BatchServerTransport(batch);
2545
+ let rpc = new RpcSession(transport, localMain, options);
2546
+ await transport.whenAllReceived();
2547
+ await rpc.drain();
2548
+ response.writeHead(200, options?.headers);
2549
+ response.end(transport.getResponseBody());
2550
+ }
2551
+
2552
+ // src/messageport.ts
2553
+ function newMessagePortRpcSession(port, localMain, options) {
2554
+ let transport = new MessagePortTransport(port);
2555
+ let rpc = new RpcSession(transport, localMain, options);
2556
+ return rpc.getRemoteMain();
2557
+ }
2558
+ var MessagePortTransport = class {
2559
+ constructor(port) {
2560
+ this.#port = port;
2561
+ port.start();
2562
+ port.addEventListener("message", (event) => {
2563
+ if (this.#error) ; else if (event.data === null) {
2564
+ this.#receivedError(new Error("Peer closed MessagePort connection."));
2565
+ } else if (typeof event.data === "string") {
2566
+ if (this.#receiveResolver) {
2567
+ this.#receiveResolver(event.data);
2568
+ this.#receiveResolver = void 0;
2569
+ this.#receiveRejecter = void 0;
2570
+ } else {
2571
+ this.#receiveQueue.push(event.data);
2572
+ }
2573
+ } else {
2574
+ this.#receivedError(new TypeError("Received non-string message from MessagePort."));
2575
+ }
2576
+ });
2577
+ port.addEventListener("messageerror", (event) => {
2578
+ this.#receivedError(new Error("MessagePort message error."));
2579
+ });
2580
+ }
2581
+ #port;
2582
+ #receiveResolver;
2583
+ #receiveRejecter;
2584
+ #receiveQueue = [];
2585
+ #error;
2586
+ async send(message) {
2587
+ if (this.#error) {
2588
+ throw this.#error;
2589
+ }
2590
+ this.#port.postMessage(message);
2591
+ }
2592
+ async receive() {
2593
+ if (this.#receiveQueue.length > 0) {
2594
+ return this.#receiveQueue.shift();
2595
+ } else if (this.#error) {
2596
+ throw this.#error;
2597
+ } else {
2598
+ return new Promise((resolve, reject) => {
2599
+ this.#receiveResolver = resolve;
2600
+ this.#receiveRejecter = reject;
2601
+ });
2602
+ }
2603
+ }
2604
+ abort(reason) {
2605
+ try {
2606
+ this.#port.postMessage(null);
2607
+ } catch (err) {
2608
+ }
2609
+ this.#port.close();
2610
+ if (!this.#error) {
2611
+ this.#error = reason;
2612
+ }
2613
+ }
2614
+ #receivedError(reason) {
2615
+ if (!this.#error) {
2616
+ this.#error = reason;
2617
+ if (this.#receiveRejecter) {
2618
+ this.#receiveRejecter(reason);
2619
+ this.#receiveResolver = void 0;
2620
+ this.#receiveRejecter = void 0;
2621
+ }
2622
+ }
2623
+ }
2624
+ };
2625
+
2626
+ // src/map.ts
2627
+ var currentMapBuilder;
2628
+ var MapBuilder = class {
2629
+ context;
2630
+ captureMap = /* @__PURE__ */ new Map();
2631
+ instructions = [];
2632
+ constructor(subject, path) {
2633
+ if (currentMapBuilder) {
2634
+ this.context = {
2635
+ parent: currentMapBuilder,
2636
+ captures: [],
2637
+ subject: currentMapBuilder.capture(subject),
2638
+ path
2639
+ };
2640
+ } else {
2641
+ this.context = {
2642
+ parent: void 0,
2643
+ captures: [],
2644
+ subject,
2645
+ path
2646
+ };
2647
+ }
2648
+ currentMapBuilder = this;
2649
+ }
2650
+ unregister() {
2651
+ currentMapBuilder = this.context.parent;
2652
+ }
2653
+ makeInput() {
2654
+ return new MapVariableHook(this, 0);
2655
+ }
2656
+ makeOutput(result) {
2657
+ let devalued;
2658
+ try {
2659
+ devalued = Devaluator.devaluate(result.value, void 0, this, result);
2660
+ } finally {
2661
+ result.dispose();
2662
+ }
2663
+ this.instructions.push(devalued);
2664
+ if (this.context.parent) {
2665
+ this.context.parent.instructions.push(
2666
+ [
2667
+ "remap",
2668
+ this.context.subject,
2669
+ this.context.path,
2670
+ this.context.captures.map((cap) => ["import", cap]),
2671
+ this.instructions
2672
+ ]
2673
+ );
2674
+ return new MapVariableHook(this.context.parent, this.context.parent.instructions.length);
2675
+ } else {
2676
+ return this.context.subject.map(this.context.path, this.context.captures, this.instructions);
2677
+ }
2678
+ }
2679
+ pushCall(hook, path, params) {
2680
+ let devalued = Devaluator.devaluate(params.value, void 0, this, params);
2681
+ devalued = devalued[0];
2682
+ let subject = this.capture(hook.dup());
2683
+ this.instructions.push(["pipeline", subject, path, devalued]);
2684
+ return new MapVariableHook(this, this.instructions.length);
2685
+ }
2686
+ pushGet(hook, path) {
2687
+ let subject = this.capture(hook.dup());
2688
+ this.instructions.push(["pipeline", subject, path]);
2689
+ return new MapVariableHook(this, this.instructions.length);
2690
+ }
2691
+ capture(hook) {
2692
+ if (hook instanceof MapVariableHook && hook.mapper === this) {
2693
+ return hook.idx;
2694
+ }
2695
+ let result = this.captureMap.get(hook);
2696
+ if (result === void 0) {
2697
+ if (this.context.parent) {
2698
+ let parentIdx = this.context.parent.capture(hook);
2699
+ this.context.captures.push(parentIdx);
2700
+ } else {
2701
+ this.context.captures.push(hook);
2702
+ }
2703
+ result = -this.context.captures.length;
2704
+ this.captureMap.set(hook, result);
2705
+ }
2706
+ return result;
2707
+ }
2708
+ // ---------------------------------------------------------------------------
2709
+ // implements Exporter
2710
+ exportStub(hook) {
2711
+ throw new Error(
2712
+ "Can't construct an RpcTarget or RPC callback inside a mapper function. Try creating a new RpcStub outside the callback first, then using it inside the callback."
2713
+ );
2714
+ }
2715
+ exportPromise(hook) {
2716
+ return this.exportStub(hook);
2717
+ }
2718
+ getImport(hook) {
2719
+ return this.capture(hook);
2720
+ }
2721
+ unexport(ids) {
2722
+ }
2723
+ createPipe(readable) {
2724
+ throw new Error("Cannot send ReadableStream inside a mapper function.");
2725
+ }
2726
+ onSendError(error) {
2727
+ }
2728
+ };
2729
+ mapImpl.sendMap = (hook, path, func) => {
2730
+ let builder = new MapBuilder(hook, path);
2731
+ let result;
2732
+ try {
2733
+ result = RpcPayload.fromAppReturn(withCallInterceptor(builder.pushCall.bind(builder), () => {
2734
+ return func(new RpcPromise(builder.makeInput(), []));
2735
+ }));
2736
+ } finally {
2737
+ builder.unregister();
2738
+ }
2739
+ if (result instanceof Promise) {
2740
+ result.catch((err) => {
2741
+ });
2742
+ throw new Error("RPC map() callbacks cannot be async.");
2743
+ }
2744
+ return new RpcPromise(builder.makeOutput(result), []);
2745
+ };
2746
+ function throwMapperBuilderUseError() {
2747
+ throw new Error(
2748
+ "Attempted to use an abstract placeholder from a mapper function. Please make sure your map function has no side effects."
2749
+ );
2750
+ }
2751
+ var MapVariableHook = class extends StubHook {
2752
+ constructor(mapper, idx) {
2753
+ super();
2754
+ this.mapper = mapper;
2755
+ this.idx = idx;
2756
+ }
2757
+ // We don't have anything we actually need to dispose, so dup() can just return the same hook.
2758
+ dup() {
2759
+ return this;
2760
+ }
2761
+ dispose() {
2762
+ }
2763
+ get(path) {
2764
+ if (path.length == 0) {
2765
+ return this;
2766
+ } else if (currentMapBuilder) {
2767
+ return currentMapBuilder.pushGet(this, path);
2768
+ } else {
2769
+ throwMapperBuilderUseError();
2770
+ }
2771
+ }
2772
+ // Other methods should never be called.
2773
+ call(path, args) {
2774
+ throwMapperBuilderUseError();
2775
+ }
2776
+ map(path, captures, instructions) {
2777
+ throwMapperBuilderUseError();
2778
+ }
2779
+ pull() {
2780
+ throwMapperBuilderUseError();
2781
+ }
2782
+ ignoreUnhandledRejections() {
2783
+ }
2784
+ onBroken(callback) {
2785
+ throwMapperBuilderUseError();
2786
+ }
2787
+ };
2788
+ var MapApplicator = class {
2789
+ constructor(captures, input) {
2790
+ this.captures = captures;
2791
+ this.variables = [input];
2792
+ }
2793
+ variables;
2794
+ dispose() {
2795
+ for (let variable of this.variables) {
2796
+ variable.dispose();
2797
+ }
2798
+ }
2799
+ apply(instructions) {
2800
+ try {
2801
+ if (instructions.length < 1) {
2802
+ throw new Error("Invalid empty mapper function.");
2803
+ }
2804
+ for (let instruction of instructions.slice(0, -1)) {
2805
+ let payload = new Evaluator(this).evaluateCopy(instruction);
2806
+ if (payload.value instanceof RpcStub) {
2807
+ let hook = unwrapStubNoProperties(payload.value);
2808
+ if (hook) {
2809
+ this.variables.push(hook);
2810
+ continue;
2811
+ }
2812
+ }
2813
+ this.variables.push(new PayloadStubHook(payload));
2814
+ }
2815
+ return new Evaluator(this).evaluateCopy(instructions[instructions.length - 1]);
2816
+ } finally {
2817
+ for (let variable of this.variables) {
2818
+ variable.dispose();
2819
+ }
2820
+ }
2821
+ }
2822
+ importStub(idx) {
2823
+ throw new Error("A mapper function cannot refer to exports.");
2824
+ }
2825
+ importPromise(idx) {
2826
+ return this.importStub(idx);
2827
+ }
2828
+ getExport(idx) {
2829
+ if (idx < 0) {
2830
+ return this.captures[-idx - 1];
2831
+ } else {
2832
+ return this.variables[idx];
2833
+ }
2834
+ }
2835
+ getPipeReadable(exportId) {
2836
+ throw new Error("A mapper function cannot use pipe readables.");
2837
+ }
2838
+ };
2839
+ function applyMapToElement(input, parent, owner, captures, instructions) {
2840
+ let inputHook = new PayloadStubHook(RpcPayload.deepCopyFrom(input, parent, owner));
2841
+ let mapper = new MapApplicator(captures, inputHook);
2842
+ try {
2843
+ return mapper.apply(instructions);
2844
+ } finally {
2845
+ mapper.dispose();
2846
+ }
2847
+ }
2848
+ mapImpl.applyMap = (input, parent, owner, captures, instructions) => {
2849
+ try {
2850
+ let result;
2851
+ if (input instanceof RpcPromise) {
2852
+ throw new Error("applyMap() can't be called on RpcPromise");
2853
+ } else if (input instanceof Array) {
2854
+ let payloads = [];
2855
+ try {
2856
+ for (let elem of input) {
2857
+ payloads.push(applyMapToElement(elem, input, owner, captures, instructions));
2858
+ }
2859
+ } catch (err) {
2860
+ for (let payload of payloads) {
2861
+ payload.dispose();
2862
+ }
2863
+ throw err;
2864
+ }
2865
+ result = RpcPayload.fromArray(payloads);
2866
+ } else if (input === null || input === void 0) {
2867
+ result = RpcPayload.fromAppReturn(input);
2868
+ } else {
2869
+ result = applyMapToElement(input, parent, owner, captures, instructions);
2870
+ }
2871
+ return new PayloadStubHook(result);
2872
+ } finally {
2873
+ for (let cap of captures) {
2874
+ cap.dispose();
2875
+ }
2876
+ }
2877
+ };
2878
+
2879
+ // src/streams.ts
2880
+ var WritableStreamStubHook = class _WritableStreamStubHook extends StubHook {
2881
+ state;
2882
+ // undefined when disposed
2883
+ // Creates a new WritableStreamStubHook that is not duplicated from an existing hook.
2884
+ static create(stream) {
2885
+ let writer = stream.getWriter();
2886
+ return new _WritableStreamStubHook({ refcount: 1, writer, closed: false });
2887
+ }
2888
+ constructor(state, dupFrom) {
2889
+ super();
2890
+ this.state = state;
2891
+ if (dupFrom) {
2892
+ ++state.refcount;
2893
+ }
2894
+ }
2895
+ getState() {
2896
+ if (this.state) {
2897
+ return this.state;
2898
+ } else {
2899
+ throw new Error("Attempted to use a WritableStreamStubHook after it was disposed.");
2900
+ }
2901
+ }
2902
+ call(path, args) {
2903
+ try {
2904
+ let state = this.getState();
2905
+ if (path.length !== 1 || typeof path[0] !== "string") {
2906
+ throw new Error("WritableStream stub only supports direct method calls");
2907
+ }
2908
+ const method = path[0];
2909
+ if (method !== "write" && method !== "close" && method !== "abort") {
2910
+ args.dispose();
2911
+ throw new Error(`Unknown WritableStream method: ${method}`);
2912
+ }
2913
+ if (method === "close" || method === "abort") {
2914
+ state.closed = true;
2915
+ }
2916
+ let func = state.writer[method];
2917
+ let promise = args.deliverCall(func, state.writer);
2918
+ return new PromiseStubHook(promise.then((payload) => new PayloadStubHook(payload)));
2919
+ } catch (err) {
2920
+ return new ErrorStubHook(err);
2921
+ }
2922
+ }
2923
+ map(path, captures, instructions) {
2924
+ for (let cap of captures) {
2925
+ cap.dispose();
2926
+ }
2927
+ return new ErrorStubHook(new Error("Cannot use map() on a WritableStream"));
2928
+ }
2929
+ get(path) {
2930
+ return new ErrorStubHook(new Error("Cannot access properties on a WritableStream stub"));
2931
+ }
2932
+ dup() {
2933
+ let state = this.getState();
2934
+ return new _WritableStreamStubHook(state, this);
2935
+ }
2936
+ pull() {
2937
+ return Promise.reject(new Error("Cannot pull a WritableStream stub"));
2938
+ }
2939
+ ignoreUnhandledRejections() {
2940
+ }
2941
+ dispose() {
2942
+ let state = this.state;
2943
+ this.state = void 0;
2944
+ if (state) {
2945
+ if (--state.refcount === 0) {
2946
+ if (!state.closed) {
2947
+ state.writer.abort(new Error("WritableStream RPC stub was disposed without calling close()")).catch(() => {
2948
+ });
2949
+ }
2950
+ state.writer.releaseLock();
2951
+ }
2952
+ }
2953
+ }
2954
+ onBroken(callback) {
2955
+ }
2956
+ };
2957
+ var INITIAL_WINDOW = 256 * 1024;
2958
+ var MAX_WINDOW = 1024 * 1024 * 1024;
2959
+ var MIN_WINDOW = 64 * 1024;
2960
+ var STARTUP_GROWTH_FACTOR = 2;
2961
+ var STEADY_GROWTH_FACTOR = 1.25;
2962
+ var DECAY_FACTOR = 0.9;
2963
+ var STARTUP_EXIT_ROUNDS = 3;
2964
+ var FlowController = class {
2965
+ constructor(now) {
2966
+ this.now = now;
2967
+ }
2968
+ // The current window size in bytes. The sender blocks when bytesInFlight >= window.
2969
+ window = INITIAL_WINDOW;
2970
+ // Total bytes currently in flight (sent but not yet acked).
2971
+ bytesInFlight = 0;
2972
+ // Whether we're still in the startup phase.
2973
+ inStartupPhase = true;
2974
+ // ----- BDP estimation state (private) -----
2975
+ // Total bytes acked so far.
2976
+ delivered = 0;
2977
+ // Time of most recent ack.
2978
+ deliveredTime = 0;
2979
+ // Time when the very first ack was received.
2980
+ firstAckTime = 0;
2981
+ firstAckDelivered = 0;
2982
+ // Global minimum RTT observed (milliseconds).
2983
+ minRtt = Infinity;
2984
+ // For startup exit: count of consecutive RTT rounds where the window didn't meaningfully grow.
2985
+ roundsWithoutIncrease = 0;
2986
+ // Window size at the start of the current round, for startup exit detection.
2987
+ lastRoundWindow = 0;
2988
+ // Time when the current round started.
2989
+ roundStartTime = 0;
2990
+ // Called when a write of `size` bytes is about to be sent. Returns a token that must be
2991
+ // passed to onAck() when the ack arrives, and whether the sender should block (window full).
2992
+ onSend(size) {
2993
+ this.bytesInFlight += size;
2994
+ let token = {
2995
+ sentTime: this.now(),
2996
+ size,
2997
+ deliveredAtSend: this.delivered,
2998
+ deliveredTimeAtSend: this.deliveredTime,
2999
+ windowAtSend: this.window,
3000
+ windowFullAtSend: this.bytesInFlight >= this.window
3001
+ };
3002
+ return { token, shouldBlock: token.windowFullAtSend };
3003
+ }
3004
+ // Called when a previously-sent write fails. Restores bytesInFlight without updating
3005
+ // any BDP estimates.
3006
+ onError(token) {
3007
+ this.bytesInFlight -= token.size;
3008
+ }
3009
+ // Called when an ack is received for a previously-sent write. Updates BDP estimates and
3010
+ // the window. Returns whether a blocked sender should now unblock.
3011
+ onAck(token) {
3012
+ let ackTime = this.now();
3013
+ this.delivered += token.size;
3014
+ this.deliveredTime = ackTime;
3015
+ this.bytesInFlight -= token.size;
3016
+ let rtt = ackTime - token.sentTime;
3017
+ this.minRtt = Math.min(this.minRtt, rtt);
3018
+ if (this.firstAckTime === 0) {
3019
+ this.firstAckTime = ackTime;
3020
+ this.firstAckDelivered = this.delivered;
3021
+ } else {
3022
+ let baseTime;
3023
+ let baseDelivered;
3024
+ if (token.deliveredTimeAtSend === 0) {
3025
+ baseTime = this.firstAckTime;
3026
+ baseDelivered = this.firstAckDelivered;
3027
+ } else {
3028
+ baseTime = token.deliveredTimeAtSend;
3029
+ baseDelivered = token.deliveredAtSend;
3030
+ }
3031
+ let interval = ackTime - baseTime;
3032
+ let bytes = this.delivered - baseDelivered;
3033
+ let bandwidth = bytes / interval;
3034
+ let growthFactor = this.inStartupPhase ? STARTUP_GROWTH_FACTOR : STEADY_GROWTH_FACTOR;
3035
+ let newWindow = bandwidth * this.minRtt * growthFactor;
3036
+ newWindow = Math.min(newWindow, token.windowAtSend * growthFactor);
3037
+ if (token.windowFullAtSend) {
3038
+ newWindow = Math.max(newWindow, token.windowAtSend * DECAY_FACTOR);
3039
+ } else {
3040
+ newWindow = Math.max(newWindow, this.window);
3041
+ }
3042
+ this.window = Math.max(Math.min(newWindow, MAX_WINDOW), MIN_WINDOW);
3043
+ if (this.inStartupPhase && token.sentTime >= this.roundStartTime) {
3044
+ if (this.window > this.lastRoundWindow * STEADY_GROWTH_FACTOR) {
3045
+ this.roundsWithoutIncrease = 0;
3046
+ } else {
3047
+ if (++this.roundsWithoutIncrease >= STARTUP_EXIT_ROUNDS) {
3048
+ this.inStartupPhase = false;
3049
+ }
3050
+ }
3051
+ this.roundStartTime = ackTime;
3052
+ this.lastRoundWindow = this.window;
3053
+ }
3054
+ }
3055
+ return this.bytesInFlight < this.window;
3056
+ }
3057
+ };
3058
+ function createWritableStreamFromHook(hook) {
3059
+ let pendingError = void 0;
3060
+ let hookDisposed = false;
3061
+ let fc = new FlowController(() => performance.now());
3062
+ let windowResolve;
3063
+ let windowReject;
3064
+ const disposeHook = () => {
3065
+ if (!hookDisposed) {
3066
+ hookDisposed = true;
3067
+ hook.dispose();
3068
+ }
3069
+ };
3070
+ return new WritableStream({
3071
+ write(chunk, controller) {
3072
+ if (pendingError !== void 0) {
3073
+ throw pendingError;
3074
+ }
3075
+ const payload = RpcPayload.fromAppParams([chunk]);
3076
+ const { promise, size } = hook.stream(["write"], payload);
3077
+ if (size === void 0) {
3078
+ return promise.catch((err) => {
3079
+ if (pendingError === void 0) {
3080
+ pendingError = err;
3081
+ }
3082
+ throw err;
3083
+ });
3084
+ } else {
3085
+ let { token, shouldBlock } = fc.onSend(size);
3086
+ promise.then(() => {
3087
+ let hasCapacity = fc.onAck(token);
3088
+ if (hasCapacity && windowResolve) {
3089
+ windowResolve();
3090
+ windowResolve = void 0;
3091
+ windowReject = void 0;
3092
+ }
3093
+ }, (err) => {
3094
+ fc.onError(token);
3095
+ if (pendingError === void 0) {
3096
+ pendingError = err;
3097
+ controller.error(err);
3098
+ disposeHook();
3099
+ }
3100
+ if (windowReject) {
3101
+ windowReject(err);
3102
+ windowResolve = void 0;
3103
+ windowReject = void 0;
3104
+ }
3105
+ });
3106
+ if (shouldBlock) {
3107
+ return new Promise((resolve, reject) => {
3108
+ windowResolve = resolve;
3109
+ windowReject = reject;
3110
+ });
3111
+ }
3112
+ }
3113
+ },
3114
+ async close() {
3115
+ if (pendingError !== void 0) {
3116
+ disposeHook();
3117
+ throw pendingError;
3118
+ }
3119
+ const { promise } = hook.stream(["close"], RpcPayload.fromAppParams([]));
3120
+ try {
3121
+ await promise;
3122
+ } catch (err) {
3123
+ throw pendingError ?? err;
3124
+ } finally {
3125
+ disposeHook();
3126
+ }
3127
+ },
3128
+ abort(reason) {
3129
+ if (pendingError !== void 0) {
3130
+ return;
3131
+ }
3132
+ pendingError = reason ?? new Error("WritableStream was aborted");
3133
+ if (windowReject) {
3134
+ windowReject(pendingError);
3135
+ windowResolve = void 0;
3136
+ windowReject = void 0;
3137
+ }
3138
+ const { promise } = hook.stream(["abort"], RpcPayload.fromAppParams([reason]));
3139
+ promise.then(() => disposeHook(), () => disposeHook());
3140
+ }
3141
+ });
3142
+ }
3143
+ var ReadableStreamStubHook = class _ReadableStreamStubHook extends StubHook {
3144
+ state;
3145
+ // undefined when disposed
3146
+ // Creates a new ReadableStreamStubHook.
3147
+ static create(stream) {
3148
+ return new _ReadableStreamStubHook({ refcount: 1, stream, canceled: false });
3149
+ }
3150
+ constructor(state, dupFrom) {
3151
+ super();
3152
+ this.state = state;
3153
+ if (dupFrom) {
3154
+ ++state.refcount;
3155
+ }
3156
+ }
3157
+ call(path, args) {
3158
+ args.dispose();
3159
+ return new ErrorStubHook(new Error("Cannot call methods on a ReadableStream stub"));
3160
+ }
3161
+ map(path, captures, instructions) {
3162
+ for (let cap of captures) {
3163
+ cap.dispose();
3164
+ }
3165
+ return new ErrorStubHook(new Error("Cannot use map() on a ReadableStream"));
3166
+ }
3167
+ get(path) {
3168
+ return new ErrorStubHook(new Error("Cannot access properties on a ReadableStream stub"));
3169
+ }
3170
+ dup() {
3171
+ let state = this.state;
3172
+ if (!state) {
3173
+ throw new Error("Attempted to dup a ReadableStreamStubHook after it was disposed.");
3174
+ }
3175
+ return new _ReadableStreamStubHook(state, this);
3176
+ }
3177
+ pull() {
3178
+ return Promise.reject(new Error("Cannot pull a ReadableStream stub"));
3179
+ }
3180
+ ignoreUnhandledRejections() {
3181
+ }
3182
+ dispose() {
3183
+ let state = this.state;
3184
+ this.state = void 0;
3185
+ if (state) {
3186
+ if (--state.refcount === 0) {
3187
+ if (!state.canceled) {
3188
+ state.canceled = true;
3189
+ if (!state.stream.locked) {
3190
+ state.stream.cancel(
3191
+ new Error("ReadableStream RPC stub was disposed without being consumed")
3192
+ ).catch(() => {
3193
+ });
3194
+ }
3195
+ }
3196
+ }
3197
+ }
3198
+ }
3199
+ onBroken(callback) {
3200
+ }
3201
+ };
3202
+ streamImpl.createWritableStreamHook = WritableStreamStubHook.create;
3203
+ streamImpl.createWritableStreamFromHook = createWritableStreamFromHook;
3204
+ streamImpl.createReadableStreamHook = ReadableStreamStubHook.create;
3205
+ var RpcStub2 = RpcStub;
3206
+ var RpcPromise2 = RpcPromise;
3207
+ var RpcSession2 = RpcSession;
3208
+ var RpcTarget4 = RpcTarget;
3209
+ var newWebSocketRpcSession2 = newWebSocketRpcSession;
3210
+ var newHttpBatchRpcSession2 = newHttpBatchRpcSession;
3211
+ var newMessagePortRpcSession2 = newMessagePortRpcSession;
3212
+ async function newWorkersRpcResponse(request, localMain) {
3213
+ if (request.method === "POST") {
3214
+ let response = await newHttpBatchRpcResponse(request, localMain);
3215
+ response.headers.set("Access-Control-Allow-Origin", "*");
3216
+ return response;
3217
+ } else if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") {
3218
+ return newWorkersWebSocketRpcResponse(request, localMain);
3219
+ } else {
3220
+ return new Response("This endpoint only accepts POST or WebSocket requests.", { status: 400 });
3221
+ }
3222
+ }
3223
+
3224
+ export { RpcPromise2 as RpcPromise, RpcSession2 as RpcSession, RpcStub2 as RpcStub, RpcTarget4 as RpcTarget, deserialize, newHttpBatchRpcResponse, newHttpBatchRpcSession2 as newHttpBatchRpcSession, newMessagePortRpcSession2 as newMessagePortRpcSession, newWebSocketRpcSession2 as newWebSocketRpcSession, newWorkersRpcResponse, newWorkersWebSocketRpcResponse, nodeHttpBatchRpcResponse, serialize };
3225
+ //# sourceMappingURL=index.js.map
3226
+ //# sourceMappingURL=index.js.map