@mcp-b/do-runtime 0.4.0 → 0.6.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.
@@ -36,11 +36,9 @@
36
36
  * `transformMaybeBackpressure` keeps the branch because
37
37
  * `DeleteAllResults.backpressure` is still a promise in `io/actor-cache.ts`.
38
38
  *
39
- * Not ported, because the substrate has no equivalent: Hibernatable WebSockets,
40
- * which is the whole reason `DurableObjectState`'s eight WebSocket methods are
41
- * named throwing stubs; V8's private wire bytes, replaced by a browser-safe
42
- * structured-clone encoding with the same public value semantics; the billing
43
- * counters
39
+ * Not ported, because the substrate has no equivalent: V8's private wire bytes,
40
+ * replaced by a browser-safe structured-clone encoding with the same public
41
+ * value semantics; the billing counters
44
42
  * (`billingUnits`, `ActorObserver`, `updateStorageWriteUnit`) and the trace
45
43
  * spans, both already absent throughout; `enableSql`, a workerd namespace option
46
44
  * that exists to simulate a non-SQLite Durable Object; and `ReplicaActorOutgoingFactory`,
@@ -56,6 +54,7 @@ import type { SqliteKv } from "../util/sqlite-kv.js";
56
54
  import type { SqliteDatabase } from "../util/sqlite.js";
57
55
  import type { ActorScopeBindings } from "./global-scope.js";
58
56
  import { SqlStorage } from "./sql.js";
57
+ import type { HibernatableWebSocketRegistry } from "./web-socket.js";
59
58
  /**
60
59
  * ← `MAX_FACET_NAME_LENGTH` / `MAX_FACET_TREE_DEPTH`
61
60
  * (`actor-state.c++:943,947`), in the anonymous namespace beside the facet code
@@ -65,14 +64,6 @@ import { SqlStorage } from "./sql.js";
65
64
  export declare const FACET_NAME_MAX_LENGTH = 256;
66
65
  /** Root is at depth 0, so the deepest allowed facet is at depth 3. */
67
66
  export declare const FACET_TREE_MAX_DEPTH = 4;
68
- /**
69
- * The substrate boundary named in the package README: Hibernatable WebSockets
70
- * exist so the platform can evict an actor while keeping its sockets open, and
71
- * Chrome exposes no equivalent lifecycle. Under this repo's fail-closed tenet
72
- * the throw IS the specified behaviour, which is why §2.5 orders the four
73
- * silent no-op stubs beside it replaced.
74
- */
75
- export declare const HIBERNATION_UNIMPLEMENTED_MESSAGE: string;
76
67
  /**
77
68
  * ← what falls off the end of `DurableObjectFacets::get`'s class switch
78
69
  * (`actor-state.c++:1029-1043`).
@@ -88,6 +79,23 @@ export declare const HIBERNATION_UNIMPLEMENTED_MESSAGE: string;
88
79
  * which every object satisfies, so nothing refuses it before the method body.
89
80
  */
90
81
  export declare const FACET_CLASS_UNSUPPORTED_MESSAGE: string;
82
+ /**
83
+ * ← `serializeV8Value`. The wire bytes differ because V8's serializer is not
84
+ * available in browsers; the public structured-clone value semantics do not.
85
+ * The short header keeps the new representation unambiguous while old JSON rows
86
+ * remain readable.
87
+ */
88
+ export declare function serializeValue(value: unknown): Uint8Array;
89
+ /**
90
+ * ← `deserializeV8Value`.
91
+ *
92
+ * Upstream logs "the key (to help find the data in the database if it hasn't
93
+ * been deleted), the length of the value, and the first three bytes of the value
94
+ * (which is just the v8-internal version header and the tag that indicates the
95
+ * type of the value, but not its contents)". Our four-byte header carries only
96
+ * a marker and version for the same reason.
97
+ */
98
+ export declare function deserializeValue<T = unknown>(key: string, buffer: Uint8Array): T;
91
99
  /**
92
100
  * ← `DurableObjectStorageOperations`. "Common implementation of
93
101
  * DurableObjectStorage and DurableObjectTransaction. This class is designed to
@@ -268,6 +276,7 @@ export type DurableObjectStateOptions = {
268
276
  * `DurableObjectState.globals`.
269
277
  */
270
278
  globals: ActorScopeBindings;
279
+ webSockets: HibernatableWebSocketRegistry;
271
280
  };
