capnweb 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,445 @@
1
+ import { IncomingMessage, ServerResponse, OutgoingHttpHeaders, OutgoingHttpHeader } from 'node:http';
2
+
3
+ // Copyright (c) 2025 Cloudflare, Inc.
4
+ // Licensed under the MIT license found in the LICENSE.txt file or at:
5
+ // https://opensource.org/license/mit
6
+
7
+ // This file borrows heavily from `types/defines/rpc.d.ts` in workerd.
8
+
9
+ // Branded types for identifying `WorkerEntrypoint`/`DurableObject`/`Target`s.
10
+ // TypeScript uses *structural* typing meaning anything with the same shape as type `T` is a `T`.
11
+ // For the classes exported by `cloudflare:workers` we want *nominal* typing (i.e. we only want to
12
+ // accept `WorkerEntrypoint` from `cloudflare:workers`, not any other class with the same shape)
13
+ declare const __RPC_STUB_BRAND: '__RPC_STUB_BRAND';
14
+ declare const __RPC_TARGET_BRAND: '__RPC_TARGET_BRAND';
15
+ // Distinguishes mapper placeholders from regular values so param unwrapping can accept them.
16
+ declare const __RPC_MAP_VALUE_BRAND: unique symbol;
17
+ interface RpcTargetBranded {
18
+ [__RPC_TARGET_BRAND]: never;
19
+ }
20
+
21
+ // Types that can be used through `Stub`s
22
+ // `never[]` preserves compatibility with strongly-typed function signatures without introducing
23
+ // `any` into inference.
24
+ type Stubable = RpcTargetBranded | ((...args: never[]) => unknown);
25
+
26
+ type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
27
+
28
+ // Types that can be passed over RPC
29
+ // The reason for using a generic type here is to build a serializable subset of structured
30
+ // cloneable composite types. This allows types defined with the "interface" keyword to pass the
31
+ // serializable check as well. Otherwise, only types defined with the "type" keyword would pass.
32
+ type RpcCompatible<T> =
33
+ // Allow `unknown` as a leaf so records/interfaces with `unknown` fields remain compatible.
34
+ | (IsUnknown<T> extends true ? unknown : never)
35
+ // Structured cloneables
36
+ | BaseType
37
+ // Structured cloneable composites
38
+ | Map<
39
+ T extends Map<infer U, unknown> ? RpcCompatible<U> : never,
40
+ T extends Map<unknown, infer U> ? RpcCompatible<U> : never
41
+ >
42
+ | Set<T extends Set<infer U> ? RpcCompatible<U> : never>
43
+ | Array<T extends Array<infer U> ? RpcCompatible<U> : never>
44
+ | ReadonlyArray<T extends ReadonlyArray<infer U> ? RpcCompatible<U> : never>
45
+ | {
46
+ [K in keyof T as K extends string | number ? K : never]: RpcCompatible<T[K]>;
47
+ }
48
+ | Promise<T extends Promise<infer U> ? RpcCompatible<U> : never>
49
+ // Special types
50
+ | Stub<Stubable>
51
+ // Serialized as stubs, see `Stubify`
52
+ | Stubable;
53
+
54
+ // Base type for all RPC stubs, including common memory management methods.
55
+ // `T` is used as a marker type for unwrapping `Stub`s later.
56
+ interface StubBase<T = unknown> extends Disposable {
57
+ [__RPC_STUB_BRAND]: T;
58
+ dup(): this;
59
+ onRpcBroken(callback: (error: any) => void): void;
60
+ }
61
+ type Stub<T extends RpcCompatible<T>> =
62
+ T extends object ? Provider<T> & StubBase<T> : StubBase<T>;
63
+
64
+ type TypedArray =
65
+ | Uint8Array
66
+ | Uint8ClampedArray
67
+ | Uint16Array
68
+ | Uint32Array
69
+ | Int8Array
70
+ | Int16Array
71
+ | Int32Array
72
+ | BigUint64Array
73
+ | BigInt64Array
74
+ | Float32Array
75
+ | Float64Array;
76
+
77
+ // This represents all the types that can be sent as-is over an RPC boundary
78
+ type BaseType =
79
+ | void
80
+ | undefined
81
+ | null
82
+ | boolean
83
+ | number
84
+ | bigint
85
+ | string
86
+ | TypedArray
87
+ | ArrayBuffer
88
+ | DataView
89
+ | Date
90
+ | Error
91
+ | RegExp
92
+ | Blob
93
+ | ReadableStream<Uint8Array>
94
+ | WritableStream<any> // Chunk type can be any RPC-compatible type
95
+ | Request
96
+ | Response
97
+ | Headers;
98
+ // Recursively rewrite all `Stubable` types with `Stub`s, and resolve promises.
99
+ // prettier-ignore
100
+ type Stubify<T> =
101
+ T extends Stubable ? Stub<T>
102
+ : T extends Promise<infer U> ? Stubify<U>
103
+ : T extends StubBase<any> ? T
104
+ : T extends Map<infer K, infer V> ? Map<Stubify<K>, Stubify<V>>
105
+ : T extends Set<infer V> ? Set<Stubify<V>>
106
+ : T extends [] ? []
107
+ : T extends [infer Head, ...infer Tail] ? [Stubify<Head>, ...Stubify<Tail>]
108
+ : T extends readonly [] ? readonly []
109
+ : T extends readonly [infer Head, ...infer Tail] ? readonly [Stubify<Head>, ...Stubify<Tail>]
110
+ : T extends Array<infer V> ? Array<Stubify<V>>
111
+ : T extends ReadonlyArray<infer V> ? ReadonlyArray<Stubify<V>>
112
+ : T extends BaseType ? T
113
+ // When using "unknown" instead of "any", interfaces are not stubified.
114
+ : T extends { [key: string | number]: any } ? { [K in keyof T as K extends string | number ? K : never]: Stubify<T[K]> }
115
+ : T;
116
+
117
+ // Recursively rewrite all `Stub<T>`s with the corresponding `T`s.
118
+ // Note we use `StubBase` instead of `Stub` here to avoid circular dependencies:
119
+ // `Stub` depends on `Provider`, which depends on `Unstubify`, which would depend on `Stub`.
120
+ // prettier-ignore
121
+ type UnstubifyInner<T> =
122
+ // Preserve local RpcTarget acceptance, but avoid needless `Stub | Value` unions when the stub
123
+ // is already assignable to the value type (important for callback contextual typing).
124
+ T extends StubBase<infer V> ? (T extends V ? UnstubifyInner<V> : (T | UnstubifyInner<V>))
125
+ : T extends Promise<infer U> ? UnstubifyInner<U>
126
+ : T extends Map<infer K, infer V> ? Map<Unstubify<K>, Unstubify<V>>
127
+ : T extends Set<infer V> ? Set<Unstubify<V>>
128
+ : T extends [] ? []
129
+ : T extends [infer Head, ...infer Tail] ? [Unstubify<Head>, ...Unstubify<Tail>]
130
+ : T extends readonly [] ? readonly []
131
+ : T extends readonly [infer Head, ...infer Tail] ? readonly [Unstubify<Head>, ...Unstubify<Tail>]
132
+ : T extends Array<infer V> ? Array<Unstubify<V>>
133
+ : T extends ReadonlyArray<infer V> ? ReadonlyArray<Unstubify<V>>
134
+ : T extends BaseType ? T
135
+ : T extends { [key: string | number]: unknown } ? { [K in keyof T as K extends string | number ? K : never]: Unstubify<T[K]> }
136
+ : T;
137
+
138
+ // You can put promises anywhere in the params and they'll be resolved before delivery.
139
+ // (This also covers RpcPromise, because it's defined as being a Promise.)
140
+ // Map placeholders are also allowed so primitive map callback inputs can be forwarded directly
141
+ // into RPC params.
142
+ //
143
+ // Keep raw non-stub members so generic assignability still works when UnstubifyInner<T> is deferred.
144
+ // Remove stub members from mixed unions so callback params don’t get both stub and unstubbed signatures.
145
+ // Marker carried by map() callback inputs. This lets primitive placeholders flow through params.
146
+ type Unstubify<T> =
147
+ | NonStubMembers<T>
148
+ | UnstubifyInner<T>
149
+ | Promise<UnstubifyInner<T>>
150
+ | MapValuePlaceholder<UnstubifyInner<T>>;
151
+
152
+ type UnstubifyAll<A extends readonly unknown[]> = { [I in keyof A]: Unstubify<A[I]> };
153
+
154
+ interface MapValuePlaceholder<T> {
155
+ [__RPC_MAP_VALUE_BRAND]: T;
156
+ }
157
+
158
+ type NonStubMembers<T> = Exclude<T, StubBase<any>>;
159
+
160
+ // Utility type for adding `Disposable`s to `object` types only.
161
+ // Note `unknown & T` is equivalent to `T`.
162
+ type MaybeDisposable<T> = T extends object ? Disposable : unknown;
163
+
164
+ // Type for method return or property on an RPC interface.
165
+ // - Stubable types are replaced by stubs.
166
+ // - RpcCompatible types are passed by value, with stubable types replaced by stubs
167
+ // and a top-level `Disposer`.
168
+ // Everything else can't be passed over RPC.
169
+ // Technically, we use custom thenables here, but they quack like `Promise`s.
170
+ // Intersecting with `(Maybe)Provider` allows pipelining.
171
+ // prettier-ignore
172
+ type Result<R> =
173
+ IsAny<R> extends true ? UnknownResult
174
+ : IsUnknown<R> extends true ? UnknownResult
175
+ : R extends Stubable ? Promise<Stub<R>> & Provider<R> & StubBase<R>
176
+ : R extends RpcCompatible<R> ? Promise<Stubify<R> & MaybeDisposable<R>> & Provider<R> & StubBase<R>
177
+ : never;
178
+
179
+ type IsAny<T> = 0 extends (1 & T) ? true : false;
180
+ type UnknownResult = Promise<unknown> & Provider<unknown> & StubBase<unknown>;
181
+
182
+ // Type for method or property on an RPC interface.
183
+ // For methods, unwrap `Stub`s in parameters, and rewrite returns to be `Result`s.
184
+ // Unwrapping `Stub`s allows calling with `Stubable` arguments.
185
+ // For properties, rewrite types to be `Result`s.
186
+ // In each case, unwrap `Promise`s.
187
+ type MethodOrProperty<V> = V extends (...args: infer P) => infer R
188
+ ? (...args: UnstubifyAll<P>) => IsAny<R> extends true ? UnknownResult : Result<Awaited<R>>
189
+ : Result<Awaited<V>>;
190
+
191
+ // Type for the callable part of an `Provider` if `T` is callable.
192
+ // This is intersected with methods/properties.
193
+ type MaybeCallableProvider<T> = T extends (...args: any[]) => any
194
+ ? MethodOrProperty<T>
195
+ : unknown;
196
+
197
+ type TupleIndexKeys<T extends ReadonlyArray<unknown>> = Extract<keyof T, `${number}`>;
198
+ type MapCallbackValue<T> =
199
+ // `Omit` removes call signatures, so re-intersect callable provider behavior.
200
+ T extends unknown
201
+ ? Omit<Result<T>, keyof Promise<unknown>> &
202
+ MaybeCallableProvider<T> &
203
+ MapValuePlaceholder<T>
204
+ : never;
205
+ type InvalidNativePromiseInMapResult<T, Seen = never> =
206
+ T extends unknown ? InvalidNativePromiseInMapResultImpl<T, Seen> : never;
207
+ type InvalidNativePromiseInMapResultImpl<T, Seen> =
208
+ [T] extends [Seen] ? never
209
+ // RpcPromise is modeled as Promise & StubBase, so allow promise-like stub values.
210
+ : T extends StubBase<any> ? never
211
+ // Native thenables cannot be represented in map recordings, even when typed as PromiseLike.
212
+ : T extends PromiseLike<unknown> ? T
213
+ : T extends Map<infer K, infer V>
214
+ ? InvalidNativePromiseInMapResult<K, Seen | T> |
215
+ InvalidNativePromiseInMapResult<V, Seen | T>
216
+ : T extends Set<infer V> ? InvalidNativePromiseInMapResult<V, Seen | T>
217
+ : T extends readonly [] ? never
218
+ : T extends readonly [infer Head, ...infer Tail]
219
+ ? InvalidNativePromiseInMapResult<Head, Seen | T> |
220
+ InvalidNativePromiseInMapResult<Tail[number], Seen | T>
221
+ : T extends ReadonlyArray<infer V> ? InvalidNativePromiseInMapResult<V, Seen | T>
222
+ : T extends { [key: string | number]: unknown }
223
+ ? InvalidNativePromiseInMapResult<
224
+ T[Extract<keyof T, string | number>],
225
+ Seen | T
226
+ >
227
+ : never;
228
+ type MapCallbackReturn<T> =
229
+ InvalidNativePromiseInMapResult<T> extends never ? T : never;
230
+ type ArrayProvider<E> = {
231
+ [K in number]: MethodOrProperty<E>;
232
+ } & {
233
+ map<V>(callback: (elem: MapCallbackValue<E>) => MapCallbackReturn<V>): Result<Array<V>>;
234
+ };
235
+ type TupleProvider<T extends ReadonlyArray<unknown>> = {
236
+ [K in TupleIndexKeys<T>]: MethodOrProperty<T[K]>;
237
+ } & ArrayProvider<T[number]>;
238
+
239
+ // Base type for all other types providing RPC-like interfaces.
240
+ // Rewrites all methods/properties to be `MethodOrProperty`s, while preserving callable types.
241
+ type Provider<T> = MaybeCallableProvider<T> &
242
+ (T extends ReadonlyArray<unknown>
243
+ ? number extends T["length"] ? ArrayProvider<T[number]> : TupleProvider<T>
244
+ : {
245
+ [K in Exclude<
246
+ keyof T,
247
+ symbol | keyof StubBase<never>
248
+ >]: MethodOrProperty<T[K]>;
249
+ } & {
250
+ map<V>(
251
+ callback: (value: MapCallbackValue<NonNullable<T>>) => MapCallbackReturn<V>
252
+ ): Result<Array<V>>;
253
+ });
254
+
255
+ /**
256
+ * Serialize a value, using Cap'n Web's underlying serialization. This won't be able to serialize
257
+ * RPC stubs, but it will support basic data types.
258
+ */
259
+ declare function serialize(value: unknown): string;
260
+ /**
261
+ * Deserialize a value serialized using serialize().
262
+ */
263
+ declare function deserialize(value: string): unknown;
264
+
265
+ /**
266
+ * Interface for an RPC transport, which is a simple bidirectional message stream. Implement this
267
+ * interface if the built-in transports (e.g. for HTTP batch and WebSocket) don't meet your needs.
268
+ */
269
+ interface RpcTransport {
270
+ /**
271
+ * Sends a message to the other end.
272
+ */
273
+ send(message: string): Promise<void>;
274
+ /**
275
+ * Receives a message sent by the other end.
276
+ *
277
+ * If and when the transport becomes disconnected, this will reject. The thrown error will be
278
+ * propagated to all outstanding calls and future calls on any stubs associated with the session.
279
+ * If there are no outstanding calls (and none are made in the future), then the error does not
280
+ * propagate anywhere -- this is considered a "clean" shutdown.
281
+ */
282
+ receive(): Promise<string>;
283
+ /**
284
+ * Indicates that the RPC system has suffered an error that prevents the session from continuing.
285
+ * The transport should ideally try to send any queued messages if it can, and then close the
286
+ * connection. (It's not strictly necessary to deliver queued messages, but the last message sent
287
+ * before abort() is called is often an "abort" message, which communicates the error to the
288
+ * peer, so if that is dropped, the peer may have less information about what happened.)
289
+ */
290
+ abort?(reason: any): void;
291
+ }
292
+ /**
293
+ * Options to customize behavior of an RPC session. All functions which start a session should
294
+ * optionally accept this.
295
+ */
296
+ type RpcSessionOptions = {
297
+ /**
298
+ * If provided, this function will be called whenever an `Error` object is serialized (for any
299
+ * reason, not just because it was thrown). This can be used to log errors, and also to redact
300
+ * them.
301
+ *
302
+ * If `onSendError` returns an Error object, than object will be substituted in place of the
303
+ * original. If it has a stack property, the stack will be sent to the client.
304
+ *
305
+ * If `onSendError` doesn't return anything (or is not provided at all), the default behavior is
306
+ * to serialize the error with the stack omitted.
307
+ */
308
+ onSendError?: (error: Error) => Error | void;
309
+ };
310
+
311
+ /**
312
+ * For use in Cloudflare Workers: Construct an HTTP response that starts a WebSocket RPC session
313
+ * with the given `localMain`.
314
+ */
315
+ declare function newWorkersWebSocketRpcResponse(request: Request, localMain?: any, options?: RpcSessionOptions): Response;
316
+
317
+ /**
318
+ * Implements the server end of an HTTP batch session, using standard Fetch API types to represent
319
+ * HTTP requests and responses.
320
+ *
321
+ * @param request The request received from the client initiating the session.
322
+ * @param localMain The main stub or RpcTarget which the server wishes to expose to the client.
323
+ * @param options Optional RPC session options.
324
+ * @returns The HTTP response to return to the client. Note that the returned object has mutable
325
+ * headers, so you can modify them using e.g. `response.headers.set("Foo", "bar")`.
326
+ */
327
+ declare function newHttpBatchRpcResponse(request: Request, localMain: any, options?: RpcSessionOptions): Promise<Response>;
328
+ /**
329
+ * Implements the server end of an HTTP batch session using traditional Node.js HTTP APIs.
330
+ *
331
+ * @param request The request received from the client initiating the session.
332
+ * @param response The response object, to which the response should be written.
333
+ * @param localMain The main stub or RpcTarget which the server wishes to expose to the client.
334
+ * @param options Optional RPC session options. You can also pass headers to set on the response.
335
+ */
336
+ declare function nodeHttpBatchRpcResponse(request: IncomingMessage, response: ServerResponse, localMain: any, options?: RpcSessionOptions & {
337
+ headers?: OutgoingHttpHeaders | OutgoingHttpHeader[];
338
+ }): Promise<void>;
339
+
340
+ /**
341
+ * Represents a reference to a remote object, on which methods may be remotely invoked via RPC.
342
+ *
343
+ * `RpcStub` can represent any interface (when using TypeScript, you pass the specific interface
344
+ * type as `T`, but this isn't known at runtime). The way this works is, `RpcStub` is actually a
345
+ * `Proxy`. It makes itself appear as if every possible method / property name is defined. You can
346
+ * invoke any method name, and the invocation will be sent to the server. If it turns out that no
347
+ * such method exists on the remote object, an exception is thrown back. But the client does not
348
+ * actually know, until that point, what methods exist.
349
+ */
350
+ type RpcStub<T extends RpcCompatible<T>> = Stub<T>;
351
+ declare const RpcStub: {
352
+ new <T extends RpcCompatible<T>>(value: T): RpcStub<T>;
353
+ };
354
+ /**
355
+ * Represents the result of an RPC call.
356
+ *
357
+ * Also used to represent properties. That is, `stub.foo` evaluates to an `RpcPromise` for the
358
+ * value of `foo`.
359
+ *
360
+ * This isn't actually a JavaScript `Promise`. It does, however, have `then()`, `catch()`, and
361
+ * `finally()` methods, like `Promise` does, and because it has a `then()` method, JavaScript will
362
+ * allow you to treat it like a promise, e.g. you can `await` it.
363
+ *
364
+ * An `RpcPromise` is also a proxy, just like `RpcStub`, where calling methods or awaiting
365
+ * properties will make a pipelined network request.
366
+ *
367
+ * Note that and `RpcPromise` is "lazy": the actual final result is not requested from the server
368
+ * until you actually `await` the promise (or call `then()`, etc. on it). This is an optimization:
369
+ * if you only intend to use the promise for pipelining and you never await it, then there's no
370
+ * need to transmit the resolution!
371
+ */
372
+ type RpcPromise<T extends RpcCompatible<T>> = Stub<T> & Promise<Stubify<T>>;
373
+ declare const RpcPromise: {};
374
+ /**
375
+ * Use to construct an `RpcSession` on top of a custom `RpcTransport`.
376
+ *
377
+ * Most people won't use this. You only need it if you've implemented your own `RpcTransport`.
378
+ */
379
+ interface RpcSession<T extends RpcCompatible<T> = undefined> {
380
+ getRemoteMain(): RpcStub<T>;
381
+ getStats(): {
382
+ imports: number;
383
+ exports: number;
384
+ };
385
+ drain(): Promise<void>;
386
+ }
387
+ declare const RpcSession: {
388
+ new <T extends RpcCompatible<T> = undefined>(transport: RpcTransport, localMain?: any, options?: RpcSessionOptions): RpcSession<T>;
389
+ };
390
+ /**
391
+ * Classes which are intended to be passed by reference and called over RPC must extend
392
+ * `RpcTarget`. A class which does not extend `RpcTarget` (and which doesn't have built-in support
393
+ * from the RPC system) cannot be passed in an RPC message at all; an exception will be thrown.
394
+ *
395
+ * Note that on Cloudflare Workers, this `RpcTarget` is an alias for the one exported from the
396
+ * "cloudflare:workers" module, so they can be used interchangably.
397
+ */
398
+ interface RpcTarget extends RpcTargetBranded {
399
+ }
400
+ declare const RpcTarget: {
401
+ new (): RpcTarget;
402
+ };
403
+ /**
404
+ * Empty interface used as default type parameter for sessions where the other side doesn't
405
+ * necessarily export a main interface.
406
+ */
407
+ interface Empty {
408
+ }
409
+ /**
410
+ * Start a WebSocket session given either an already-open WebSocket or a URL.
411
+ *
412
+ * @param webSocket Either the `wss://` URL to connect to, or an already-open WebSocket object to
413
+ * use.
414
+ * @param localMain The main RPC interface to expose to the peer. Returns a stub for the main
415
+ * interface exposed from the peer.
416
+ */
417
+ declare let newWebSocketRpcSession: <T extends RpcCompatible<T> = Empty>(webSocket: WebSocket | string, localMain?: any, options?: RpcSessionOptions) => RpcStub<T>;
418
+ /**
419
+ * Initiate an HTTP batch session from the client side.
420
+ *
421
+ * The parameters to this method have exactly the same signature as `fetch()`, but the return
422
+ * value is an RpcStub. You can customize anything about the request except for the method
423
+ * (it will always be set to POST) and the body (which the RPC system will fill in).
424
+ */
425
+ declare let newHttpBatchRpcSession: <T extends RpcCompatible<T>>(urlOrRequest: string | Request, options?: RpcSessionOptions) => RpcStub<T>;
426
+ /**
427
+ * Initiate an RPC session over a MessagePort, which is particularly useful for communicating
428
+ * between an iframe and its parent frame in a browser context. Each side should call this function
429
+ * on its own end of the MessageChannel.
430
+ */
431
+ declare let newMessagePortRpcSession: <T extends RpcCompatible<T> = Empty>(port: MessagePort, localMain?: any, options?: RpcSessionOptions) => RpcStub<T>;
432
+ /**
433
+ * Implements unified handling of HTTP-batch and WebSocket responses for the Cloudflare Workers
434
+ * Runtime.
435
+ *
436
+ * SECURITY WARNING: This function accepts cross-origin requests. If you do not want this, you
437
+ * should validate the `Origin` header before calling this, or use `newHttpBatchRpcSession()` and
438
+ * `newWebSocketRpcSession()` directly with appropriate security measures for each type of request.
439
+ * But if your API uses in-band authorization (i.e. it has an RPC method that takes the user's
440
+ * credentials as parameters and returns the authorized API), then cross-origin requests should
441
+ * be safe.
442
+ */
443
+ declare function newWorkersRpcResponse(request: Request, localMain: any): Promise<Response>;
444
+
445
+ export { type RpcTransport as R, __RPC_TARGET_BRAND as _, type RpcTargetBranded as a, type RpcSessionOptions as b, type RpcCompatible as c, RpcStub as d, RpcPromise as e, RpcSession as f, RpcTarget as g, deserialize as h, newHttpBatchRpcSession as i, newMessagePortRpcSession as j, newWebSocketRpcSession as k, newWorkersRpcResponse as l, newWorkersWebSocketRpcResponse as m, newHttpBatchRpcResponse as n, nodeHttpBatchRpcResponse as o, serialize as s };
@@ -93,6 +93,8 @@ function typeForRpc(value) {
93
93
  return "request";
94
94
  case Response.prototype:
95
95
  return "response";
96
+ case Blob.prototype:
97
+ return "blob";
96
98
  // TODO: All other structured clone types.
97
99
  case RpcStub.prototype:
98
100
  return "stub";
@@ -563,6 +565,7 @@ var RpcPayload = class _RpcPayload {
563
565
  case "bigint":
564
566
  case "date":
565
567
  case "bytes":
568
+ case "blob":
566
569
  case "error":
567
570
  case "undefined":
568
571
  return value;
@@ -799,6 +802,7 @@ var RpcPayload = class _RpcPayload {
799
802
  case "primitive":
800
803
  case "bigint":
801
804
  case "bytes":
805
+ case "blob":
802
806
  case "date":
803
807
  case "error":
804
808
  case "undefined":
@@ -901,6 +905,7 @@ var RpcPayload = class _RpcPayload {
901
905
  case "primitive":
902
906
  case "bigint":
903
907
  case "bytes":
908
+ case "blob":
904
909
  case "date":
905
910
  case "error":
906
911
  case "undefined":
@@ -992,6 +997,7 @@ function followPath(value, parent, path, owner) {
992
997
  case "primitive":
993
998
  case "bigint":
994
999
  case "bytes":
1000
+ case "blob":
995
1001
  case "date":
996
1002
  case "error":
997
1003
  case "headers":
@@ -1318,6 +1324,10 @@ var NullExporter = class {
1318
1324
  }
1319
1325
  };
1320
1326
  var NULL_EXPORTER = new NullExporter();
1327
+ async function streamToBlob(stream, type) {
1328
+ let b = await new Response(stream).blob();
1329
+ return b.type === type ? b : b.slice(0, b.size, type);
1330
+ }
1321
1331
  var ERROR_TYPES = {
1322
1332
  Error,
1323
1333
  EvalError,
@@ -1405,8 +1415,10 @@ var Devaluator = class _Devaluator {
1405
1415
  }
1406
1416
  case "bigint":
1407
1417
  return ["bigint", value.toString()];
1408
- case "date":
1409
- return ["date", value.getTime()];
1418
+ case "date": {
1419
+ const time = value.getTime();
1420
+ return ["date", Number.isNaN(time) ? null : time];
1421
+ }
1410
1422
  case "bytes": {
1411
1423
  let bytes = value;
1412
1424
  if (bytes.toBase64) {
@@ -1492,14 +1504,52 @@ var Devaluator = class _Devaluator {
1492
1504
  }
1493
1505
  return ["response", body, init];
1494
1506
  }
1507
+ case "blob": {
1508
+ let blob = value;
1509
+ let readable = blob.stream();
1510
+ let hook = streamImpl.createReadableStreamHook(readable);
1511
+ let importId = this.exporter.createPipe(readable, hook);
1512
+ return ["blob", blob.type, ["readable", importId]];
1513
+ }
1495
1514
  case "error": {
1496
1515
  let e = value;
1497
1516
  let rewritten = this.exporter.onSendError(e);
1498
1517
  if (rewritten) {
1499
1518
  e = rewritten;
1500
1519
  }
1520
+ let anyE = e;
1521
+ let props;
1522
+ let captureProp = (key, val) => {
1523
+ let exportsBefore = this.exports?.length ?? 0;
1524
+ try {
1525
+ let encoded = this.devaluateImpl(val, e, depth + 1);
1526
+ if (!props) props = {};
1527
+ props[key] = encoded;
1528
+ } catch (err) {
1529
+ if (this.exports && this.exports.length > exportsBefore) {
1530
+ let tail = this.exports.splice(exportsBefore);
1531
+ try {
1532
+ this.exporter.unexport(tail);
1533
+ } catch (err2) {
1534
+ }
1535
+ }
1536
+ }
1537
+ };
1538
+ for (let key of Object.keys(e)) {
1539
+ if (key === "name" || key === "message" || key === "stack") continue;
1540
+ captureProp(key, anyE[key]);
1541
+ }
1542
+ if ("cause" in e) {
1543
+ captureProp("cause", anyE.cause);
1544
+ }
1545
+ if (e instanceof AggregateError) {
1546
+ captureProp("errors", e.errors);
1547
+ }
1501
1548
  let result = ["error", e.name, e.message];
1502
- if (rewritten && rewritten.stack) {
1549
+ if (props) {
1550
+ result.push(rewritten && rewritten.stack ? rewritten.stack : null);
1551
+ result.push(props);
1552
+ } else if (rewritten && rewritten.stack) {
1503
1553
  result.push(rewritten.stack);
1504
1554
  }
1505
1555
  return result;
@@ -1599,6 +1649,12 @@ function fixBrokenRequestBody(request, body) {
1599
1649
  });
1600
1650
  return new RpcPromise(new PromiseStubHook(promise), []);
1601
1651
  }
1652
+ function streamToBlobPromise(stream, type) {
1653
+ let promise = streamToBlob(stream, type).then((blob) => {
1654
+ return new PayloadStubHook(RpcPayload.fromAppReturn(blob));
1655
+ });
1656
+ return new RpcPromise(new PromiseStubHook(promise), []);
1657
+ }
1602
1658
  var Evaluator = class _Evaluator {
1603
1659
  constructor(importer) {
1604
1660
  this.importer = importer;
@@ -1634,6 +1690,9 @@ var Evaluator = class _Evaluator {
1634
1690
  }
1635
1691
  break;
1636
1692
  case "date":
1693
+ if (value[1] === null) {
1694
+ return /* @__PURE__ */ new Date(NaN);
1695
+ }
1637
1696
  if (typeof value[1] == "number") {
1638
1697
  return new Date(value[1]);
1639
1698
  }
@@ -1659,10 +1718,22 @@ var Evaluator = class _Evaluator {
1659
1718
  case "error":
1660
1719
  if (value.length >= 3 && typeof value[1] === "string" && typeof value[2] === "string") {
1661
1720
  let cls = ERROR_TYPES[value[1]] || Error;
1662
- let result = new cls(value[2]);
1721
+ let result = cls === AggregateError ? new cls([], value[2]) : new cls(value[2]);
1663
1722
  if (typeof value[3] === "string") {
1664
1723
  result.stack = value[3];
1665
1724
  }
1725
+ if (value.length >= 5) {
1726
+ let props = value[4];
1727
+ if (!props || typeof props !== "object" || Array.isArray(props)) {
1728
+ break;
1729
+ }
1730
+ let anyResult = result;
1731
+ let propsObj = props;
1732
+ for (let key of Object.keys(propsObj)) {
1733
+ if (key === "name" || key === "message" || key === "stack") continue;
1734
+ anyResult[key] = this.evaluateImpl(propsObj[key], result, key);
1735
+ }
1736
+ }
1666
1737
  return result;
1667
1738
  }
1668
1739
  break;
@@ -1727,6 +1798,17 @@ var Evaluator = class _Evaluator {
1727
1798
  }
1728
1799
  return new Response(body, init);
1729
1800
  }
1801
+ case "blob": {
1802
+ if (value.length !== 3 || typeof value[1] !== "string") break;
1803
+ let contentType = value[1];
1804
+ let content = this.evaluateImpl(value[2], parent, property);
1805
+ if (!(content instanceof ReadableStream)) {
1806
+ throw new TypeError("Blob content must be serialized as a ReadableStream.");
1807
+ }
1808
+ let promise = streamToBlobPromise(content, contentType);
1809
+ this.promises.push({ promise, parent, property });
1810
+ return promise;
1811
+ }
1730
1812
  case "import":
1731
1813
  case "pipeline": {
1732
1814
  if (value.length < 2 || value.length > 4) {
@@ -2067,12 +2149,7 @@ var RpcSessionImpl = class {
2067
2149
  this.options = options;
2068
2150
  this.exports.push({ hook: mainHook, refcount: 1 });
2069
2151
  this.imports.push(new ImportTableEntry(this, 0, false));
2070
- let rejectFunc;
2071
- let abortPromise = new Promise((resolve, reject) => {
2072
- rejectFunc = reject;
2073
- });
2074
- this.cancelReadLoop = rejectFunc;
2075
- this.readLoop(abortPromise).catch((err) => this.abort(err));
2152
+ this.readLoop().catch((err) => this.abort(err));
2076
2153
  }
2077
2154
  exports = [];
2078
2155
  reverseExports = /* @__PURE__ */ new Map();
@@ -2356,7 +2433,8 @@ var RpcSessionImpl = class {
2356
2433
  }
2357
2434
  abort(error, trySendAbortMessage = true) {
2358
2435
  if (this.abortReason !== void 0) return;
2359
- this.cancelReadLoop(error);
2436
+ this.cancelReadLoop?.(error);
2437
+ this.cancelReadLoop = void 0;
2360
2438
  if (trySendAbortMessage) {
2361
2439
  try {
2362
2440
  this.transport.send(JSON.stringify(["abort", Devaluator.devaluate(error, void 0, this)])).catch((err) => {
@@ -2392,9 +2470,19 @@ var RpcSessionImpl = class {
2392
2470
  this.exports[i].hook.dispose();
2393
2471
  }
2394
2472
  }
2395
- async readLoop(abortPromise) {
2473
+ async readLoop() {
2396
2474
  while (!this.abortReason) {
2397
- let msg = JSON.parse(await Promise.race([this.transport.receive(), abortPromise]));
2475
+ let readCanceled = Promise.withResolvers();
2476
+ this.cancelReadLoop = readCanceled.reject;
2477
+ let msgText;
2478
+ try {
2479
+ msgText = await Promise.race([this.transport.receive(), readCanceled.promise]);
2480
+ } finally {
2481
+ if (this.cancelReadLoop === readCanceled.reject) {
2482
+ this.cancelReadLoop = void 0;
2483
+ }
2484
+ }
2485
+ let msg = JSON.parse(msgText);
2398
2486
  if (this.abortReason) break;
2399
2487
  if (msg instanceof Array) {
2400
2488
  switch (msg[0]) {