@mcp-b/do-runtime 0.5.0 → 0.6.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/CHANGELOG.md +18 -0
- package/README.md +30 -3
- package/dist/browser.js +55 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunks/{io-context-RmmjNtwm.js → io-context-BBgKEsdR.js} +12 -3
- package/dist/chunks/{io-context-RmmjNtwm.js.map → io-context-BBgKEsdR.js.map} +1 -1
- package/dist/chunks/web-socket-W63BdFn5.js +2574 -0
- package/dist/chunks/web-socket-W63BdFn5.js.map +1 -0
- package/dist/conformance/host.d.ts +16 -2
- package/dist/conformance.js.map +1 -1
- package/dist/gate.js +1 -1
- package/dist/index.js +183 -2310
- package/dist/index.js.map +1 -1
- package/dist/src/api/actor-state.d.ts +30 -21
- package/dist/src/api/global-scope.d.ts +12 -3
- package/dist/src/api/web-socket.d.ts +88 -73
- package/dist/src/browser.d.ts +15 -0
- package/dist/src/index.d.ts +6 -4
- package/dist/src/io/io-context.d.ts +1 -0
- package/dist/src/server/actor-container.d.ts +16 -2
- package/dist/src/server/hibernation-mirror.d.ts +16 -0
- package/package.json +5 -1
|
@@ -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:
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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(
|
|
332
|
-
getWebSockets(
|
|
333
|
-
setWebSocketAutoResponse(
|
|
334
|
-
getWebSocketAutoResponse():
|
|
335
|
-
getWebSocketAutoResponseTimestamp(
|
|
336
|
-
setHibernatableWebSocketEventTimeout(
|
|
337
|
-
getHibernatableWebSocketEventTimeout():
|
|
338
|
-
getTags(
|
|
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
|
-
|
|
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
|
|
242
|
-
*
|
|
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.
|
|
@@ -1,93 +1,108 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ← workerd `src/workerd/api/web-socket.{h,c++}` —
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RawWebSocket } from "./api/web-socket.js";
|
|
2
|
+
export type UpgradeWebSocket = EventTarget & RawWebSocket & {
|
|
3
|
+
accept(): void;
|
|
4
|
+
readonly readyState: number;
|
|
5
|
+
};
|
|
6
|
+
type CloneableRequest = {
|
|
7
|
+
readonly headers: Headers;
|
|
8
|
+
clone(): CloneableRequest;
|
|
9
|
+
};
|
|
10
|
+
/** Install the Request/Response half of browser-hosted WebSocket upgrades. */
|
|
11
|
+
export declare function installWebSocketUpgradeGlobals(): void;
|
|
12
|
+
export declare function upgradeWebSocket(response: Response): UpgradeWebSocket | undefined;
|
|
13
|
+
/** Preserve the upgrade signal across browser `Request.clone()` calls. */
|
|
14
|
+
export declare function withWebSocketUpgrade<T extends CloneableRequest>(request: T): T;
|
|
15
|
+
export {};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -51,8 +51,10 @@ 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
|
+
export type { HibernationAutoResponse } from "./server/hibernation-mirror.js";
|
|
57
|
+
export { HibernationMirror } from "./server/hibernation-mirror.js";
|
|
56
58
|
export type { ActorChannelFactory, GlobalActorRequest } from "./api/actor.js";
|
|
57
59
|
export { createDurableObjectNamespace } from "./server/actor-namespace.js";
|
|
58
60
|
export { ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE } from "./api/actor.js";
|
|
@@ -74,7 +76,7 @@ export { ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE } from "./api/actor.js"
|
|
|
74
76
|
*/
|
|
75
77
|
export type { LoopbackDurableObjectClassOptions, LoopbackDurableObjectClassValue, ActorClassChannelFactory, } from "./api/export-loopback.js";
|
|
76
78
|
export { asLoopbackDurableObjectClass, LoopbackDurableObjectClass } from "./api/export-loopback.js";
|
|
77
|
-
export { FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH,
|
|
79
|
+
export { FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH, } from "./api/actor-state.js";
|
|
78
80
|
export { PITR_UNIMPLEMENTED_MESSAGE, REPLICATION_UNIMPLEMENTED_MESSAGE } from "./io/actor-cache.js";
|
|
79
81
|
/**
|
|
80
82
|
* The alarm scheduler is exported where the gate is not, and the difference is
|
|
@@ -114,8 +116,8 @@ export { AlarmInvocationInfo } from "./api/global-scope.js";
|
|
|
114
116
|
*/
|
|
115
117
|
export type { ActorGlobalScope, ActorGlobalScopeOptions, ActorScopeBindings, FetchPort, Scheduler, SchedulerWaitOptions, } from "./api/global-scope.js";
|
|
116
118
|
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";
|
|
119
|
+
export type { AcceptedWebSocket, RawWebSocket, RehydratedWebSocket } from "./api/web-socket.js";
|
|
120
|
+
export { ALREADY_ACCEPTED_MESSAGE, WebSocketRequestResponsePair, installWebSocketGlobals, markWebSocketUsed, } from "./api/web-socket.js";
|
|
119
121
|
export { BYOB_READER_UNGATABLE_MESSAGE, gateRequestBody } from "./api/http.js";
|
|
120
122
|
/**
|
|
121
123
|
* 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
|
|
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
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HibernationHost, RawWebSocket, RehydratedWebSocket } from "../api/web-socket.js";
|
|
2
|
+
export type HibernationAutoResponse = {
|
|
3
|
+
request: string;
|
|
4
|
+
response: string;
|
|
5
|
+
};
|
|
6
|
+
/** In-memory socket state shared by embedders that replace live actor containers. */
|
|
7
|
+
export declare class HibernationMirror implements HibernationHost {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(rehydrated?: readonly RehydratedWebSocket[], autoResponsePair?: HibernationAutoResponse | null);
|
|
10
|
+
get autoResponsePair(): HibernationAutoResponse | null;
|
|
11
|
+
accepted(socket: RawWebSocket, tags: readonly string[]): void;
|
|
12
|
+
attachment(socket: RawWebSocket, bytes: Uint8Array | null): void;
|
|
13
|
+
autoResponse(pair: HibernationAutoResponse | null): void;
|
|
14
|
+
closed(socket: RawWebSocket): void;
|
|
15
|
+
snapshot(): RehydratedWebSocket[];
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/do-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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",
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"types": "./dist/src/index.d.ts",
|
|
42
42
|
"import": "./dist/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./browser": {
|
|
45
|
+
"types": "./dist/src/browser.d.ts",
|
|
46
|
+
"import": "./dist/browser.js"
|
|
47
|
+
},
|
|
44
48
|
"./server/alarm-scheduler": {
|
|
45
49
|
"types": "./dist/src/server/alarm-scheduler.d.ts",
|
|
46
50
|
"import": "./dist/server/alarm-scheduler.js"
|