272
281
  /** The type passed as the first parameter to a Durable Object class's constructor. */
273
282
  export declare class DurableObjectState implements globalThis.DurableObjectState {
@@ -328,13 +337,13 @@ export declare class DurableObjectState implements globalThis.DurableObjectState
328
337
  configureReadReplication(options: {
329
338
  mode: string;
330
339
  }): Promise<void>;
331
- acceptWebSocket(_ws: WebSocket, _tags?: string[]): never;
332
- getWebSockets(_tag?: string): never;
333
- setWebSocketAutoResponse(_maybeReqResp?: WebSocketRequestResponsePair): never;
334
- getWebSocketAutoResponse(): never;
335
- getWebSocketAutoResponseTimestamp(_ws: WebSocket): never;
336
- setHibernatableWebSocketEventTimeout(_timeoutMs?: number): never;
337
- getHibernatableWebSocketEventTimeout(): never;
338
- getTags(_ws: WebSocket): never;
340
+ acceptWebSocket(ws: WebSocket, tags?: string[]): void;
341
+ getWebSockets(tag?: string): WebSocket[];
342
+ setWebSocketAutoResponse(maybeReqResp?: WebSocketRequestResponsePair): void;
343
+ getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;
344
+ getWebSocketAutoResponseTimestamp(ws: WebSocket): Date | null;
345
+ setHibernatableWebSocketEventTimeout(timeoutMs?: number): void;
346
+ getHibernatableWebSocketEventTimeout(): number | null;
347
+ getTags(ws: WebSocket): string[];
339
348
  }
340
349
  export {};
@@ -42,6 +42,8 @@
42
42
  * docs/decisions.md.
43
43
  */
44
44
  import { type IoContext } from "../io/io-context.js";
45
+ import { WebSocketRequestResponsePair, type HibernatableWebSocketRegistry } from "./web-socket.js";
46
+ type WebSocketPairConstructor = typeof WebSocketPair;
45
47
  /**
46
48
  * ← `AlarmInvocationInfo` (`api/global-scope.h:386-412`): "a jsg::Object used to
47
49
  * pass alarm invocation info to an alarm handler."
@@ -136,6 +138,7 @@ declare class GatedCrypto {
136
138
  /** What the host supplies beneath `ServiceWorkerGlobalScope::fetch`. */
137
139
  export type FetchPort = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
138
140
  export type ActorGlobalScopeOptions = {
141
+ readonly webSockets: HibernatableWebSocketRegistry;
139
142
  /** Opaque identity of the external entry whose synchronous body is running. */
140
143
  readonly currentExternalEntry?: (() => object | undefined) | undefined;
141
144
  /**
@@ -174,7 +177,10 @@ export declare class ActorGlobalScope {
174
177
  #private;
175
178
  readonly scheduler: Scheduler;
176
179
  readonly crypto: GatedCrypto;
177
- constructor(ctx: IoContext, options?: ActorGlobalScopeOptions);
180
+ readonly WebSocket: typeof globalThis.WebSocket;
181
+ readonly WebSocketPair: WebSocketPairConstructor;
182
+ readonly WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair;
183
+ constructor(ctx: IoContext, options: ActorGlobalScopeOptions);
178
184
  /** Opaque identity available only during an external entry's synchronous body. */
179
185
  get currentExternalEntry(): object | undefined;
180
186
  /** Re-enter this actor after a host promise settles. */
@@ -220,6 +226,9 @@ export type ActorScopeBindings = {
220
226
  readonly clearInterval: (id?: number | null) => void;
221
227
  readonly fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
222
228
  readonly crypto: Crypto;
229
+ readonly WebSocket: typeof globalThis.WebSocket;
230
+ readonly WebSocketPair: WebSocketPairConstructor;
231
+ readonly WebSocketRequestResponsePair: typeof WebSocketRequestResponsePair;
223
232
  readonly currentExternalEntry?: object | undefined;
224
233
  };
225
234
  /**
@@ -238,8 +247,8 @@ export declare function actorScopeBindings(resolve: () => ActorGlobalScope): Act
238
247
  * one that is not.
239
248
  *
240
249
  * **A host should call this rather than assigning the names itself**, and the
241
- * reason is the failure it prevents: a host that installs five of the six leaves
242
- * one primitive ungated, and an ungated primitive that WORKS is invisible until
250
+ * reason is the failure it prevents: a host that installs only a subset leaves a
251
+ * primitive ungated, and an ungated primitive that WORKS is invisible until
243
252
  * a continuation after it touches storage — possibly never, on the path that
244
253
  * matters. The set is the package's, so it can grow without every host growing
245
254
  * with it.
@@ -26,14 +26,24 @@
26
26
  * `abstract` in the types — JSG nested types have no JS constructor — so the
27
27
  * faithful shape is a constructor that refuses. `sql.Cursor` exists for
28
28
  * `instanceof`, which is all upstream exposes it for.
29
- * 3. **The regulator is ported whole; what is missing is the authorizer that
30
- * calls it.** All three callbacks are here and none of them needed the
29
+ * 3. **The regulator is ported whole, and that is not the whole authorizer.**
30
+ * Four of its five members are here as callbacks, and none needed the
31
31
  * authorizer to compute anything — `isAllowedName` is a prefix test,
32
- * `isAllowedTrigger` is `return true`, `allowTransactions` throws. What the
33
- * authorizer supplied was the *identifiers*, not the decisions. With no
34
- * authorizer the statement text is the only source, so `exec` tokenizes it
32
+ * `isAllowedTrigger` is `return true`, `allowTransactions` throws,
33
+ * `shouldAddQueryStats` is a constant. The fifth, `onError`, is not a
34
+ * callback at all in this port: it is every `throw new Error(message)`
35
+ * below, which is all its upstream body does with a refusal message.
36
+ * For those, what the authorizer supplied was the *identifiers*:
37
+ * with none, the statement text is the only source, so `exec` tokenizes it
35
38
  * and runs `isAllowedName` over every identifier-shaped token. That is
36
39
  * deliberately STRICTER than upstream — see `SQL_RESERVED_PREFIX_MESSAGE`.
40
+ *
41
+ * But the authorizer also makes decisions no callback ever sees, and those
42
+ * do NOT arrive with the regulator: `SQLITE_ATTACH` / `SQLITE_DETACH`, the
43
+ * `SQLITE_CREATE_TEMP_*` family and the `temp` schema, `SQLITE_PRAGMA`, and
44
+ * the `SQLITE_CREATE_VTABLE` module list. Each is refused from the text in
45
+ * `refuseUnauthorizedForms` and `requireAllowedPragmas`. `SQLITE_FUNCTION`
46
+ * is the one still unported — see the README divergence row.
37
47
  * 4. **`ingest` stays at upstream's SQLite seam.** `SqliteDatabase.ingest()`
38
48
  * executes every complete statement and returns the partial tail, using the
39
49
  * same compiled boundaries and regulator as `exec`.
@@ -73,7 +83,7 @@ export declare const STATEMENT_NOT_CONSTRUCTIBLE_MESSAGE = "Illegal invocation:
73
83
  */
74
84
  export declare const SQL_RESERVED_PREFIX_MESSAGE: string;
75
85
  /**
76
- * ← `SqlStorageRegulator` (`sql.h:15-22`, `sql.c++:143-173`), whole.
86
+ * ← `SqlStorageRegulator` (`sql.h:15-22`, `sql.c++:141-165`), whole.
77
87
  *
78
88
  * Upstream reaches these through the SQLite authorizer while a statement is
79
89
  * being compiled. `exec` calls them from the statement text instead, which is
@@ -100,6 +110,7 @@ export declare const SqlStorageRegulator: {
100
110
  * byte-identical so a caller matching on it ports unchanged.
101
111
  */
102
112
  export declare const SQL_NOT_AUTHORIZED_MESSAGE = "not authorized: SQLITE_AUTH";
113
+ export declare const SQL_VACUUM_REFUSED_MESSAGE = "cannot VACUUM from within a transaction: SQLITE_ERROR";
103
114
  /**
104
115
  * ← the `jsg::Ref<DurableObjectStorage>` `SqlStorage` holds, narrowed to the one
105
116
  * member it reaches through (`SqlStorage::getDb`). `DurableObjectStorage`
@@ -1,93 +1,108 @@
1
1
  /**
2
- * ← workerd `src/workerd/api/web-socket.{h,c++}` — the gating, and nothing else.
2
+ * ← workerd `src/workerd/api/web-socket.{h,c++}` — classic and hibernatable
3
+ * socket delivery over the actor's input/output gates.
3
4
  *
4
- * A socket is the one primitive that is neither of the other two, and §1.8 says
5
- * why in three lines: incoming frames "each take a fresh input lock via
6
- * `context.run(...)`", the read loop "captures the critical section at
7
- * `accept()` time", and outbound messages "each carry their own output-gate
8
- * promise captured at `send()` time". Upstream states the first outright, on the
9
- * line that does it (`web-socket.c++:1056-1059`):
10
- *
11
- * > "Re-enter the context with context.run(). This is arguably a bit unusual
12
- * > compared to other I/O which is delivered by return from context.awaitIo(),
13
- * > but the difference here is that we have a long stream of events over time.
14
- * > It makes sense to use context.run() each time a new event arrives."
15
- *
16
- * So a socket cannot be `awaitIo`: there is no single result to resume from.
17
- * `accept()` starts a loop, and the loop is the gate's caller.
18
- *
19
- * **What is ported and what is not.** The frame protocol, the hibernation
20
- * states, auto-response, `WebSocketPair` and the byte accounting are all
21
- * absent — the substrate ships a `WebSocket`, and hibernation is a recorded
22
- * substrate boundary with no Chrome lifecycle to be faithful to. What is here is
23
- * `WebSocket::Accepted`: the three gate properties above, over whatever socket
24
- * the host hands in. That is the same division `api/http.ts` makes and for the
25
- * same reason.
26
- *
27
- * **The accept contract, and the hole it leaves.** After `acceptWebSocket`, the
28
- * gated view owns the raw socket's events. A consumer that keeps a reference to
29
- * the raw socket and registers a listener on it directly gets that listener
30
- * called ungated, and nothing here can prevent it — upstream cannot be reached
31
- * that way because `accept()` moves the `kj::WebSocket` into `Accepted` and the
32
- * JS object never had it. The refusal below covers the case that is detectable
33
- * (accepting the same socket twice); the rest is the accept contract, stated.
34
- *
35
- * Spec: §1.1, §1.8 and decision 5 in
36
- * docs/decisions.md.
5
+ * The runtime owns the WebSocket object produced by `WebSocketPair`; embedders
6
+ * still own network transports supplied as `RawWebSocket`s. Hibernation state is
7
+ * per container and can be mirrored through `HibernationHost` for reconstruction.
37
8
  */
38
9
  import type { IoContext } from "../io/io-context.js";
39
- /**
40
- * The socket beneath. Deliberately structural and minimal: a real `WebSocket`,
41
- * the extension's `WebSocketFacade` over capnweb, and a test double all satisfy
42
- * it, and none of them is a type this package should name.
43
- */
44
10
  export interface RawWebSocket {
45
11
  addEventListener(type: string, listener: (event: Event) => void): void;
46
12
  send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void;
47
13
  close(code?: number, reason?: string): void;
14
+ readonly readyState?: number;
15
+ binaryType?: string;
16
+ }
17
+ export interface HibernationHost {
18
+ accepted(socket: RawWebSocket, tags: readonly string[]): void;
19
+ attachment(socket: RawWebSocket, bytes: Uint8Array | null): void;
20
+ autoResponse(pair: {
21
+ request: string;
22
+ response: string;
23
+ } | null): void;
24
+ closed(socket: RawWebSocket): void;
48
25
  }
26
+ export type RehydratedWebSocket = {
27
+ socket: RawWebSocket;
28
+ tags?: readonly string[];
29
+ attachment?: Uint8Array;
30
+ autoResponseTimestamp?: number;
31
+ };
32
+ type WebSocketPairConstructor = typeof WebSocketPair;
33
+ declare const RuntimeWebSocketRequestResponsePair: typeof WebSocketRequestResponsePair;
34
+ export { RuntimeWebSocketRequestResponsePair as WebSocketRequestResponsePair };
49
35
  /** ← the `JSG_REQUIRE(!native.state.is<Accepted>(), ...)` at the head of `accept()`. */
50
36
  export declare const ALREADY_ACCEPTED_MESSAGE: string;
51
- /**
52
- * `WebSocket::Accepted` (`web-socket.h:~300-360`), reached through
53
- * `accept()` `internalAccept(js, IoContext::current().getCriticalSection())`
54
- * `startReadLoop` (`web-socket.c++:133`, `:426`, `:429-433`, `:507`).
55
- *
56
- * An `EventTarget`, so a consumer registers listeners the way it would on a real
57
- * socket — but on THIS object rather than on the raw one, because this is what
58
- * runs them inside a gated slice.
59
- */
60
- export declare class AcceptedWebSocket extends EventTarget {
37
+ export declare const HIBERNATION_ALREADY_ACCEPTED_MESSAGE = "Cannot call `acceptWebSocket()` if the WebSocket was already accepted via `accept()`";
38
+ export declare const HIBERNATION_AFTER_ACCEPT_MESSAGE = "Can't accept() WebSocket after enabling hibernation.";
39
+ export declare const HIBERNATION_PAIR_USED_MESSAGE = "Cannot call `acceptWebSocket()` on this WebSocket because its pair has already been accepted or used in a Response.";
40
+ declare const SOCKET_EVENTS: readonly ["open", "message", "close", "error"];
41
+ type SocketEvent = (typeof SOCKET_EVENTS)[number];
42
+ type PairState = {
43
+ used: boolean;
44
+ hibernationAccepted: boolean;
45
+ };
46
+ /** One public socket identity, in classic or hibernatable mode after acceptance. */
47
+ export declare class AcceptedWebSocket extends EventTarget implements RawWebSocket, WebSocket {
61
48
  #private;
49
+ static readonly READY_STATE_CONNECTING: 0;
50
+ static readonly READY_STATE_OPEN: 1;
51
+ static readonly READY_STATE_CLOSING: 2;
52
+ static readonly READY_STATE_CLOSED: 3;
53
+ static readonly CONNECTING: 0;
54
+ static readonly OPEN: 1;
55
+ static readonly CLOSING: 2;
56
+ static readonly CLOSED: 3;
57
+ readonly READY_STATE_CONNECTING: 0;
58
+ readonly READY_STATE_OPEN: 1;
59
+ readonly READY_STATE_CLOSING: 2;
60
+ readonly READY_STATE_CLOSED: 3;
61
+ readonly CONNECTING: 0;
62
+ readonly OPEN: 1;
63
+ readonly CLOSING: 2;
64
+ readonly CLOSED: 3;
65
+ readonly bufferedAmount = 0;
66
+ readonly extensions = "";
67
+ readonly protocol = "";
68
+ readonly url = "";
62
69
  onopen: ((event: Event) => void) | null;
63
70
  onmessage: ((event: MessageEvent) => void) | null;
64
71
  onclose: ((event: CloseEvent) => void) | null;
65
72
  onerror: ((event: Event) => void) | null;
66
- constructor(ctx: IoContext, socket: RawWebSocket);
67
- /**
68
- * ← `WebSocket::send` (`web-socket.c++:~640`), which inserts a
69
- * `GatedMessage{IoContext::current().waitForOutputLocksIfNecessary(), …}`.
70
- *
71
- * Synchronous, as upstream's is: the wait is the pump's, not the caller's. The
72
- * output gate is what "blocks all outgoing messages from an actor that would
73
- * allow the rest of the world to observe the actor's state" (§1.1), and a
74
- * socket frame is exactly such a message.
75
- *
76
- * `waitForOutputLocksIfNecessary()` collapses to `waitForOutputLocks()` here
77
- * for the reason the whole file collapses `kj::Maybe<Worker::Actor&>`: its
78
- * body is `actor.map(…)` (`io-context.c++:383-386`) and every context in this
79
- * runtime is an actor context.
80
- */
73
+ constructor(ctx: IoContext, socket: RawWebSocket, pairState?: PairState);
74
+ get readyState(): number;
75
+ get binaryType(): "blob" | "arraybuffer";
76
+ set binaryType(value: "blob" | "arraybuffer");
77
+ accept(): void;
81
78
  send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void;
82
- /** ← `WebSocket::close`, which enqueues a `Close` through the same gate. */
83
79
  close(code?: number, reason?: string): void;
80
+ serializeAttachment(value: unknown): void;
81
+ deserializeAttachment(): unknown;
82
+ acceptHibernation(registry: HibernatableWebSocketRegistry): void;
83
+ rehydrateHibernation(registry: HibernatableWebSocketRegistry, ctx: IoContext): void;
84
+ markPairUsed(): void;
85
+ }
86
+ type HandlerDispatch = {
87
+ message(socket: RawWebSocket, message: string | ArrayBuffer): unknown;
88
+ close(socket: RawWebSocket, code: number, reason: string, wasClean: boolean): unknown;
89
+ error(socket: RawWebSocket, error: unknown): unknown;
90
+ };
91
+ export declare class HibernatableWebSocketRegistry {
92
+ #private;
93
+ constructor(ctx: IoContext, dispatch: HandlerDispatch, host?: HibernationHost, rehydrated?: readonly RehydratedWebSocket[]);
94
+ get WebSocketPair(): WebSocketPairConstructor;
95
+ acceptWebSocket(socket: RawWebSocket, tags?: string[]): void;
96
+ getWebSockets(tag?: string): WebSocket[];
97
+ getTags(socket: RawWebSocket): string[];
98
+ setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): void;
99
+ getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;
100
+ getWebSocketAutoResponseTimestamp(socket: RawWebSocket): Date | null;
101
+ setHibernatableWebSocketEventTimeout(value?: number): void;
102
+ getHibernatableWebSocketEventTimeout(): number | null;
103
+ attachmentChanged(socket: RawWebSocket, bytes: Uint8Array): void;
104
+ receive(socket: RawWebSocket, type: SocketEvent, event: Event): void;
84
105
  }
85
- /**
86
- * ← `accept()` / `state.acceptWebSocket()`, as the one verb.
87
- *
88
- * Named for what upstream names it, because the critical-section capture is a
89
- * property of accepting rather than of constructing: "a socket accepted inside a
90
- * `blockConcurrencyWhile` delivers its messages inside that critical section"
91
- * (§1.8).
92
- */
93
106
  export declare function acceptWebSocket(ctx: IoContext, socket: RawWebSocket): AcceptedWebSocket;
107
+ export declare function markWebSocketUsed(socket: RawWebSocket): void;
108
+ export declare function installWebSocketGlobals(target: object, pairConstructor: WebSocketPairConstructor): void;
@@ -51,7 +51,7 @@ export type { Module as SourceModule, ModuleContent, ModulesSource, WorkerSource
51
51
  * `src/` references `src/server/`, so a declaration in this file is one
52
52
  * `server/` may not import.
53
53
  */
54
- export type { ActorContainer, ActorContainerOptions, ActorEntry, ActorPorts, FacetHandle, FacetHost, FacetId, FacetStartRequest, FacetTree, } from "./server/actor-container.js";
54
+ export type { ActorContainer, ActorContainerOptions, ActorEntry, ActorPorts, HibernationHost, FacetHandle, FacetHost, FacetId, FacetStartRequest, FacetTree, } from "./server/actor-container.js";
55
55
  export { createActorContainer, FACET_ALARM_UNIMPLEMENTED_MESSAGE, noFacets, } from "./server/actor-container.js";
56
56
  export type { ActorChannelFactory, GlobalActorRequest } from "./api/actor.js";
57
57
  export { createDurableObjectNamespace } from "./server/actor-namespace.js";
@@ -74,7 +74,7 @@ export { ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE } from "./api/actor.js"
74
74
  */
75
75
  export type { LoopbackDurableObjectClassOptions, LoopbackDurableObjectClassValue, ActorClassChannelFactory, } from "./api/export-loopback.js";
76
76
  export { asLoopbackDurableObjectClass, LoopbackDurableObjectClass } from "./api/export-loopback.js";
77
- export { FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH, HIBERNATION_UNIMPLEMENTED_MESSAGE, } from "./api/actor-state.js";
77
+ export { FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH, } from "./api/actor-state.js";
78
78
  export { PITR_UNIMPLEMENTED_MESSAGE, REPLICATION_UNIMPLEMENTED_MESSAGE } from "./io/actor-cache.js";
79
79
  /**
80
80
  * The alarm scheduler is exported where the gate is not, and the difference is
@@ -114,8 +114,8 @@ export { AlarmInvocationInfo } from "./api/global-scope.js";
114
114
  */
115
115
  export type { ActorGlobalScope, ActorGlobalScopeOptions, ActorScopeBindings, FetchPort, Scheduler, SchedulerWaitOptions, } from "./api/global-scope.js";
116
116
  export { actorScopeBindings, FOREIGN_SLICE_MESSAGE, installActorScope, NO_GLOBAL_OUTBOUND_MESSAGE, } from "./api/global-scope.js";
117
- export type { AcceptedWebSocket, RawWebSocket } from "./api/web-socket.js";
118
- export { ALREADY_ACCEPTED_MESSAGE } from "./api/web-socket.js";
117
+ export type { AcceptedWebSocket, RawWebSocket, RehydratedWebSocket } from "./api/web-socket.js";
118
+ export { ALREADY_ACCEPTED_MESSAGE, WebSocketRequestResponsePair, installWebSocketGlobals, markWebSocketUsed, } from "./api/web-socket.js";
119
119
  export { BYOB_READER_UNGATABLE_MESSAGE, gateRequestBody } from "./api/http.js";
120
120
  /**
121
121
  * The transport, for the same reason the loader binding and the scheduler are
@@ -424,6 +424,7 @@ export declare class IoContext {
424
424
  * upstream's other half of `taskFailed()` is a log this package has no port for.
425
425
  */
426
426
  waitUntilStatus(): unknown;
427
+ waitUntilTaskCount(): number;
427
428
  /**
428
429
  * ← `IncomingRequest::drain()`, actor branch. "For actors, all promises are canceled on
429
430
  * actor shutdown, not on a fixed timeout, because work doesn't necessarily happen on a
@@ -41,12 +41,13 @@
41
41
  import { DurableObjectState } from "../api/actor-state.js";
42
42
  import type { FetchPort } from "../api/global-scope.js";
43
43
  import { ActorGlobalScope } from "../api/global-scope.js";
44
- import type { AcceptedWebSocket, RawWebSocket } from "../api/web-socket.js";
44
+ import type { AcceptedWebSocket, HibernationHost, RawWebSocket, RehydratedWebSocket } from "../api/web-socket.js";
45
45
  import type { IsolateChannelFactory, WorkerLoaderOptions } from "../api/worker-loader.js";
46
46
  import { WorkerLoader } from "../api/worker-loader.js";
47
47
  import type { AlarmOutlet } from "../io/actor-sqlite.js";
48
48
  import type { AlarmResult } from "./alarm-scheduler.js";
49
49
  import type { Timer } from "../io/io-context.js";
50
+ import type { InputGateHooks, OutputGateHooks } from "../io/io-gate.js";
50
51
  import type { SqlDatabase, SqlDatabaseProvider } from "../util/sqlite.js";
51
52
  import type { IndexFile } from "./facet-tree-index.js";
52
53
  import { FacetReferenceEpochs } from "./facet-deletion.js";
@@ -190,7 +191,7 @@ export interface FacetHost {
190
191
  */
191
192
  export declare const noFacets: FacetHost;
192
193
  /**
193
- * The four ports. Each one is a seam workerd itself takes as a constructor
194
+ * The five ports. Each one is a seam workerd itself takes as a constructor
194
195
  * input; a port that would exist only because our code is currently shaped
195
196
  * badly is an invented seam and was rejected. Rejected, for the record:
196
197
  * a transport port (one implementation per substrate, forever), a logger port
@@ -232,7 +233,9 @@ export type ActorPorts = {
232
233
  * prevent.
233
234
  */
234
235
  fetch?: FetchPort;
236
+ hibernation?: HibernationHost;
235
237
  };
238
+ export type { HibernationHost } from "../api/web-socket.js";
236
239
  /**
237
240
  * The whole-tree facet state, which belongs to the root container and is shared
238
241
  * by every container in one actor tree.
@@ -311,6 +314,11 @@ export type ActorContainerOptions = {
311
314
  exports: Record<string, unknown>;
312
315
  env: unknown;
313
316
  ports: ActorPorts;
317
+ webSockets?: readonly RehydratedWebSocket[];
318
+ gateHooks?: {
319
+ input?: InputGateHooks;
320
+ output?: OutputGateHooks;
321
+ };
314
322
  /** Present when this container hosts a facet rather than a root. */
315
323
  facet?: {
316
324
  /** Root is 0, a direct child of the root is 1. `getDepth()` answers with it. */
@@ -483,6 +491,12 @@ export interface ActorContainer {
483
491
  waitOutputLocks(): Promise<void>;
484
492
  /** For the host's idle check — today's `drainWaitUntil`. */
485
493
  drainWaitUntil(): Promise<void>;
494
+ quiescence(): {
495
+ armedTimers: number;
496
+ pendingWaitUntil: number;
497
+ inputLockHeld: boolean;
498
+ outputGateBroken: boolean;
499
+ };
486
500
  /**
487
501
  * ← `WorkerdApi::compileGlobals`'s `Global::WorkerLoader` arm
488
502
  * (`server/workerd-api.c++:748-752`), which is the step that turns a configured
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/do-runtime",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Cloudflare's Durable Object runtime (workerd), ported to TypeScript: actors with input/output gates, SQLite storage, facets and alarms, running in the browser and in Node.",
5
5
  "keywords": [
6
6
  "actors",