@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/dist/index.js CHANGED
@@ -1,536 +1,9 @@
1
- import { a as hasUserErrorDetail, c as setUserErrorDetail, d as tryCurrentSlice, f as CanceledError, i as captureGateStack, m as OutputGate, n as IoContext, o as isExceptionFromInputGateBroken, p as InputGate, r as atCheckpointEnd, s as requireInputLock, t as BrokenActorError, u as tryCurrentIoContext } from "./chunks/io-context-RmmjNtwm.js";
1
+ import { a as hasUserErrorDetail, d as tryCurrentSlice, f as CanceledError, i as captureGateStack, m as OutputGate, n as IoContext, o as isExceptionFromInputGateBroken, p as InputGate, r as atCheckpointEnd, s as requireInputLock, t as BrokenActorError, u as tryCurrentIoContext } from "./chunks/io-context-BBgKEsdR.js";
2
+ import { _ as DurableObjectClass, a as installWebSocketGlobals, c as DurableObjectStorage, d as LoopbackColoLocalActorNamespace, f as LoopbackDurableObjectClass, g as ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE, h as asLoopbackDurableObjectClass, i as acceptWebSocket, l as FACET_NAME_MAX_LENGTH, m as LoopbackServiceStub, n as HibernatableWebSocketRegistry, o as markWebSocketUsed, p as LoopbackDurableObjectNamespace, r as RuntimeWebSocketRequestResponsePair, s as DurableObjectState, t as ALREADY_ACCEPTED_MESSAGE, u as FACET_TREE_MAX_DEPTH, v as DurableObjectId, y as DurableObjectNamespace } from "./chunks/web-socket-W63BdFn5.js";
2
3
  import { RpcTarget as RpcTarget$1 } from "./cloudflare-workers.js";
3
4
  import { a as SqliteDatabase, c as getText, l as hasCurrentSqliteTable, o as getBlob, s as getInt64, t as ensureRuntimeStorageVersion, u as isNull } from "./chunks/sqlite-migrations-DsWmLP_B.js";
4
5
  import { ALARM_RETRY_MAX_TRIES, ALARM_RETRY_START_SECONDS, AlarmScheduler, RETRY_BACKOFF_MAX, RETRY_JITTER_FACTOR, alarmRetryDelayMs } from "./server/alarm-scheduler.js";
5
- import { deserialize, serialize } from "@ungap/structured-clone";
6
6
  import { RpcTarget, newMessagePortRpcSession } from "capnweb";
7
- //#region src/api/actor.ts
8
- /**
9
- * ← the `[1, 2048]` bound in `ColoLocalActorNamespace::get`.
10
- *
11
- * Upstream compares `actorId.size()`, which for a `kj::String` is **bytes**, so
12
- * this is measured in UTF-8 bytes rather than in UTF-16 code units. That costs
13
- * one `TextEncoder` pass and buys an exact match on a bound a caller can hit.
14
- */
15
- var MAX_COLO_LOCAL_ACTOR_ID_BYTES = 2048;
16
- /**
17
- * Upstream never faces this: JSG unwraps a `jsg::Ref<DurableObjectId>` parameter
18
- * and throws a `TypeError` before the method body runs, so `getInner()` cannot be
19
- * reached on something that is not one. Here the parameter type is
20
- * workers-types' structural `DurableObjectId` interface, which any object with a
21
- * `toString` and an `equals` satisfies — so the unwrap has to be written, and it
22
- * fails closed rather than guessing at the string form.
23
- */
24
- var FOREIGN_ACTOR_ID_MESSAGE$1 = "This DurableObjectId was not created by this runtime, so its underlying actor id cannot be read. Ids must come from newUniqueId(), idFromName() or idFromString() on a DurableObjectNamespace.";
25
- /** Substrate boundary: `jsg::Serializer`, `Frankenvalue` and channel tokens have no port. */
26
- var ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE = "DurableObjectClass cannot be serialized in this runtime: upstream writes a channel token through jsg::Serializer and Frankenvalue, and neither the token nor the serializer has an equivalent here.";
27
- /**
28
- * Replication is a named substrate boundary (`io/actor-cache.ts`), so no request
29
- * this file builds asks for replica routing. Upstream reads
30
- * `FeatureFlags::get(js).getReplicaRouting()` here.
31
- */
32
- var ENABLE_REPLICA_ROUTING = false;
33
- /**
34
- * ← `ColoLocalActorNamespace` (`actor.h:25-37`). "A capability to an ephemeral
35
- * Actor namespace."
36
- */
37
- var ColoLocalActorNamespace = class {
38
- #channel;
39
- constructor(channel) {
40
- this.#channel = channel;
41
- }
42
- /** ← `ColoLocalActorNamespace::get` (`actor.c++:116-129`). */
43
- get(actorId) {
44
- const bytes = utf8Length(actorId);
45
- if (!(bytes > 0 && bytes <= 2048)) throw new TypeError(`Actor ID length must be in the range [1, ${MAX_COLO_LOCAL_ACTOR_ID_BYTES}].`);
46
- return this.#channel.getColoLocalActor({ actorId });
47
- }
48
- };
49
- var textEncoder$1 = new TextEncoder();
50
- /** `kj::String::size()` is bytes; `String.prototype.length` is UTF-16 code units. */
51
- function utf8Length(value) {
52
- return textEncoder$1.encode(value).length;
53
- }
54
- /**
55
- * ← `DurableObjectId` (`actor.h:42-84`). "DurableObjectId type seen by
56
- * JavaScript."
57
- *
58
- * `name` and `jurisdiction` are read from the inner id **once, at construction**,
59
- * where upstream's are `JSG_READONLY_INSTANCE_PROPERTY`s that re-read it on every
60
- * access. That is not a preference: `@cloudflare/workers-types` declares both
61
- * `readonly name?: string`, and under `exactOptionalPropertyTypes` a getter
62
- * returning `string | undefined` does not satisfy an optional `string`. An own
63
- * property assigned only when the value exists does, and it is what keeps this
64
- * class assignable to the interface with no cast (§2.4). The one behaviour lost
65
- * is `ActorIdImpl::clearName()` (`server/actor-id-impl.h`) taking effect on an
66
- * already-wrapped id — a `server/`-internal that runs before the id reaches JS.
67
- */
68
- var DurableObjectId = class {
69
- #id;
70
- name;
71
- jurisdiction;
72
- constructor(id) {
73
- this.#id = id;
74
- const name = id.getName();
75
- if (name !== void 0) this.name = name;
76
- const jurisdiction = id.getJurisdiction();
77
- if (jurisdiction !== void 0) this.jurisdiction = jurisdiction;
78
- }
79
- /** ← `getInner()`. Not JS-visible upstream either; the outgoing factories take it. */
80
- getInner() {
81
- return this.#id;
82
- }
83
- /** "Converts to a string which can be passed back to the constructor to reproduce the same ID." */
84
- toString() {
85
- return this.#id.toString();
86
- }
87
- equals(other) {
88
- return this.#id.equals(innerIdOf(other));
89
- }
90
- };
91
- /** The unwrap JSG performs for a `jsg::Ref<DurableObjectId>` parameter. */
92
- function requireDurableObjectId(id) {
93
- if (id instanceof DurableObjectId) return id;
94
- throw new TypeError(FOREIGN_ACTOR_ID_MESSAGE$1);
95
- }
96
- function innerIdOf(id) {
97
- return requireDurableObjectId(id).getInner();
98
- }
99
- /**
100
- * ← `DurableObject` (`actor.h:87-139`). "Stub object used to send messages to a
101
- * remote durable object."
102
- *
103
- * Upstream's carries its whole behaviour by `JSG_INHERIT(Fetcher)` and adds
104
- * exactly two readonly properties. So does this: the `Fetcher` is the transport's
105
- * and everything except `id` and `name` belongs to it. `asDurableObjectStub`
106
- * below is where the inheritance goes.
107
- */
108
- var DurableObject = class {
109
- #id;
110
- #fetcher;
111
- constructor(id, fetcher) {
112
- this.#id = id;
113
- this.#fetcher = fetcher;
114
- }
115
- /** ← `JSG_READONLY_INSTANCE_PROPERTY(id, getId)`. */
116
- getId() {
117
- return this.#id;
118
- }
119
- /** ← `JSG_READONLY_INSTANCE_PROPERTY(name, getName)`. */
120
- getName() {
121
- return this.#id.name;
122
- }
123
- /** The `Fetcher` upstream inherits from rather than holds. */
124
- getFetcher() {
125
- return this.#fetcher;
126
- }
127
- };
128
- /**
129
- * ← `js.alloc<DurableObject>(...)` plus `JSG_INHERIT(Fetcher)` plus the
130
- * `JSG_TS_OVERRIDE` that renames the resource type to `DurableObjectStub`.
131
- *
132
- * The named assertion is the same one `io/worker.ts`'s `asFacetStub` makes and
133
- * for the same reason: `DurableObjectStub<T>` is `Fetcher<T, …> & { id, name }`,
134
- * and `Fetcher<T>` for an unresolved `T` is `Rpc.Provider<T, …>`, a conditional
135
- * type TypeScript defers until `T` is known — where `T` is the caller's claim
136
- * about a class it named, which no value can confirm. Upstream is in the same
137
- * position and answers it the same way, with the parameter living only inside a
138
- * `JSG_TS_OVERRIDE`.
139
- *
140
- * The `Proxy` is what JSG inheritance costs in JS. Two properties have to answer
141
- * from the id and every other property — `fetch`, `connect`, and every RPC method
142
- * name, which are the whole point of a stub — has to reach the transport with
143
- * `this` still bound to it. Bound methods are memoised so `stub.foo === stub.foo`,
144
- * which upstream gets for free by there being one object rather than two.
145
- */
146
- function asDurableObjectStub(object) {
147
- const fetcher = object.getFetcher();
148
- const bound = /* @__PURE__ */ new Map();
149
- return new Proxy(fetcher, {
150
- get(target, property) {
151
- if (property === "id") return object.getId();
152
- if (property === "name") return object.getName();
153
- const cached = bound.get(property);
154
- if (cached !== void 0) return cached;
155
- const value = Reflect.get(target, property, target);
156
- if (typeof value !== "function") return value;
157
- const method = value.bind(target);
158
- bound.set(property, method);
159
- return method;
160
- },
161
- has(target, property) {
162
- if (property === "id" || property === "name") return true;
163
- return Reflect.has(target, property);
164
- },
165
- ownKeys(target) {
166
- return [
167
- "id",
168
- "name",
169
- ...Reflect.ownKeys(target).filter((key) => key !== "id" && key !== "name")
170
- ];
171
- },
172
- getOwnPropertyDescriptor(target, property) {
173
- if (property === "id" || property === "name") return {
174
- value: property === "id" ? object.getId() : object.getName(),
175
- writable: false,
176
- enumerable: true,
177
- configurable: true
178
- };
179
- return Reflect.getOwnPropertyDescriptor(target, property);
180
- }
181
- });
182
- }
183
- /**
184
- * ← `DurableObjectNamespace` (`actor.h:142-291`). "Global durable object class
185
- * binding type."
186
- */
187
- var DurableObjectNamespace = class DurableObjectNamespace {
188
- #channel;
189
- #idFactory;
190
- constructor(channel, idFactory) {
191
- this.#channel = channel;
192
- this.#idFactory = idFactory;
193
- }
194
- /**
195
- * "Create a new unique ID for a durable object that will be allocated nearby
196
- * the calling colo."
197
- */
198
- newUniqueId(options) {
199
- return new DurableObjectId(this.#idFactory.newUniqueId(options?.jurisdiction ?? void 0));
200
- }
201
- /**
202
- * "Create a name-derived ID. Passing in the same `name` (to the same class)
203
- * will always produce the same ID."
204
- */
205
- idFromName(name) {
206
- return new DurableObjectId(this.#idFactory.idFromName(name));
207
- }
208
- /**
209
- * "Create a DurableObjectId from the stringified form of the ID (as produced by
210
- * calling `toString()` on a durable object ID). Throws if the ID is not a
211
- * 64-digit hex number, or if the ID was not originally created for this class."
212
- */
213
- idFromString(id) {
214
- return new DurableObjectId(this.#idFactory.idFromString(id));
215
- }
216
- /** "Gets a durable object by ID or creates it if it doesn't already exist." */
217
- get(id, options) {
218
- return this.#getImpl("GET_OR_CREATE", id, options);
219
- }
220
- /**
221
- * "Gets a durable object by name or creates it if it doesn't already exist.
222
- * Short for `idFromName()` followed by `get()`."
223
- */
224
- getByName(name, options) {
225
- return this.#getImpl("GET_OR_CREATE", this.idFromName(name), options);
226
- }
227
- /**
228
- * "Experimental. Gets a durable object by ID if it already exists. Currently,
229
- * gated for use by cloudflare only."
230
- *
231
- * Upstream exposes it only when the `durableObjectGetExisting` compat flag is
232
- * on, and `@cloudflare/workers-types` 4.20260702.1 does not declare it. It is
233
- * exposed unconditionally here, which is the current-behaviour reading every
234
- * other compat flag in this file gets.
235
- */
236
- getExisting(id, options) {
237
- return this.#getImpl("GET_EXISTING", id, options);
238
- }
239
- /**
240
- * "Creates a subnamespace with the jurisdiction hardcoded."
241
- *
242
- * The argument is optional because upstream's is a
243
- * `jsg::Optional<kj::Maybe<kj::String>>`, so both "omitted" and "null" mean the
244
- * same thing — `cloneWithJurisdiction(kj::none)`, a subnamespace with none.
245
- */
246
- jurisdiction(jurisdiction) {
247
- return new DurableObjectNamespace(this.#channel, this.#idFactory.cloneWithJurisdiction(jurisdiction ?? void 0));
248
- }
249
- /** ← `DurableObjectNamespace::getImpl` (`actor.c++:167-213`). */
250
- #getImpl(mode, id, options) {
251
- const durableObjectId = requireDurableObjectId(id);
252
- const inner = durableObjectId.getInner();
253
- if (!this.#idFactory.matchesJurisdiction(inner)) throw new TypeError("get called on jurisdictional subnamespace with an ID from a different jurisdiction");
254
- let routingMode = "DEFAULT";
255
- const requestedRoutingMode = options?.routingMode;
256
- if (requestedRoutingMode !== void 0) {
257
- if (requestedRoutingMode !== "primary-only") throw new RangeError(`unknown routingMode: ${requestedRoutingMode}`);
258
- routingMode = "PRIMARY_ONLY";
259
- }
260
- return asDurableObjectStub(new DurableObject(durableObjectId, this.#channel.getGlobalActor({
261
- id: inner,
262
- locationHint: options?.locationHint,
263
- mode,
264
- enableReplicaRouting: ENABLE_REPLICA_ROUTING,
265
- routingMode,
266
- version: actorVersionOf(options?.version)
267
- })));
268
- }
269
- };
270
- /**
271
- * ← `version = ActorVersion{.cohort = kj::mv(v.cohort)}` (`actor.c++:186-190`),
272
- * behind `FeatureFlags::get(js).getEnableVersionApi()` which this file reads as
273
- * on. A version with no cohort is still a version, which is why the empty object
274
- * is not collapsed to `undefined`.
275
- */
276
- function actorVersionOf(version) {
277
- if (version === void 0) return void 0;
278
- return version.cohort === void 0 ? {} : { cohort: version.cohort };
279
- }
280
- /**
281
- * ← `DurableObjectClass` (`actor.h:367-393`). "DurableObjectClass represents a
282
- * binding to a Durable Object class that can be used as a facet. The only use of
283
- * this type is to pass to `ctx.facets.get()`."
284
- *
285
- * `getChannel()` takes no `IoContext` because the parameter existed to resolve the
286
- * numbered-channel arm, and there is no numbered-channel arm here.
287
- */
288
- var DurableObjectClass = class {
289
- #channel;
290
- constructor(channel) {
291
- this.#channel = channel;
292
- }
293
- /** ← `DurableObjectClass::getChannel` (`actor.c++:232-242`). */
294
- getChannel() {
295
- return this.#channel;
296
- }
297
- /**
298
- * ← `DurableObjectClass::serialize` (`actor.c++:244-306`). Substrate boundary.
299
- *
300
- * `requireAllowsTransfer()` runs first, exactly as upstream's does, so a class
301
- * that refuses transfer reports that rather than the boundary — the refusal is
302
- * the more specific answer and it is the one upstream would give too.
303
- */
304
- serialize() {
305
- this.#channel.requireAllowsTransfer();
306
- throw new Error(ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE);
307
- }
308
- /** ← `DurableObjectClass::deserialize` (`actor.c++:308-359`). Substrate boundary. */
309
- static deserialize() {
310
- throw new Error(ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE);
311
- }
312
- };
313
- //#endregion
314
- //#region src/api/export-loopback.ts
315
- /**
316
- * ← what JSG's struct unwrapper does with a value that is not an object
317
- * (`jsg/struct.h:246`). Undefined and null are **not** in that set: a struct
318
- * whose every field is optional — which both option structs here are — unwraps
319
- * from either as an empty struct (`jsg/struct.h:236-243`), so `ctx.exports.Foo()`
320
- * is upstream's own empty-options call and not an error.
321
- */
322
- var LOOPBACK_OPTIONS_NOT_AN_OBJECT_MESSAGE = "A ctx.exports binding is invoked with an options object: pass { props }, or nothing at all.";
323
- /** ← what JSG does unwrapping a `jsg::JsRef<jsg::JsObject>` from a non-object. */
324
- var LOOPBACK_PROPS_NOT_AN_OBJECT_MESSAGE = "`props` must be an object. Upstream unwraps it as a jsg::JsObject, which refuses anything else.";
325
- /**
326
- * ← `LoopbackServiceStub` (`export-loopback.h:18-109`).
327
- *
328
- * Upstream is a `Fetcher` on the loopback channel and holds the channel number a
329
- * second time so `callImpl` can re-specialize it. Here the `Fetcher` is the
330
- * transport's — `api/http.{h,c++}` is not ported — so the unspecialized stub is
331
- * what the factory returns for a request with no props and no version, and the
332
- * factory is the thing held twice over.
333
- */
334
- var LoopbackServiceStub = class {
335
- #channel;
336
- #fetcher;
337
- constructor(channel) {
338
- this.#channel = channel;
339
- this.#fetcher = channel.getSubrequestChannel({
340
- props: void 0,
341
- version: void 0
342
- });
343
- }
344
- /** The `Fetcher` upstream inherits from rather than holds, as `DurableObject`'s is. */
345
- getFetcher() {
346
- return this.#fetcher;
347
- }
348
- /**
349
- * ← `LoopbackServiceStub::callImpl` (`export-loopback.c++:11-29`) reached
350
- * through `callWithVersion` (`export-loopback.h:53-55`), which is the callable
351
- * when `enableVersionApi` is on. "Create a specialized Fetcher which can be
352
- * passed over RPC."
353
- */
354
- callWithVersion(options) {
355
- return this.#channel.getSubrequestChannel({
356
- props: requireProps(options.props),
357
- version: versionRequestOf(options.version)
358
- });
359
- }
360
- };
361
- /**
362
- * ← `LoopbackDurableObjectClass` (`export-loopback.h:116-148`). "Similar to
363
- * LoopbackServiceStub, but for actor classes … this is used for actor classes
364
- * that do *not* have any storage configured. If you simply export a class
365
- * extending `DurableObject` but you don't configure storage for it, it shows up
366
- * in `ctx.exports` as this type. This can be used to create a Durable Object
367
- * facet."
368
- *
369
- * Upstream's base `DurableObjectClass` holds the channel *number*, and
370
- * `getChannel(ioctx)` resolves it lazily. There is no numbered arm here, so the
371
- * unspecialized channel is requested once, in the constructor — which is the same
372
- * value `getActorClass(channel)` with default props would have produced.
373
- */
374
- var LoopbackDurableObjectClass = class extends DurableObjectClass {
375
- #channel;
376
- constructor(channel) {
377
- super(channel.getActorClass({ props: void 0 }));
378
- this.#channel = channel;
379
- }
380
- /**
381
- * ← `LoopbackDurableObjectClass::call` (`export-loopback.c++:31-40`). "Create a
382
- * specialized DurableObjectClass which can be passed over RPC."
383
- *
384
- * The result is a plain `DurableObjectClass`, as `js.alloc<DurableObjectClass>`
385
- * is: specializing a loopback class does not produce another loopback class.
386
- */
387
- call(options) {
388
- return new DurableObjectClass(this.#channel.getActorClass({ props: requireProps(options.props) }));
389
- }
390
- };
391
- function asLoopbackDurableObjectClass(actorClass) {
392
- return asCallable({
393
- properties: actorClass,
394
- prototype: Object.getPrototypeOf(actorClass),
395
- call: (options) => actorClass.call(requireOptions(options))
396
- });
397
- }
398
- /**
399
- * ← `LoopbackDurableObjectNamespace` (`export-loopback.h:155-189`).
400
- *
401
- * Upstream: "used when the class has storage configured. In this case, we want a
402
- * binding that behaves *both* like a LoopbackDurableObjectClass *and* like a
403
- * DurableObjectNamespace binding. Easy enough, we'll inherit
404
- * DurableObjectNamespace, but also make the binding invokable as a function like
405
- * LoopbackDurableObjectClass."
406
- */
407
- var LoopbackDurableObjectNamespace = class extends DurableObjectNamespace {
408
- #loopbackClass;
409
- constructor(channel, idFactory, loopbackClass) {
410
- super(channel, idFactory);
411
- this.#loopbackClass = loopbackClass;
412
- }
413
- /** ← `getClass()`. "getClass() accessor for use from C++ only." */
414
- getClass() {
415
- return this.#loopbackClass;
416
- }
417
- /** ← `call`. "Invoking the binding creates a specialization of the class -- not the namespace." */
418
- call(options) {
419
- return this.#loopbackClass.call(options);
420
- }
421
- };
422
- /**
423
- * ← `LoopbackColoLocalActorNamespace` (`export-loopback.h:192-220`). "Like
424
- * LoopbackDurableObjectNamespace, but for colo-local (ephemeral) actor
425
- * namespaces."
426
- */
427
- var LoopbackColoLocalActorNamespace = class extends ColoLocalActorNamespace {
428
- #loopbackClass;
429
- constructor(channel, loopbackClass) {
430
- super(channel);
431
- this.#loopbackClass = loopbackClass;
432
- }
433
- /** ← `getClass()`. "getClass() accessor for use from C++ only." */
434
- getClass() {
435
- return this.#loopbackClass;
436
- }
437
- /** ← `call`. "Invoking the binding creates a specialization of the class -- not the namespace." */
438
- call(options) {
439
- return this.#loopbackClass.call(options);
440
- }
441
- };
442
- /**
443
- * ← `JSG_CALLABLE`, which makes a JSG resource object invocable while leaving
444
- * every other property answering from the resource type.
445
- *
446
- * `properties` is where reads land, with `this` bound to it so a method reaching
447
- * a private field still finds one — the same binding, memoised the same way,
448
- * that `asDurableObjectStub` performs for `JSG_INHERIT`. `prototype` is what
449
- * `instanceof` sees, and it is separate from `properties` because
450
- * `LoopbackServiceStub` is one object with two halves: upstream's identity is the
451
- * resource type while its behaviour is the inherited `Fetcher`'s.
452
- *
453
- * The target is an arrow function rather than a plain one because a plain
454
- * function has a non-configurable own `prototype` property, which a Proxy may not
455
- * hide from `ownKeys`.
456
- *
457
- * This is the one assertion the four producers above need, made once here. It is
458
- * `asDurableObjectStub`'s, for `asDurableObjectStub`'s reason: the declared value
459
- * is a `Fetcher<T>` or a `DurableObjectClass<T>` intersected with a call
460
- * signature, and `T` is the caller's claim about a class it named, which no value
461
- * can confirm. Upstream states the same shapes the same way, in a
462
- * `JSG_TS_OVERRIDE` that no C++ value is checked against either.
463
- */
464
- var INVOCATION_METHODS = /* @__PURE__ */ new Set([
465
- "call",
466
- "apply",
467
- "bind"
468
- ]);
469
- function asCallable(facade) {
470
- const bound = /* @__PURE__ */ new Map();
471
- const target = () => {
472
- throw new Error("unreachable: the apply trap answers every invocation");
473
- };
474
- return new Proxy(target, {
475
- apply(_target, _thisArg, args) {
476
- return facade.call(args[0]);
477
- },
478
- get(target, property, receiver) {
479
- if (INVOCATION_METHODS.has(property)) return Reflect.get(target, property, receiver);
480
- const cached = bound.get(property);
481
- if (cached !== void 0) return cached;
482
- const value = Reflect.get(facade.properties, property, facade.properties);
483
- if (typeof value !== "function") return value;
484
- const method = value.bind(facade.properties);
485
- bound.set(property, method);
486
- return method;
487
- },
488
- has(_target, property) {
489
- return Reflect.has(facade.properties, property);
490
- },
491
- ownKeys() {
492
- return Reflect.ownKeys(facade.properties);
493
- },
494
- getOwnPropertyDescriptor(_target, property) {
495
- const descriptor = Reflect.getOwnPropertyDescriptor(facade.properties, property);
496
- if (descriptor === void 0) return void 0;
497
- return {
498
- ...descriptor,
499
- configurable: true
500
- };
501
- },
502
- getPrototypeOf() {
503
- return facade.prototype;
504
- }
505
- });
506
- }
507
- /**
508
- * ← JSG's struct unwrapper (`jsg/struct.h:236-246`), for the two option structs
509
- * here: every field of both is optional, so undefined and null yield an empty
510
- * struct and anything that is not an object is a `TypeError`. V8's `IsObject()`
511
- * is true for functions, which is why one is not refused here either.
512
- *
513
- * `cohort` is not checked, because upstream does not check it: JSG's `kj::String`
514
- * unwrapper calls `ToString` on whatever it is given (`jsg/value.h:501-506`), so
515
- * refusing a non-string here would refuse what workerd coerces. That is the same
516
- * reading `api/actor.ts`'s `actorVersionOf` already takes of the same field.
517
- */
518
- function requireOptions(options) {
519
- if (options !== void 0 && options !== null && typeof options !== "object" && typeof options !== "function") throw new TypeError(LOOPBACK_OPTIONS_NOT_AN_OBJECT_MESSAGE);
520
- return options ?? {};
521
- }
522
- /** ← `jsg::Optional<jsg::JsRef<jsg::JsObject>> props` — present means an object. */
523
- function requireProps(props) {
524
- if (props === void 0) return void 0;
525
- if (props === null || typeof props !== "object" && typeof props !== "function") throw new TypeError(LOOPBACK_PROPS_NOT_AN_OBJECT_MESSAGE);
526
- return props;
527
- }
528
- /** ← `.cohort = kj::mv(version.cohort).orDefault(kj::none)` (`export-loopback.c++:19-23`). */
529
- function versionRequestOf(version) {
530
- if (version === void 0) return void 0;
531
- return { cohort: version.cohort ?? void 0 };
532
- }
533
- //#endregion
534
7
  //#region src/api/worker-loader.ts
535
8
  /** ← `JSG_REQUIRE(code.modules.fields.size() > 0, …)` (`worker-loader.c++:175-176`). */
536
9
  var NO_MODULES_MESSAGE = "Dynamic Worker code must contain at least one module.";
@@ -851,7 +324,7 @@ function objectModuleContentOf(name, module) {
851
324
  };
852
325
  if (module.data !== void 0) return {
853
326
  type: "dataModule",
854
- body: copyBytes$1(module.data)
327
+ body: copyBytes(module.data)
855
328
  };
856
329
  if (module.json !== void 0) return {
857
330
  type: "jsonModule",
@@ -863,1642 +336,112 @@ function objectModuleContentOf(name, module) {
863
336
  };
864
337
  if (module.wasm !== void 0) return {
865
338
  type: "wasmModule",
866
- body: copyBytes$1(module.wasm)
867
- };
868
- throw new Error("unreachable: exactly one module field is set");
869
- }
870
- /**
871
- * ← `jsg::asBytes()` followed by `kj::heapArray<const kj::byte>(data.asPtr())`
872
- * (`worker-loader.c++:231`, `:244`). The copy is the whole point — see the caller.
873
- */
874
- function copyBytes$1(value) {
875
- if (value instanceof ArrayBuffer) return new Uint8Array(value.slice(0));
876
- if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength));
877
- throw new TypeError(NOT_BYTES_MESSAGE);
878
- }
879
- /**
880
- * ← `Fetcher::getSubrequestChannel(ioctx)` followed by
881
- * `channel->requireAllowsTransfer()` (`worker-loader.c++:125-126`, `:144-145`,
882
- * `:157-158`).
883
- *
884
- * **The check has nothing to call, and that is a recorded divergence rather than
885
- * an omission.** Upstream's `SubrequestChannel` carries `requireAllowsTransfer()`;
886
- * here a `SubrequestChannel` and the `Fetcher` built over it are one object
887
- * (`io/io-channels.ts`'s header, and the same collapse `api/actor.ts` and
888
- * `api/export-loopback.ts` already made), and a `Fetcher` is
889
- * `@cloudflare/workers-types`' interface with no such member. The one refusal it
890
- * produces in the open-source runtime is `throwDynamicEntrypointTransferError`
891
- * (`server.c++:167-173`), raised by `WorkerService::requireAllowsTransfer` when
892
- * `isDynamic` — an isolate-host fact here, exactly as it is a `server/` fact there.
893
- *
894
- * Kept as a named function rather than inlined so the three call sites read as
895
- * upstream's do, and so there is one place to put the check if a `Fetcher` seam
896
- * ever grows one.
897
- */
898
- function requireTransferableChannel(fetcher) {
899
- return fetcher;
900
- }
901
- /**
902
- * ← `Frankenvalue::fromJs(js, …)`, whose serializer refuses any object whose JSG
903
- * resource type declares no `JSG_SERIALIZABLE` (`jsg/ser.c++:175-183`).
904
- *
905
- * Four types reachable from this package are in that set, and all four for one
906
- * reason: `JSG_INHERIT` does not carry serializability. Upstream says so twice — in
907
- * `export-loopback.h:57-58` ("Note that `LoopbackServiceStub` is intentionally NOT
908
- * serializable, unlike its parent class Fetcher") and in the test that pins it,
909
- * whose comment reads "it's more testing LoopbackServiceStub and that
910
- * serializability is not inherited" (`worker-loader-test.js:91-92`). None of
911
- * `LoopbackServiceStub`, `LoopbackDurableObjectClass`,
912
- * `LoopbackDurableObjectNamespace` or `LoopbackColoLocalActorNamespace` declares
913
- * `JSG_SERIALIZABLE`; every one of them *invoked* produces something that does — a
914
- * `Fetcher` or a plain `DurableObjectClass` — which is exactly the distinction
915
- * `worker-loader-test.js:62-107` measures, accepting the invoked form in `props`
916
- * and refusing the bare binding.
917
- *
918
- * **This discharges the obligation the README left open on Section 7.** That row
919
- * says the refusal would have to come from `src/transport/` if a `Fetcher` ever
920
- * became serializable; the layer upstream refuses at is this one, because
921
- * `Frankenvalue::fromJs` runs inside `getEntrypoint`, `getDurableObjectClass` and
922
- * `toDynamicWorkerSource`. Putting it here makes it reachable today rather than
923
- * conditional on a transport change that has not happened, and it depends on no
924
- * transport fact — so `api/` still imports no transport library.
925
- *
926
- * The walk descends into plain objects and arrays only. Everything else is a host
927
- * object: either one the substrate knows how to carry (a `Fetcher`, a
928
- * `DurableObjectClass`, an `RpcTarget`) or one of the four above. Walking a host
929
- * object's own keys would drive proxy traps — a stub's `get` mints an RPC import —
930
- * which is a cost upstream's serializer never pays, because it asks the type rather
931
- * than the value.
932
- */
933
- function requireSerializableProps(root, field) {
934
- const seen = /* @__PURE__ */ new Set();
935
- const visit = (value, path) => {
936
- if (value === null || typeof value !== "object" && typeof value !== "function") return;
937
- const subject = value;
938
- const refused = notSerializableType(subject);
939
- if (refused !== void 0) throw new DOMException(`${notSerializableMessage(refused)} At ${path}.`, "DataCloneError");
940
- if (seen.has(subject)) return;
941
- seen.add(subject);
942
- if (Array.isArray(subject)) {
943
- subject.forEach((entry, index) => {
944
- visit(entry, `${path}[${index}]`);
945
- });
946
- return;
947
- }
948
- const prototype = Object.getPrototypeOf(subject);
949
- if (prototype !== Object.prototype && prototype !== null) return;
950
- for (const [name, entry] of Object.entries(subject)) visit(entry, `${path}.${name}`);
339
+ body: copyBytes(module.wasm)
951
340
  };
952
- visit(root, `<${field}>`);
953
- return root;
954
- }
955
- /**
956
- * The four `ctx.exports` binding types, named by the class whose name
957
- * `GetConstructorName()` would report.
958
- *
959
- * The four are mutually exclusive — each extends a different base
960
- * (`api/export-loopback.ts`) so the order is presentational. What the order does
961
- * NOT do is reach a base class: `DurableObjectClass`, `DurableObjectNamespace`,
962
- * `ColoLocalActorNamespace` and `Fetcher` are all serializable upstream
963
- * (`actor.h:389`), and it is exactly `JSG_INHERIT`'s failure to carry
964
- * serializability that makes the four subclasses refuse where their bases accept.
965
- */
966
- function notSerializableType(value) {
967
- if (value instanceof LoopbackServiceStub) return "LoopbackServiceStub";
968
- if (value instanceof LoopbackDurableObjectNamespace) return "LoopbackDurableObjectNamespace";
969
- if (value instanceof LoopbackColoLocalActorNamespace) return "LoopbackColoLocalActorNamespace";
970
- if (value instanceof LoopbackDurableObjectClass) return "LoopbackDurableObjectClass";
971
- }
972
- //#endregion
973
- //#region src/api/sql.ts
974
- /**
975
- * ← workerd `src/workerd/api/sql.{h,c++}`
976
- *
977
- * `SqlStorage` and its two nested types. ~330 call sites depend on this — it is
978
- * the real storage layer, not KV.
979
- *
980
- * Four things about the translation, in descending order of how much they cost:
981
- *
982
- * 1. **The cursor is materialised, not live.** Upstream's `Cursor` owns a
983
- * running `SqliteDatabase::Query` and pulls one row at a time; the backend
984
- * seam this package chose (`SqlDatabase.exec` → `SqlResult`) has already
985
- * collected every row before a cursor exists. Everything downstream of that
986
- * follows: there is no statement cache, so `CachedStatement`, the 1 MiB LRU
987
- * and `reusedCachedQueryForTest` are absent with it; there is no live
988
- * statement to cancel, so `Cursor::canceled` and `selfRef` — both already
989
- * dead upstream, written but never assigned — have nothing to guard; and
990
- * `endQuery`'s job of returning a statement to the cache is nothing here, so
991
- * the counters it saves off are simply the counters. What is kept is every
992
- * observable: the position is shared across `next`/`toArray`/`one`/`raw`,
993
- * and a drained cursor keeps yielding done.
994
- * 2. **`Cursor` and `Statement` must be constructible with no arguments**, or
995
- * `SqlStorage` cannot satisfy workers-types without a cast: the interface
996
- * types them `typeof SqlStorageCursor` / `typeof SqlStorageStatement`, and
997
- * both are `abstract` there, so their construct signatures take none.
998
- * Upstream's are unconstructible from JS for the same reason they are
999
- * `abstract` in the types — JSG nested types have no JS constructor — so the
1000
- * faithful shape is a constructor that refuses. `sql.Cursor` exists for
1001
- * `instanceof`, which is all upstream exposes it for.
1002
- * 3. **The regulator is ported whole, and that is not the whole authorizer.**
1003
- * Four of its five members are here as callbacks, and none needed the
1004
- * authorizer to compute anything — `isAllowedName` is a prefix test,
1005
- * `isAllowedTrigger` is `return true`, `allowTransactions` throws,
1006
- * `shouldAddQueryStats` is a constant. The fifth, `onError`, is not a
1007
- * callback at all in this port: it is every `throw new Error(message)`
1008
- * below, which is all its upstream body does with a refusal message.
1009
- * For those, what the authorizer supplied was the *identifiers*:
1010
- * with none, the statement text is the only source, so `exec` tokenizes it
1011
- * and runs `isAllowedName` over every identifier-shaped token. That is
1012
- * deliberately STRICTER than upstream — see `SQL_RESERVED_PREFIX_MESSAGE`.
1013
- *
1014
- * But the authorizer also makes decisions no callback ever sees, and those
1015
- * do NOT arrive with the regulator: `SQLITE_ATTACH` / `SQLITE_DETACH`, the
1016
- * `SQLITE_CREATE_TEMP_*` family and the `temp` schema, `SQLITE_PRAGMA`, and
1017
- * the `SQLITE_CREATE_VTABLE` module list. Each is refused from the text in
1018
- * `refuseUnauthorizedForms` and `requireAllowedPragmas`. `SQLITE_FUNCTION`
1019
- * is the one still unported — see the README divergence row.
1020
- * 4. **`ingest` stays at upstream's SQLite seam.** `SqliteDatabase.ingest()`
1021
- * executes every complete statement and returns the partial tail, using the
1022
- * same compiled boundaries and regulator as `exec`.
1023
- *
1024
- * Spec: §1.4, §2.4 in docs/decisions.md.
1025
- */
1026
- /**
1027
- * ← `SqlStorageRegulator::allowTransactions()`, copied verbatim. Users match on
1028
- * it and it is the one regulator callback our substrate can still answer.
1029
- */
1030
- var SQL_TRANSACTION_REFUSED_MESSAGE = "To execute a transaction, please use the state.storage.transaction() or state.storage.transactionSync() APIs instead of the SQL BEGIN TRANSACTION or SAVEPOINT statements. The JavaScript API is safer because it will automatically roll back on exceptions, and because it interacts correctly with Durable Objects' automatic atomic write coalescing.";
1031
- /** See translation 2 in the header: the class is exposed for `instanceof` only. */
1032
- var CURSOR_NOT_CONSTRUCTIBLE_MESSAGE = "Illegal invocation: SqlStorage.Cursor cannot be constructed directly. Use sql.exec().";
1033
- /** Same, for the prepared-statement compatibility shim. */
1034
- var STATEMENT_NOT_CONSTRUCTIBLE_MESSAGE = "Illegal invocation: SqlStorage.Statement cannot be constructed directly. Use sql.prepare().";
1035
- /**
1036
- * ← SQLite's own denial text, with the reason appended.
1037
- *
1038
- * There is no upstream string to copy here: `SqlStorageRegulator::onError` just
1039
- * rethrows whatever SQLite produced, and SQLite produces `not authorized` for an
1040
- * authorizer denial (`access to X.Y is prohibited` for the column-read case,
1041
- * which needs a resolved identifier we do not have). The prefix is kept so that
1042
- * anything matching upstream still matches; the rest is here because a bare
1043
- * `not authorized` is not debuggable.
1044
- */
1045
- var SQL_RESERVED_PREFIX_MESSAGE = "not authorized: a SQL statement may not name the reserved _cf_ prefix, which is where this Durable Object keeps its own KV and metadata tables.";
1046
- /**
1047
- * ← the five transaction-control forms `sqlite3_stmt_readonly()` reports
1048
- * read-only and the authorizer reports as `SQLITE_TRANSACTION` /
1049
- * `SQLITE_SAVEPOINT`. The same set `util/sqlite.ts` classifies, read here from
1050
- * the leading keyword because the untrusted path has to refuse them before the
1051
- * trusted one applies them.
1052
- *
1053
- * `;` counts as leading trivia here and in every other leading-keyword refusal
1054
- * below. A statement boundary comes from the backend, and `node:sqlite` reports
1055
- * an empty leading statement as part of the span it compiled: the `sourceSQL`
1056
- * for `;ATTACH ...` is the whole string, so an anchor of `^\s*` would read the
1057
- * keyword as `;` and let the compiled ATTACH through. The browser backend cuts
1058
- * the same input at the first `;` and refuses the empty statement instead.
1059
- */
1060
- var TRANSACTION_CONTROL = /^[\s;]*(?:BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
1061
- /** Cheap pre-test, so the tokenizer below runs only on a statement that could fail it. */
1062
- var RESERVED_PREFIX_HINT = /_cf_/i;
1063
- /** A SQL identifier. Double-quoted and bracketed forms are still identifiers, so only the
1064
- * delimiters are stripped and the word inside is scanned like any other. */
1065
- var IDENTIFIER = /[A-Za-z_][A-Za-z0-9_$]*/g;
1066
- /**
1067
- * Everything in a statement an identifier cannot come from: single-quoted string
1068
- * literals (SQLite escapes an embedded quote by doubling it), `--` line comments,
1069
- * and `/* *\/` block comments. Replaced with a space before tokenizing, so what is
1070
- * left is code.
1071
- *
1072
- * Backtick-quoted names are NOT here: MySQL-compatible quoting produces an
1073
- * identifier, exactly as the double-quoted form does.
1074
- */
1075
- var NOT_CODE = /'(?:[^']|'')*'|--[^\n]*|\/\*[\s\S]*?\*\//g;
1076
- /**
1077
- * Comments are never code. String literals STAY, for both of this regex's callers: pragma
1078
- * arguments may be quoted, and SQLite's misquoting feature reads a single-quoted string as an
1079
- * identifier — `CREATE TABLE 'temp'.t(x)` really creates a temp-schema table — so the checks
1080
- * that read identifier positions must still see it. `NOT_CODE` blanks literals, which is right
1081
- * for the token scans and would be a bypass for these callers.
1082
- */
1083
- var NOT_COMMENT = /--[^\n]*|\/\*[\s\S]*?\*\//g;
1084
- /**
1085
- * ← `SqlStorageRegulator` (`sql.h:15-22`, `sql.c++:141-165`), whole.
1086
- *
1087
- * Upstream reaches these through the SQLite authorizer while a statement is
1088
- * being compiled. `exec` calls them from the statement text instead, which is
1089
- * the same translation Section 3 made for the write classifier and for
1090
- * transaction state.
1091
- */
1092
- var SqlStorageRegulator = {
1093
- /**
1094
- * Upstream's body is `return !name.startsWith("_cf_")`, with an autogate that
1095
- * makes the comparison case-insensitive and logs a warning until it lands. The
1096
- * case-insensitive form is taken here: it is the direction upstream is moving,
1097
- * and there is no logger for the warning half.
1098
- */
1099
- isAllowedName(name) {
1100
- return name.length < 4 || name.slice(0, 4).toLowerCase() !== "_cf_";
1101
- },
1102
- /** Upstream's body is `return true`. */
1103
- isAllowedTrigger(_name) {
1104
- return true;
1105
- },
1106
- /** Upstream's body is a `JSG_FAIL_REQUIRE` with this message. */
1107
- allowTransactions() {
1108
- throw new Error(SQL_TRANSACTION_REFUSED_MESSAGE);
1109
- },
1110
- /** "Bill for queries executed from JavaScript." Nothing reads it — `SqliteObserver` has no port. */
1111
- shouldAddQueryStats() {
1112
- return true;
1113
- }
1114
- };
1115
- /**
1116
- * The text-level stand-in for the authorizer's `isAllowedName` calls — see
1117
- * `SQL_RESERVED_PREFIX_MESSAGE` and the README row.
1118
- *
1119
- * Upstream refuses a *resolved identifier* that starts with `_cf_`, because it
1120
- * reaches `isAllowedName` through the SQLite authorizer while the statement is
1121
- * being compiled. There is no authorizer here, so this tokenizes the statement
1122
- * text instead, over everything that is not a string literal or a comment —
1123
- * which is the same set of characters an identifier can come from.
1124
- *
1125
- * **The literals were once refused too, and that was wrong.** The first draft
1126
- * scanned the whole statement on the reasoning that no legitimate consumer
1127
- * statement contains the token, so being stricter than upstream was the safe
1128
- * direction. A retained conformance case uses `_cf_keepAliveHeartbeat` as a
1129
- * bound value: real workerd accepts it, so this parser must distinguish data
1130
- * from identifiers. `conformance/suite/sql.spec.ts` pins the rule: refused as a
1131
- * table name and as a quoted identifier, allowed as data.
1132
- *
1133
- * A name that merely CONTAINS the token — `my_cf_thing` — stays allowed, because
1134
- * `isAllowedName` tests a prefix.
1135
- */
1136
- function requireAllowedNames(query) {
1137
- if (!RESERVED_PREFIX_HINT.test(query)) return;
1138
- const code = query.replace(NOT_CODE, " ");
1139
- for (const [token] of code.matchAll(IDENTIFIER)) if (!SqlStorageRegulator.isAllowedName(token)) throw new Error(SQL_RESERVED_PREFIX_MESSAGE);
1140
- }
1141
- /** Refuse transaction control against one SQLite-decided statement boundary. */
1142
- function refuseTransactionControl(statement) {
1143
- const code = statement.replace(NOT_CODE, " ");
1144
- if (TRANSACTION_CONTROL.test(code)) SqlStorageRegulator.allowTransactions();
1145
- }
1146
- /**
1147
- * ← the message a `SQLITE_DENY` from the authorizer surfaces to JavaScript,
1148
- * byte-identical so a caller matching on it ports unchanged.
1149
- */
1150
- var SQL_NOT_AUTHORIZED_MESSAGE = "not authorized: SQLITE_AUTH";
1151
- /**
1152
- * The forms neither the regulator nor any callback sees: the authorizer's own
1153
- * action codes, plus `VACUUM`, which SQLite itself refuses by precondition.
1154
- *
1155
- * Porting `SqlStorageRegulator` whole (point 3 in the file header) carried over its members, but
1156
- * not the authorizer's own action codes: `SQLITE_ATTACH`, `SQLITE_DETACH`, `SQLITE_CREATE_TEMP_*`
1157
- * and `SQLITE_CREATE_VTABLE` consult no callback, so nothing here refused them. Every form below
1158
- * was measured on real workerd through the conformance oracle, not inferred.
1159
- *
1160
- * `ATTACH` and `DETACH` are also the isolation boundary rather than a fidelity detail: both
1161
- * backends open a real file, so on `node:sqlite` an `ATTACH` reads another actor's database and
1162
- * a `VACUUM INTO` writes anywhere the process can. The reserved-name scan does not cover it,
1163
- * because that scan tokenizes the SUBMITTED statement — `other._cf_KV` is caught, and every
1164
- * application table in the same attached database is not.
1165
- */
1166
- var DATABASE_ATTACHMENT = /^[\s;]*(?:ATTACH|DETACH)\b/i;
1167
- /**
1168
- * ← the `SQLITE_CREATE_TEMP_*` denials (`sqlite.c++:1323`) and the `dbName == temp` rule
1169
- * (`sqlite.c++:1073`), which permits a temp-schema database name only `READ` and `UPDATE`.
1170
- * Upstream's own reason to deny them applies here unchanged: a temporary table makes SQLite
1171
- * open a separate temporary file that the storage engine knows nothing about.
1172
- *
1173
- * Two spellings, one refusal each by its own upstream path: `CREATE TEMP TABLE t(x)` is the
1174
- * keyword and hits the action codes; `CREATE TABLE temp.t(x)` is the schema qualifier and hits
1175
- * the `dbName` rule — and the qualifier was the live gap, where the table was created, written
1176
- * and read back here while workerd refused it outright. The qualifier accepts every quoting
1177
- * SQLite does, single quotes included, with or without whitespace after the keyword (measured:
1178
- * workerd refuses `CREATE TABLE 'temp'.t(x)` and `CREATE TABLE"temp".t(x)` the same way), and
1179
- * is matched only in the object-name position, so an application table merely NAMED `tempest`
1180
- * or `temporary_log` is untouched. The keyword spelling needs no qualifier arm of its own here:
1181
- * `TEMP_SCHEMA` already refuses every `CREATE TEMP…` before this pattern is consulted.
1182
- *
1183
- * The rest of the `dbName` rule goes unported on purpose: with no way to create a temp-schema
1184
- * object, `INSERT`/`DELETE`/`DROP` against one die in SQLite as `no such table`, and the one
1185
- * silent form measures identically — workerd allows `DROP TABLE IF EXISTS temp.ghost` too.
1186
- */
1187
- var TEMP_SCHEMA = /^[\s;]*CREATE\s+(?:TEMP|TEMPORARY)\b/i;
1188
- var TEMP_QUALIFIED = /^[\s;]*CREATE\s+(?:UNIQUE\s+|VIRTUAL\s+)?(?:TABLE|VIEW|TRIGGER|INDEX)\s*(?:IF\s+NOT\s+EXISTS\s*)?(?:"temp"|'temp'|`temp`|\[temp\]|temp)\s*\./i;
1189
- /**
1190
- * ← `SQLITE_CREATE_VTABLE` (`sqlite.c++:1298-1316`): a virtual table is native-code callbacks, so
1191
- * upstream allows exactly four modules — FTS5 and its `fts5vocab` companion, R*Tree and its
1192
- * `rtree_i32` variant — and denies every other module SQLite was compiled with.
1193
- *
1194
- * `dbstat` is why this is not only fidelity: it reports a row per table with page counts and
1195
- * byte sizes, so `SELECT name FROM d` enumerates `_cf_KV` and the rest of the runtime's own
1196
- * tables without the statement ever naming them — around `requireAllowedNames`, which can only
1197
- * see the text it was given.
1198
- *
1199
- * The table name and the module accept every quoting SQLite does — double quotes, backticks,
1200
- * brackets, and the misquoting feature's single-quoted string, with or without whitespace
1201
- * before them — because the module has to be read from PAST the name, and a guessed name
1202
- * boundary is a bypass in both directions: an unparseable name skipped the check, and
1203
- * `"a USING fts5 b" USING dbstat` read its module out of the quoted name. Measured: workerd
1204
- * refuses both, refuses `CREATE VIRTUAL TABLE"d"USING dbstat`, resolves `USING "dbstat"` to
1205
- * the same denial, and allows `USING 'fts5'`. A `CREATE VIRTUAL TABLE` whose module the
1206
- * pattern cannot read is refused outright — the deliberately stricter direction the
1207
- * unparseable-PRAGMA fallback below already takes.
1208
- */
1209
- var SQL_IDENTIFIER_SOURCE = /"(?:[^"]|"")*"|'(?:[^']|'')*'|`(?:[^`]|``)*`|\[[^\]]*\]|[A-Za-z_\u0080-\uffff][A-Za-z0-9_$\u0080-\uffff]*/.source;
1210
- var VIRTUAL_TABLE = /^[\s;]*CREATE\s+VIRTUAL\s+TABLE\b/i;
1211
- var VIRTUAL_TABLE_MODULE = new RegExp(String.raw`^[\s;]*CREATE\s+VIRTUAL\s+TABLE\s*(?:IF\s+NOT\s+EXISTS\s*)?(?:${SQL_IDENTIFIER_SOURCE})\s*(?:\.\s*(?:${SQL_IDENTIFIER_SOURCE})\s*)?USING\s*(${SQL_IDENTIFIER_SOURCE})`, "i");
1212
- var ALLOWED_VIRTUAL_TABLE_MODULES = /* @__PURE__ */ new Set([
1213
- "fts5",
1214
- "fts5vocab",
1215
- "rtree",
1216
- "rtree_i32"
1217
- ]);
1218
- /**
1219
- * `VACUUM` has no action code of its own, so the authorizer never sees it. What refuses it
1220
- * upstream is SQLite's own `cannot VACUUM from within a transaction` precondition, against the
1221
- * transaction a Durable Object always has open — and that message is what the oracle returned,
1222
- * for `VACUUM`, `VACUUM main` and `VACUUM INTO` alike. Byte-identical for the same reason
1223
- * `SQL_NOT_AUTHORIZED_MESSAGE` is: a caller matching on it ports unchanged.
1224
- *
1225
- * Refused here unconditionally rather than by transaction state, which is a divergence only in
1226
- * mechanism: this runtime never runs a statement where upstream would have allowed it.
1227
- */
1228
- var VACUUM_STATEMENT = /^[\s;]*VACUUM\b/i;
1229
- var SQL_VACUUM_REFUSED_MESSAGE = "cannot VACUUM from within a transaction: SQLITE_ERROR";
1230
- /** Refuse the authorizer-only forms against one SQLite-decided statement boundary. */
1231
- function refuseUnauthorizedForms(statement) {
1232
- const code = statement.replace(NOT_COMMENT, " ");
1233
- if (VACUUM_STATEMENT.test(code)) throw new Error(SQL_VACUUM_REFUSED_MESSAGE);
1234
- if (DATABASE_ATTACHMENT.test(code) || TEMP_SCHEMA.test(code) || TEMP_QUALIFIED.test(code)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
1235
- if (VIRTUAL_TABLE.test(code)) {
1236
- const moduleName = VIRTUAL_TABLE_MODULE.exec(code)?.[1];
1237
- if (moduleName === void 0 || !ALLOWED_VIRTUAL_TABLE_MODULES.has(unquoted(moduleName).toLowerCase())) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
1238
- }
1239
- }
1240
- /** Cheap pre-test; `PRAGMA` and the `pragma_` functions both contain it. */
1241
- var PRAGMA_HINT = /pragma/i;
1242
- /** `PRAGMA [schema.]name`, then `= value`, `(argument)`, or nothing. */
1243
- var PRAGMA_STATEMENT = /^[\s;]*PRAGMA\s+(?:[A-Za-z_][A-Za-z0-9_$]*\s*\.\s*)?([A-Za-z_][A-Za-z0-9_$]*)(?:\s*=\s*([\s\S]+?)|\s*\(\s*([\s\S]*?)\s*\))?\s*;?\s*$/i;
1244
- /**
1245
- * ← `ALLOWED_PRAGMAS` (`util/sqlite.c++:543-571`) and `PragmaSignature` (`:528-535`),
1246
- * verbatim. `table_list`, `table_info`, and `table_xinfo` are special-cased
1247
- * ahead of the table in the authorizer, exactly as upstream's `SQLITE_PRAGMA`
1248
- * case does (`util/sqlite.c++:1194-1273`).
1249
- */
1250
- var ALLOWED_PRAGMAS = /* @__PURE__ */ new Map([
1251
- ["data_version", "NO_ARG"],
1252
- ["page_size", "NO_ARG"],
1253
- ["case_sensitive_like", "BOOLEAN"],
1254
- ["foreign_keys", "BOOLEAN"],
1255
- ["defer_foreign_keys", "BOOLEAN"],
1256
- ["ignore_check_constraints", "BOOLEAN"],
1257
- ["legacy_alter_table", "BOOLEAN"],
1258
- ["recursive_triggers", "BOOLEAN"],
1259
- ["reverse_unordered_selects", "BOOLEAN"],
1260
- ["foreign_key_check", "OPTIONAL_OBJECT_NAME"],
1261
- ["foreign_key_list", "OBJECT_NAME"],
1262
- ["index_info", "OBJECT_NAME"],
1263
- ["index_list", "OBJECT_NAME"],
1264
- ["index_xinfo", "OBJECT_NAME"],
1265
- ["quick_check", "NULL_NUMBER_OR_OBJECT_NAME"],
1266
- ["optimize", "NULL_OR_NUMBER"]
1267
- ]);
1268
- /** Upstream compares the eight literal forms as PREFIXES, case-insensitively. */
1269
- var BOOLEAN_PRAGMA_VALUE = /^(?:true|false|yes|no|on|off|1|0)/i;
1270
- /**
1271
- * The pragmas SQLite ships (https://www.sqlite.org/pragma.html), so a
1272
- * `pragma_X` identifier can be told apart: `X` here means the table-valued
1273
- * pragma function and follows the allowlist; any other `pragma_`-prefixed
1274
- * identifier is an ordinary application name, which upstream's authorizer
1275
- * distinguishes by resolution and the conformance suite pins.
1276
- */
1277
- var SQLITE_PRAGMA_NAMES = /* @__PURE__ */ new Set([
1278
- "analysis_limit",
1279
- "application_id",
1280
- "auto_vacuum",
1281
- "automatic_index",
1282
- "busy_timeout",
1283
- "cache_size",
1284
- "cache_spill",
1285
- "case_sensitive_like",
1286
- "cell_size_check",
1287
- "checkpoint_fullfsync",
1288
- "collation_list",
1289
- "compile_options",
1290
- "data_version",
1291
- "database_list",
1292
- "defer_foreign_keys",
1293
- "encoding",
1294
- "foreign_key_check",
1295
- "foreign_key_list",
1296
- "foreign_keys",
1297
- "freelist_count",
1298
- "full_column_names",
1299
- "fullfsync",
1300
- "function_list",
1301
- "hard_heap_limit",
1302
- "ignore_check_constraints",
1303
- "incremental_vacuum",
1304
- "index_info",
1305
- "index_list",
1306
- "index_xinfo",
1307
- "integrity_check",
1308
- "journal_mode",
1309
- "journal_size_limit",
1310
- "legacy_alter_table",
1311
- "legacy_file_format",
1312
- "locking_mode",
1313
- "max_page_count",
1314
- "mmap_size",
1315
- "module_list",
1316
- "optimize",
1317
- "page_count",
1318
- "page_size",
1319
- "pragma_list",
1320
- "query_only",
1321
- "quick_check",
1322
- "read_uncommitted",
1323
- "recursive_triggers",
1324
- "reverse_unordered_selects",
1325
- "schema_version",
1326
- "secure_delete",
1327
- "short_column_names",
1328
- "shrink_memory",
1329
- "soft_heap_limit",
1330
- "synchronous",
1331
- "table_info",
1332
- "table_list",
1333
- "table_xinfo",
1334
- "temp_store",
1335
- "threads",
1336
- "trusted_schema",
1337
- "user_version",
1338
- "wal_autocheckpoint",
1339
- "wal_checkpoint",
1340
- "writable_schema"
1341
- ]);
1342
- /** kj's `tryParseAs` is decimal; keep the same acceptance. */
1343
- var DECIMAL = /^[+-]?\d+$/;
1344
- /**
1345
- * One layer of SQL quoting, any of the four forms — a pragma argument, or the module token
1346
- * `VIRTUAL_TABLE_MODULE` captured. Doubled inner quotes stay doubled, which cannot change a
1347
- * verdict here: no allowlisted comparison target contains a quote character.
1348
- */
1349
- function unquoted(argument) {
1350
- const first = argument[0];
1351
- const last = argument[argument.length - 1];
1352
- if (argument.length >= 2) {
1353
- if ((first === "'" || first === "\"" || first === "`") && last === first) return argument.slice(1, -1);
1354
- if (first === "[" && last === "]") return argument.slice(1, -1);
1355
- }
1356
- return argument;
1357
- }
1358
- /** ← the `SQLITE_PRAGMA` authorizer case (`util/sqlite.c++:1194-1273`), whole. */
1359
- function isAllowedPragma(name, argument) {
1360
- const pragma = name.toLowerCase();
1361
- if (pragma === "table_list") return true;
1362
- if (pragma === "table_info" || pragma === "table_xinfo") {
1363
- if (argument === void 0) return false;
1364
- return SqlStorageRegulator.isAllowedName(unquoted(argument));
1365
- }
1366
- const signature = ALLOWED_PRAGMAS.get(pragma);
1367
- if (signature === void 0) return false;
1368
- switch (signature) {
1369
- case "NO_ARG": return argument === void 0;
1370
- case "BOOLEAN": return argument === void 0 || BOOLEAN_PRAGMA_VALUE.test(unquoted(argument));
1371
- case "OBJECT_NAME": return argument !== void 0 && SqlStorageRegulator.isAllowedName(unquoted(argument));
1372
- case "OPTIONAL_OBJECT_NAME": return argument === void 0 || SqlStorageRegulator.isAllowedName(unquoted(argument));
1373
- case "NULL_OR_NUMBER": return argument === void 0 || DECIMAL.test(argument);
1374
- case "NULL_NUMBER_OR_OBJECT_NAME": return argument === void 0 || DECIMAL.test(argument) || SqlStorageRegulator.isAllowedName(unquoted(argument));
1375
- }
1376
- }
1377
- /**
1378
- * The text-level stand-in for the authorizer's `SQLITE_PRAGMA` case, against
1379
- * one SQLite-decided statement boundary. Load-bearing beyond fidelity:
1380
- * `user_version` is where runtime storage versioning keeps its per-file stamp
1381
- * (`util/sqlite-migrations.ts`), and `writable_schema` would let application
1382
- * SQL rewrite `sqlite_master` out from under the `_cf_` reservation.
1383
- *
1384
- * The `pragma_` table-valued functions reach the same authorizer path
1385
- * upstream, so they follow the same allowlist here — by pragma NAME only. An
1386
- * argument the text cannot see (a string literal or a binding) goes unchecked
1387
- * where upstream's authorizer sees the resolved value; a `_cf_` name smuggled
1388
- * that way reads schema whose shape is public source anyway, while identifier
1389
- * arguments stay covered by `requireAllowedNames`. The README divergence row
1390
- * records this.
1391
- */
1392
- function requireAllowedPragmas(statement) {
1393
- if (!PRAGMA_HINT.test(statement)) return;
1394
- const code = statement.replace(NOT_COMMENT, " ");
1395
- const direct = code.match(PRAGMA_STATEMENT);
1396
- if (direct !== null) {
1397
- const [, name = "", assigned, called] = direct;
1398
- const argument = (assigned ?? called)?.trim();
1399
- if (!isAllowedPragma(name, argument === "" ? void 0 : argument)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
1400
- return;
1401
- }
1402
- if (/^[\s;]*PRAGMA\b/i.test(code)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
1403
- const literalFree = code.replace(NOT_CODE, " ");
1404
- for (const [token] of literalFree.matchAll(IDENTIFIER)) {
1405
- if (token.length <= 7 || token.slice(0, 7).toLowerCase() !== "pragma_") continue;
1406
- const name = token.slice(7).toLowerCase();
1407
- if (!SQLITE_PRAGMA_NAMES.has(name)) continue;
1408
- if (name !== "table_list" && name !== "table_info" && name !== "table_xinfo" && !ALLOWED_PRAGMAS.has(name)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
1409
- }
1410
- }
1411
- /** Everything the untrusted path refuses at one statement boundary. */
1412
- function regulateUntrustedStatement(statement) {
1413
- refuseTransactionControl(statement);
1414
- refuseUnauthorizedForms(statement);
1415
- requireAllowedPragmas(statement);
1416
- }
1417
- /** ← `JSG_INHERIT_INTRINSIC(v8::kIteratorPrototype)` (`jsg/iterator.h:1044`). */
1418
- var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
1419
- /**
1420
- * ← the `JSG_ITERATOR` types (`jsg/iterator.h:1036-1050`): `next` and
1421
- * self-iterability on `%IteratorPrototype%` — which is what carries the ES
1422
- * iterator helpers; `raw().toArray()` is what Drizzle's durable-sqlite driver
1423
- * calls — and NOTHING else. No `return`, no `throw` (only the async variant
1424
- * registers `return_`, `:1069-1085`), so `IteratorClose` after a `break`, a
1425
- * partial destructuring, or a `take()` is a no-op and a retained iterator
1426
- * resumes. Results are `JSG_STRUCT(done, value)` in that key order
1427
- * (`jsg/iterator.h:706-710`).
1428
- */
1429
- var RawIterator = class {
1430
- #pull;
1431
- constructor(pull) {
1432
- this.#pull = pull;
1433
- }
1434
- next() {
1435
- const raw = this.#pull();
1436
- if (raw === void 0) return {
1437
- done: true,
1438
- value: void 0
1439
- };
1440
- return {
1441
- done: false,
1442
- value: asRawRow([...raw])
1443
- };
1444
- }
1445
- [Symbol.iterator]() {
1446
- return this;
1447
- }
1448
- };
1449
- Object.setPrototypeOf(RawIterator.prototype, IteratorPrototype);
1450
- Object.defineProperty(RawIterator.prototype, Symbol.toStringTag, {
1451
- value: "RawIterator",
1452
- configurable: true
1453
- });
1454
- /** ← `RowIterator`, shaped exactly as `RawIterator` above. */
1455
- var RowIterator = class {
1456
- #pull;
1457
- constructor(pull) {
1458
- this.#pull = pull;
1459
- }
1460
- next() {
1461
- const row = this.#pull();
1462
- if (row === void 0) return {
1463
- done: true,
1464
- value: void 0
1465
- };
1466
- return {
1467
- done: false,
1468
- value: row
1469
- };
1470
- }
1471
- [Symbol.iterator]() {
1472
- return this;
1473
- }
1474
- };
1475
- Object.setPrototypeOf(RowIterator.prototype, IteratorPrototype);
1476
- Object.defineProperty(RowIterator.prototype, Symbol.toStringTag, {
1477
- value: "RowIterator",
1478
- configurable: true
1479
- });
1480
- /**
1481
- * ← `SqlStorage::Cursor`.
1482
- *
1483
- * `rowsRead` is the one counter that is not upstream's. Upstream reads
1484
- * `Query::getRowsRead()`, a billing counter sourced from libsql's
1485
- * `STMTSTATUS_ROWS_READ` that counts index rows and that neither backend
1486
- * exposes — the same absence the README already records for `SqlResult`. The
1487
- * interface requires a number, so this returns the rows the cursor has yielded,
1488
- * which is what today's browser host returns and what its tests assert. It
1489
- * undercounts any query that scans more rows than it returns.
1490
- */
1491
- var Cursor = class {
1492
- #columnNames;
1493
- #rawRows;
1494
- #rowsWritten;
1495
- #position = 0;
1496
- constructor(state) {
1497
- if (state === void 0) throw new Error(CURSOR_NOT_CONSTRUCTIBLE_MESSAGE);
1498
- this.#columnNames = state.columnNames;
1499
- this.#rawRows = state.rawRows;
1500
- this.#rowsWritten = state.rowsWritten;
1501
- }
1502
- /**
1503
- * ← `JSG_READONLY_PROTOTYPE_PROPERTY(columnNames)` (`sql.h:210`): a
1504
- * prototype accessor, not an own field, so a cursor JSON-stringifies to `{}`.
1505
- */
1506
- get columnNames() {
1507
- return this.#columnNames;
1508
- }
1509
- /** ← `Cursor::next`, whose `RowIterator::Next` is this exact shape. */
1510
- next() {
1511
- const row = this.#nextRow();
1512
- if (row === void 0) return {
1513
- done: true,
1514
- value: void 0
1515
- };
1516
- return {
1517
- done: false,
1518
- value: row
1519
- };
1520
- }
1521
- /** ← `Cursor::toArray`, which drains from the current position. */
1522
- toArray() {
1523
- const rows = [];
1524
- for (;;) {
1525
- const row = this.#nextRow();
1526
- if (row === void 0) return rows;
1527
- rows.push(row);
1528
- }
1529
- }
1530
- /** ← `Cursor::one`. Both messages are upstream's, verbatim. */
1531
- one() {
1532
- const row = this.#nextRow();
1533
- if (row === void 0) throw new Error("Expected exactly one result from SQL query, but got no results.");
1534
- if (this.#position < this.#rawRows.length) {
1535
- this.#position = this.#rawRows.length;
1536
- throw new Error("Expected exactly one result from SQL query, but got multiple results.");
1537
- }
1538
- return row;
1539
- }
1540
- /**
1541
- * ← `Cursor::raw`, which shares this cursor's position rather than
1542
- * restarting. The iterator's shape is `RawIterator`'s whole doc comment.
1543
- */
1544
- raw() {
1545
- return new RawIterator(() => this.#nextRaw());
1546
- }
1547
- /** ← `JSG_ITERABLE(rows)`, yielding through the same shared position. */
1548
- [Symbol.iterator]() {
1549
- return new RowIterator(() => this.#nextRow());
1550
- }
1551
- get rowsRead() {
1552
- return this.#position;
1553
- }
1554
- /** ← `Cursor::getRowsWritten`, which is `SqlResult.rowsWritten` here. */
1555
- get rowsWritten() {
1556
- return this.#rowsWritten;
1557
- }
1558
- #nextRaw() {
1559
- const raw = this.#rawRows[this.#position];
1560
- if (raw === void 0) return void 0;
1561
- this.#position += 1;
1562
- return raw;
1563
- }
1564
- /** ← `Cursor::rowIteratorNext`: zip the column names onto the row. */
1565
- #nextRow() {
1566
- const raw = this.#nextRaw();
1567
- if (raw === void 0) return void 0;
1568
- const row = {};
1569
- this.#columnNames.forEach((name, index) => {
1570
- row[name] = raw[index] ?? null;
1571
- });
1572
- return asRow(row);
1573
- }
1574
- };
1575
- /** ← the jsg resource-type tag every workerd API object carries (`resource.h`). */
1576
- Object.defineProperty(Cursor.prototype, Symbol.toStringTag, {
1577
- value: "Cursor",
1578
- configurable: true
1579
- });
1580
- /**
1581
- * ← `SqlStorage::Statement`, which upstream describes as "supported only for
1582
- * backwards compatibility ... it is actually just a wrapper around `exec()`".
1583
- * `JSG_CALLABLE(run)` makes the object itself callable, so `prepare()` returns a
1584
- * function wearing this prototype rather than an object with a `run` method.
1585
- */
1586
- var Statement = class {
1587
- constructor() {
1588
- throw new Error(STATEMENT_NOT_CONSTRUCTIBLE_MESSAGE);
1589
- }
1590
- };
1591
- var SqlStorage = class {
1592
- #ctx;
1593
- #owner;
1594
- /** ← `kj::Maybe<uint> pageSize`, memoized for the same reason. */
1595
- #pageSize;
1596
- constructor(ctx, owner) {
1597
- this.#ctx = ctx;
1598
- this.#owner = owner;
1599
- }
1600
- /** ← `JSG_NESTED_TYPE(Cursor)`. Exposed so `instanceof` works, as upstream's is. */
1601
- Cursor = Cursor;
1602
- /** ← `JSG_NESTED_TYPE(Statement)`. */
1603
- Statement = Statement;
1604
- exec(query, ...bindings) {
1605
- requireInputLock(this.#ctx, "sql.exec()");
1606
- const db = this.#owner.getSqliteDb();
1607
- const sqlBindings = bindings.map(toSqlBindingValue);
1608
- requireAllowedNames(query);
1609
- const result = db.run({ regulate: regulateUntrustedStatement }, query, ...sqlBindings);
1610
- return new Cursor({
1611
- columnNames: [...result.columnNames],
1612
- rawRows: result.rawRows.map((row) => row.map(toSqlStorageValue)),
1613
- rowsWritten: result.rowsWritten
1614
- });
1615
- }
1616
- /**
1617
- * ← `SqlStorage::getDatabaseSize`.
1618
- *
1619
- * Upstream's second query is `PRAGMA page_size;`, which `sqlite3_stmt_readonly()`
1620
- * reports read-only. With no such call the text is the only source and §1.7.1's
1621
- * rule is write-unless-provably-a-read, so a bare `PRAGMA` would open a
1622
- * transaction and take an output-gate lock to answer a size question. The
1623
- * `pragma_page_size` table-valued function is the same value read through the
1624
- * `SELECT` upstream already uses for the page count.
1625
- */
1626
- get databaseSize() {
1627
- requireInputLock(this.#ctx, "sql.databaseSize");
1628
- const db = this.#owner.getSqliteDb();
1629
- return readNumber(db.run("select (select * from pragma_page_count) - (select * from pragma_freelist_count);"), "page count") * this.#getPageSize(db);
1630
- }
1631
- /** ← `SqlStorage::prepare`. Experimental and deprecated upstream; `exec` caches for you. */
1632
- prepare(query) {
1633
- requireInputLock(this.#ctx, "sql.prepare()");
1634
- const run = (...bindings) => this.exec(query, ...bindings);
1635
- Object.setPrototypeOf(run, Statement.prototype);
1636
- return run;
1637
- }
1638
- /** ← `SqlStorage::ingest`. */
1639
- ingest(query) {
1640
- requireInputLock(this.#ctx, "sql.ingest()");
1641
- requireAllowedNames(query);
1642
- return this.#owner.getSqliteDb().ingest(query, regulateUntrustedStatement);
1643
- }
1644
- /** ← `SqlStorage::setMaxPageCountForTest`, which is what its name says. */
1645
- setMaxPageCountForTest(count) {
1646
- requireInputLock(this.#ctx, "sql.setMaxPageCountForTest()");
1647
- this.#owner.getSqliteDb().run(`PRAGMA max_page_count = ${count}`);
1648
- }
1649
- /** ← `SqlStorage::getPageSize`. */
1650
- #getPageSize(db) {
1651
- const cached = this.#pageSize;
1652
- if (cached !== void 0) return cached;
1653
- const size = readNumber(db.run("select * from pragma_page_size;"), "page size");
1654
- this.#pageSize = size;
1655
- return size;
1656
- }
1657
- };
1658
- function readNumber(result, what) {
1659
- const value = result.rawRows[0]?.[0];
1660
- if (typeof value === "number") return value;
1661
- if (typeof value === "bigint") return Number(value);
1662
- throw new Error(`Expected a number for the database's ${what}.`);
1663
- }
1664
- /** ← JSG's conversion from JavaScript arguments to `SqlStorage::BindingValue`. */
1665
- function toSqlBindingValue(value) {
1666
- if (value === null || value === void 0) return null;
1667
- if (typeof value === "string" || typeof value === "number") return value;
1668
- if (typeof value === "boolean") return String(value);
1669
- if (typeof value === "bigint") throw new TypeError("Cannot convert a BigInt value to a number");
1670
- if (value instanceof ArrayBuffer) return copyBytes(new Uint8Array(value));
1671
- if (ArrayBuffer.isView(value)) return copyBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
1672
- throw new TypeError(`Cannot convert ${Object.prototype.toString.call(value)} to a SQL value`);
1673
- }
1674
- function copyBytes(bytes) {
1675
- const copy = new Uint8Array(bytes.byteLength);
1676
- copy.set(bytes);
1677
- return copy;
1678
- }
1679
- /**
1680
- * ← `SqlStorage::wrapSqlValue` plus the `Query::getValue` switch above it.
1681
- *
1682
- * Upstream's int64 arm carries its own comment: "int64 will become BigInt, but
1683
- * most applications won't want all their integers to be BigInt. We will coerce
1684
- * to a double here." That coercion is kept rather than refused, because it is
1685
- * the documented behaviour of `sql.exec` and a caller storing an id larger than
1686
- * 2^53 has already lost on workerd.
1687
- */
1688
- function toSqlStorageValue(value) {
1689
- if (value === null || value === void 0) return null;
1690
- if (typeof value === "string" || typeof value === "number") return value;
1691
- if (typeof value === "bigint") return Number(value);
1692
- if (typeof value === "boolean") return value ? 1 : 0;
1693
- if (value instanceof Uint8Array) {
1694
- const copy = new ArrayBuffer(value.byteLength);
1695
- new Uint8Array(copy).set(value);
1696
- return copy;
1697
- }
1698
- throw new Error(`SQL returned a ${typeof value}, which is not a SqlStorageValue.`);
1699
- }
1700
- /**
1701
- * The two narrowings a generic row type needs. `T` is the caller's claim about
1702
- * the shape of a row SQLite produced at runtime, so no check can confirm it and
1703
- * upstream does not try — its `Cursor<T>` is the same claim written in a
1704
- * `JSG_TS_OVERRIDE`. Confined to these two functions so the claim is one place
1705
- * rather than sprinkled through the cursor.
1706
- */
1707
- function asRow(row) {
1708
- return row;
1709
- }
1710
- function asRawRow(values) {
1711
- return values;
1712
- }
1713
- //#endregion
1714
- //#region src/api/actor-state.ts
1715
- /**
1716
- * ← workerd `src/workerd/api/actor-state.{h,c++}`
1717
- *
1718
- * The JS-facing storage objects: `DurableObjectStorageOperations` and its two
1719
- * subclasses, `DurableObjectFacets`, and `DurableObjectState`. Everything below
1720
- * this file is reached through one of them.
1721
- *
1722
- * **`DurableObjectStorage` satisfies workers-types with no cast (§2.4).** That
1723
- * was checked rather than asserted, and two shapes here exist only because it
1724
- * has to: `sql.Cursor` and `sql.Statement` must be constructible with no
1725
- * arguments (see `sql.ts`), and `storage.kv` is required. The narrowings that
1726
- * remain are all one thing —
1727
- * `get<T>` returns the caller's claim about the shape of a value SQLite handed
1728
- * back as bytes, which no check can confirm and which upstream states the same
1729
- * way, as a `jsg::JsRef<jsg::JsValue>` behind a `JSG_TS_OVERRIDE`'d
1730
- * `Promise<T>`. There is no `as unknown as` anywhere in this layer.
1731
- *
1732
- * **Every throw is synchronous, including from the promise-returning methods.**
1733
- * That is upstream's: a `JSG_REQUIRE` inside a method returning `jsg::Promise`
1734
- * throws into the isolate before the promise exists, so `put(k, undefined)`
1735
- * throws rather than rejecting. The same goes for a value that will not decode,
1736
- * because §1.4 makes the SQLite path run the decoder before `Promise.resolve`.
1737
- *
1738
- * **What the input gate does and does not do here.** Every entry point calls
1739
- * `requireInputLock` — see its comment in `io/io-context.ts`, which is the one
1740
- * place this package decides what an empty invocation stack means. Nothing else
1741
- * takes a lock: a read returns a value, a write returns a resolved promise, and
1742
- * `atCheckpointEnd` is what keeps the whole chain inside one transaction
1743
- * (§1.7.1). The two exceptions are upstream's own — `sync()` and the bookmark
1744
- * pair release the gate via `awaitIo`, and `transaction()` takes a critical
1745
- * section.
1746
- *
1747
- * **Decision 2's branch has one reachable site**, and it is not where upstream's
1748
- * is. §1.4 measures that SQLite cache operations are immediate, so their
1749
- * `kj::OneOf<T, kj::Promise<T>>` branch has nothing to select between.
1750
- * `transformMaybeBackpressure` keeps the branch because
1751
- * `DeleteAllResults.backpressure` is still a promise in `io/actor-cache.ts`.
1752
- *
1753
- * Not ported, because the substrate has no equivalent: Hibernatable WebSockets,
1754
- * which is the whole reason `DurableObjectState`'s eight WebSocket methods are
1755
- * named throwing stubs; V8's private wire bytes, replaced by a browser-safe
1756
- * structured-clone encoding with the same public value semantics; the billing
1757
- * counters
1758
- * (`billingUnits`, `ActorObserver`, `updateStorageWriteUnit`) and the trace
1759
- * spans, both already absent throughout; `enableSql`, a workerd namespace option
1760
- * that exists to simulate a non-SQLite Durable Object; and `ReplicaActorOutgoingFactory`,
1761
- * whose replication half is a named boundary in `io/actor-cache.ts`.
1762
- *
1763
- * Spec: §1.4, §1.5, §1.10, §2.4, §2.5, decisions 2, 4 and 14 in
1764
- * docs/decisions.md.
1765
- */
1766
- /**
1767
- * ← `MAX_FACET_NAME_LENGTH` / `MAX_FACET_TREE_DEPTH`
1768
- * (`actor-state.c++:943,947`), in the anonymous namespace beside the facet code
1769
- * that enforces them. The scaffolding had them in `server/`, which is neither
1770
- * where upstream puts them nor where they are checked.
1771
- */
1772
- var FACET_NAME_MAX_LENGTH = 256;
1773
- /** Root is at depth 0, so the deepest allowed facet is at depth 3. */
1774
- var FACET_TREE_MAX_DEPTH = 4;
1775
- /**
1776
- * The substrate boundary named in the package README: Hibernatable WebSockets
1777
- * exist so the platform can evict an actor while keeping its sockets open, and
1778
- * Chrome exposes no equivalent lifecycle. Under this repo's fail-closed tenet
1779
- * the throw IS the specified behaviour, which is why §2.5 orders the four
1780
- * silent no-op stubs beside it replaced.
1781
- */
1782
- var HIBERNATION_UNIMPLEMENTED_MESSAGE = "Hibernatable WebSockets are not available in this runtime: they exist so the platform can evict a Durable Object while keeping its sockets open, and there is no equivalent lifecycle to be faithful to.";
1783
- /**
1784
- * ← what falls off the end of `DurableObjectFacets::get`'s class switch
1785
- * (`actor-state.c++:1029-1043`).
1786
- *
1787
- * Upstream accepts three things as `FacetStartupOptions.class`: a bare
1788
- * `DurableObjectClass`, a `LoopbackDurableObjectNamespace`, or a
1789
- * `LoopbackColoLocalActorNamespace`, unwrapping the last two through
1790
- * `getClass()`. All three are ported — the loopback pair by
1791
- * `api/export-loopback.ts` — and `KJ_UNREACHABLE` is the fourth case there
1792
- * because JSG has already refused anything else while unwrapping the
1793
- * `kj::OneOf`. The check has to be written here because
1794
- * `@cloudflare/workers-types` declares `interface DurableObjectClass<_T> {}`,
1795
- * which every object satisfies, so nothing refuses it before the method body.
1796
- */
1797
- var FACET_CLASS_UNSUPPORTED_MESSAGE = "facets.get() was given a class this runtime cannot resolve. `class` must be a DurableObjectClass, a LoopbackDurableObjectNamespace or a LoopbackColoLocalActorNamespace — which is what a ctx.exports entry for a Durable Object class is.";
1798
- /** ← `DurableObjectStorageOperations::OpName`. Named only where an error quotes them. */
1799
- var OP_GET = "get()";
1800
- var OP_GET_ALARM = "getAlarm()";
1801
- var OP_LIST = "list()";
1802
- var OP_PUT = "put()";
1803
- var OP_PUT_ALARM = "setAlarm()";
1804
- var OP_DELETE = "delete()";
1805
- var OP_DELETE_ALARM = "deleteAlarm()";
1806
- var OP_ROLLBACK = "rollback()";
1807
- /** ← `actor-state.c++:455`, verbatim: the one message both overloads' misuse produces. */
1808
- var PUT_OVERLOAD_MESSAGE = "put() may only be called with a single key-value pair and optional options as put(key, value, options) or with multiple key-value pairs and optional options as put(entries, options)";
1809
- /**
1810
- * ← the `kj::OneOf<kj::String, jsg::Dict<…>>` unwrap on put()'s first parameter: `jsg::Dict`
1811
- * takes any JS object except an Array — functions and Maps included — and `kj::String` takes
1812
- * everything else by coercion. A type predicate, so the overload split narrows without a cast.
1813
- */
1814
- function isEntriesArgument(value) {
1815
- return (typeof value === "object" || typeof value === "function") && value !== null && !Array.isArray(value);
1816
- }
1817
- /**
1818
- * ← the struct wrapper (`jsg/struct.h:246-258`), which is NOT the Dict wrapper: `PutOptions` is
1819
- * all-optional fields, so `null` unwraps to default options, and any object does — arrays and
1820
- * functions included, because the wrapper checks `IsObject()` with no Array exclusion. Only a
1821
- * non-null primitive fails to unwrap. Measured on real workerd: `put({k: 1}, null)`, `…, [])`
1822
- * and `…, function () {})` all write, and `put({k: 1}, "v")` alone is the overload error.
1823
- */
1824
- function isPutOptions(value) {
1825
- return value === null || typeof value === "object" || typeof value === "function";
1826
- }
1827
- /** The key immediately after `k` in byte order is `k` plus this. */
1828
- var NULL_CHARACTER = "\0";
1829
- /** ← the `0xff` upstream strips from the tail of a prefix, in UTF-16 code units. */
1830
- var MAX_CODE_UNIT = 65535;
1831
- var textEncoder = new TextEncoder();
1832
- var textDecoder = new TextDecoder();
1833
- /** A byte JSON could never begin with, `DO`, and the local codec version. */
1834
- var VALUE_CODEC_HEADER = new Uint8Array([
1835
- 0,
1836
- 68,
1837
- 79,
1838
- 1
1839
- ]);
1840
- /**
1841
- * ← `serializeV8Value`. The wire bytes differ because V8's serializer is not
1842
- * available in browsers; the public structured-clone value semantics do not.
1843
- * The short header keeps the new representation unambiguous while old JSON rows
1844
- * remain readable.
1845
- */
1846
- function serializeValue(value) {
1847
- const body = textEncoder.encode(JSON.stringify(serialize(value)));
1848
- const encoded = new Uint8Array(VALUE_CODEC_HEADER.byteLength + body.byteLength);
1849
- encoded.set(VALUE_CODEC_HEADER);
1850
- encoded.set(body, VALUE_CODEC_HEADER.byteLength);
1851
- return encoded;
1852
- }
1853
- /**
1854
- * ← `deserializeV8Value`.
1855
- *
1856
- * Upstream logs "the key (to help find the data in the database if it hasn't
1857
- * been deleted), the length of the value, and the first three bytes of the value
1858
- * (which is just the v8-internal version header and the tag that indicates the
1859
- * type of the value, but not its contents)". Our four-byte header carries only
1860
- * a marker and version for the same reason.
1861
- */
1862
- function deserializeValue(key, buffer) {
1863
- if (buffer.byteLength === 0) throw new Error(`unexpectedly empty value buffer; key = ${key}`);
1864
- try {
1865
- const structured = VALUE_CODEC_HEADER.every((byte, index) => buffer[index] === byte);
1866
- const bytes = structured ? buffer.subarray(VALUE_CODEC_HEADER.byteLength) : buffer;
1867
- const parsed = JSON.parse(textDecoder.decode(bytes));
1868
- return structured ? deserialize(parsed) : parsed;
1869
- } catch (exception) {
1870
- throw new Error(`actor storage deserialization failed: failed to deserialize stored value; key = ${key}; size = ${buffer.byteLength}`, { cause: exception });
1871
- }
1872
- }
1873
- /**
1874
- * ← `transformMaybeBackpressure` (`actor-state.c++:103-119`). THIS is decision
1875
- * 2's live site: `DeleteAllResults.backpressure` is still `Promise<void> |
1876
- * undefined`, so the branch has something to select between.
1877
- *
1878
- * Upstream's own note, kept because it is the reason the flag is threaded here
1879
- * at all: "In practice `allowConcurrency` will have no effect on a backpressure
1880
- * promise since backpressure blocks everything anyway, but we pass the option
1881
- * through for consistency in case of future changes."
1882
- */
1883
- function transformMaybeBackpressure(ctx, options, maybeBackpressure) {
1884
- if (maybeBackpressure === void 0) return Promise.resolve();
1885
- if (options.allowConcurrency === true) return ctx.awaitIo(maybeBackpressure);
1886
- return ctx.awaitIoWithInputLock(maybeBackpressure, () => {});
1887
- }
1888
- /**
1889
- * ← `DurableObjectStorageOperations::compileListOptions`
1890
- * (`actor-state.c++:314-417`). Returns undefined if the list operation would
1891
- * provably return no results. `SyncKvStorage` reuses it, exactly as upstream's
1892
- * comment says it must.
1893
- *
1894
- * Two translations. `startAfter` gains ONE null character where upstream's
1895
- * `kj::String` gains two, because the second of upstream's is the terminator and
1896
- * a JS string has none. And every comparison here is on UTF-16 code units where
1897
- * upstream's is on UTF-8 bytes, while the range the database actually applies is
1898
- * SQLite's `BINARY` collation over UTF-8 — the two orders agree for every key
1899
- * outside the astral planes, and a key that mixes astral characters with a
1900
- * prefix can land on the wrong side of a clamp this function computes.
1901
- */
1902
- function compileListOptions(options) {
1903
- let start = "";
1904
- let end;
1905
- let reverse = false;
1906
- let limit;
1907
- if (options !== void 0) {
1908
- if (options.start !== void 0) {
1909
- if (options.startAfter !== void 0) throw new TypeError("list() cannot be called with both start and startAfter values.");
1910
- start = options.start;
1911
- }
1912
- if (options.startAfter !== void 0) start = options.startAfter + NULL_CHARACTER;
1913
- if (options.end !== void 0) end = options.end;
1914
- if (options.reverse !== void 0) reverse = options.reverse;
1915
- if (options.limit !== void 0) {
1916
- if (!(options.limit > 0)) throw new TypeError("List limit must be positive.");
1917
- limit = options.limit;
1918
- }
1919
- const prefix = options.prefix;
1920
- if (prefix !== void 0 && prefix.length > 0) {
1921
- if (start < prefix) start = prefix;
1922
- else if (start.startsWith(prefix)) {} else return;
1923
- const keyAfterPrefix = firstKeyAfterPrefix(prefix);
1924
- if (keyAfterPrefix === void 0) {} else if (end === void 0) end = keyAfterPrefix;
1925
- else if (end <= prefix) return;
1926
- else if (end.startsWith(prefix)) {} else end = keyAfterPrefix;
1927
- }
1928
- }
1929
- if (end !== void 0 && end <= start) return;
1930
- return {
1931
- start,
1932
- end,
1933
- reverse,
1934
- limit
1935
- };
1936
- }
1937
- /**
1938
- * ← the `keyAfterPrefix` vector: strip maximal trailing units, then increment.
1939
- *
1940
- * Returns undefined when the prefix is nothing but maximal units, which is
1941
- * upstream's "the prefix is a string of some number of 0xff bytes, so includes
1942
- * the entire key space up through the last possible key".
1943
- */
1944
- function firstKeyAfterPrefix(prefix) {
1945
- let head = prefix;
1946
- while (head.length > 0 && head.charCodeAt(head.length - 1) === MAX_CODE_UNIT) head = head.slice(0, -1);
1947
- if (head.length === 0) return void 0;
1948
- return head.slice(0, -1) + String.fromCharCode(head.charCodeAt(head.length - 1) + 1);
1949
- }
1950
- /**
1951
- * ← workerd `src/workerd/api/sync-kv.{h,c++}`. The synchronous surface lives
1952
- * beside the asynchronous storage owner because both share the same codec and
1953
- * list-option compiler over one `SqliteKv`.
1954
- */
1955
- var SyncKvStorage = class {
1956
- #ctx;
1957
- #kv;
1958
- constructor(ctx, kv) {
1959
- this.#ctx = ctx;
1960
- this.#kv = kv;
1961
- }
1962
- get(key) {
1963
- requireInputLock(this.#ctx, "kv.get()");
1964
- const value = this.#kv.get(key);
1965
- if (value === void 0) return void 0;
1966
- return deserializeValue(key, value);
1967
- }
1968
- list(options) {
1969
- requireInputLock(this.#ctx, "kv.list()");
1970
- const compiled = compileListOptions(options);
1971
- if (compiled === void 0) return [];
1972
- return listIterator(this.#kv.list(compiled.start, compiled.end, compiled.limit, compiled.reverse ? "REVERSE" : "FORWARD"));
1973
- }
1974
- put(key, value) {
1975
- requireInputLock(this.#ctx, "kv.put()");
1976
- this.#kv.put(key, serializeValue(value));
1977
- }
1978
- delete(key) {
1979
- requireInputLock(this.#ctx, "kv.delete()");
1980
- return this.#kv.delete(key);
1981
- }
1982
- };
1983
- /** ← `SyncKvStorage::listNext`, whose cancellation branch is the reason it is not a plain loop. */
1984
- function* listIterator(cursor) {
1985
- for (;;) {
1986
- const pair = cursor.next();
1987
- if (pair !== void 0) {
1988
- yield [pair.key, deserializeValue(pair.key, pair.value)];
1989
- continue;
1990
- }
1991
- if (cursor.wasCanceled()) throw new Error("kv.list() iterator was invalidated because a new call to kv.list() was started. Only one kv.list() iterator can exist at a time.");
1992
- return;
1993
- }
1994
- }
1995
- /**
1996
- * ← `DurableObjectStorageOperations`. "Common implementation of
1997
- * DurableObjectStorage and DurableObjectTransaction. This class is designed to
1998
- * be used as a mixin."
1999
- */
2000
- var DurableObjectStorageOperations = class {
2001
- ctx;
2002
- constructor(ctx) {
2003
- this.ctx = ctx;
2004
- }
2005
- get(keyOrKeys, maybeOptions) {
2006
- requireInputLock(this.ctx, OP_GET);
2007
- const options = { ...maybeOptions };
2008
- if (typeof keyOrKeys === "string") return this.#getOne(keyOrKeys, options);
2009
- return this.#getMultiple(keyOrKeys, options);
2010
- }
2011
- getAlarm(maybeOptions) {
2012
- requireInputLock(this.ctx, OP_GET_ALARM);
2013
- const options = {
2014
- ...maybeOptions,
2015
- noCache: false
2016
- };
2017
- return Promise.resolve(this.getCache(OP_GET_ALARM).getAlarm(options));
2018
- }
2019
- list(maybeOptions) {
2020
- requireInputLock(this.ctx, OP_LIST);
2021
- const compiled = compileListOptions(maybeOptions);
2022
- if (compiled === void 0) return Promise.resolve(/* @__PURE__ */ new Map());
2023
- const options = { ...maybeOptions };
2024
- const cache = this.getCache(OP_LIST);
2025
- const result = compiled.reverse ? cache.listReverse(compiled.start, compiled.end, compiled.limit, options) : cache.list(compiled.start, compiled.end, compiled.limit, options);
2026
- return Promise.resolve(listResultsToMap(result));
2027
- }
2028
- put(keyOrEntries, valueOrOptions, maybeOptions) {
2029
- requireInputLock(this.ctx, OP_PUT);
2030
- if (!isEntriesArgument(keyOrEntries)) {
2031
- if (valueOrOptions === void 0) throw new TypeError("put() called with undefined value.");
2032
- return this.#putOne(`${keyOrEntries}`, valueOrOptions, { ...maybeOptions });
2033
- }
2034
- if (valueOrOptions !== void 0 && !isPutOptions(valueOrOptions)) throw new TypeError(PUT_OVERLOAD_MESSAGE);
2035
- return this.#putMultiple(keyOrEntries, { ...valueOrOptions });
2036
- }
2037
- delete(keyOrKeys, maybeOptions) {
2038
- requireInputLock(this.ctx, OP_DELETE);
2039
- const options = { ...maybeOptions };
2040
- if (typeof keyOrKeys === "string") return Promise.resolve(this.getCache(OP_DELETE).delete(keyOrKeys, options));
2041
- return Promise.resolve(this.getCache(OP_DELETE).deleteMultiple(keyOrKeys, options));
2042
- }
2043
- setAlarm(scheduledTime, maybeOptions) {
2044
- requireInputLock(this.ctx, OP_PUT_ALARM);
2045
- const when = scheduledTime instanceof Date ? scheduledTime.getTime() : scheduledTime;
2046
- if (!(when > 0)) throw new TypeError("setAlarm() cannot be called with an alarm time <= 0");
2047
- this.ctx.getActorOrThrow().assertCanSetAlarm();
2048
- const options = {
2049
- ...maybeOptions,
2050
- noCache: false
2051
- };
2052
- this.getCache(OP_PUT_ALARM).setAlarm(Math.max(when, this.ctx.now()), options);
2053
- return Promise.resolve();
2054
- }
2055
- deleteAlarm(maybeOptions) {
2056
- requireInputLock(this.ctx, OP_DELETE_ALARM);
2057
- const options = {
2058
- ...maybeOptions,
2059
- noCache: false
2060
- };
2061
- this.getCache(OP_DELETE_ALARM).setAlarm(null, options);
2062
- return Promise.resolve();
2063
- }
2064
- #getOne(key, options) {
2065
- const value = this.getCache(OP_GET).get(key, options);
2066
- return Promise.resolve(value === void 0 ? void 0 : deserializeValue(key, value));
2067
- }
2068
- #getMultiple(keys, options) {
2069
- const result = this.getCache(OP_GET).getMultiple(keys, options);
2070
- return Promise.resolve(listResultsToMap(result));
2071
- }
2072
- #putOne(key, value, options) {
2073
- this.getCache(OP_PUT).put(key, serializeValue(value), options);
2074
- return Promise.resolve();
2075
- }
2076
- #putMultiple(entries, options) {
2077
- const pairs = [];
2078
- for (const [key, value] of Object.entries(entries)) {
2079
- if (value === void 0) continue;
2080
- pairs.push({
2081
- key,
2082
- value: serializeValue(value)
2083
- });
2084
- }
2085
- this.getCache(OP_PUT).putMultiple(pairs, options);
2086
- return Promise.resolve();
2087
- }
2088
- };
2089
- /** ← `listResultsToMap` and `getMultipleResultsToMap`, minus the billing halves. */
2090
- function listResultsToMap(rows) {
2091
- const map = /* @__PURE__ */ new Map();
2092
- for (const entry of rows) map.set(entry.key, deserializeValue(entry.key, entry.value));
2093
- return map;
341
+ throw new Error("unreachable: exactly one module field is set");
342
+ }
343
+ /**
344
+ * ← `jsg::asBytes()` followed by `kj::heapArray<const kj::byte>(data.asPtr())`
345
+ * (`worker-loader.c++:231`, `:244`). The copy is the whole point — see the caller.
346
+ */
347
+ function copyBytes(value) {
348
+ if (value instanceof ArrayBuffer) return new Uint8Array(value.slice(0));
349
+ if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength));
350
+ throw new TypeError(NOT_BYTES_MESSAGE);
2094
351
  }
2095
- var DurableObjectStorage = class extends DurableObjectStorageOperations {
2096
- #cache;
2097
- #sql;
2098
- #kv;
2099
- constructor(ctx, cache) {
2100
- super(ctx);
2101
- this.#cache = cache;
2102
- }
2103
- /** ← `DurableObjectStorage::getActorCacheInterface`, which `DurableObjectState::abort` needs. */
2104
- getActorCacheInterface() {
2105
- return this.#cache;
2106
- }
2107
- /** ← `DurableObjectStorage::getSqliteDb`. Always SQLite-backed here; see the header. */
2108
- getSqliteDb() {
2109
- return this.#cache.getSqliteDatabase();
2110
- }
2111
- getCache() {
2112
- return this.#cache;
2113
- }
2114
- /** ← `JSG_LAZY_INSTANCE_PROPERTY(sql, getSql)`. */
2115
- get sql() {
2116
- this.#sql ??= new SqlStorage(this.ctx, this);
2117
- return this.#sql;
2118
- }
2119
- /** ← `JSG_LAZY_INSTANCE_PROPERTY(kv, getKv)`. */
2120
- get kv() {
2121
- this.#kv ??= new SyncKvStorage(this.ctx, this.#cache.getSqliteKv());
2122
- return this.#kv;
2123
- }
2124
- /**
2125
- * ← `DurableObjectStorage::deleteAll`.
2126
- *
2127
- * `deleteAlarm` is upstream's `FeatureFlags::get(js).getDeleteAllDeletesAlarm()`,
2128
- * a compatibility flag that exists so Workers published before it keep the old
2129
- * behaviour. A runtime with no deployed history takes the current behaviour.
2130
- */
2131
- deleteAll(maybeOptions) {
2132
- requireInputLock(this.ctx, "deleteAll()");
2133
- const options = { ...maybeOptions };
2134
- const result = this.#cache.deleteAll(options, { deleteAlarm: true });
2135
- return transformMaybeBackpressure(this.ctx, options, result.backpressure);
2136
- }
2137
- /**
2138
- * ← `DurableObjectStorage::transaction`.
2139
- *
2140
- * The critical section is load bearing and upstream says why: "the call to
2141
- * `startTransaction()` is when the SQLite-backed implementation will actually
2142
- * invoke `BEGIN TRANSACTION`, so it's important that we're inside the
2143
- * blockConcurrencyWhile block before that point so we don't accidentally catch
2144
- * some other asynchronous event in our transaction."
2145
- *
2146
- * The exception is packed into the result rather than thrown out of the
2147
- * section, and then rethrown outside it. Upstream's reason: "We don't actually
2148
- * want to reset the object, we only want to roll back the transaction and
2149
- * propagate the exception." A throw out of a critical section permanently
2150
- * breaks the input gate (§1.5), so a failing transaction callback would
2151
- * destroy the actor.
2152
- */
2153
- transaction(closure) {
2154
- requireInputLock(this.ctx, "transaction()");
2155
- return this.ctx.blockConcurrencyWhile(async () => {
2156
- const txn = new DurableObjectTransaction(this.ctx, this.#cache.startTransaction());
2157
- try {
2158
- const value = await closure(txn);
2159
- txn.maybeCommit();
2160
- return {
2161
- isError: false,
2162
- value
2163
- };
2164
- } catch (exception) {
2165
- txn.maybeRollback();
2166
- return {
2167
- isError: true,
2168
- exception
2169
- };
2170
- }
2171
- }).then((result) => {
2172
- if (result.isError) throw result.exception;
2173
- return result.value;
2174
- });
2175
- }
2176
- /** ← `DurableObjectStorage::transactionSync`, a forward for the reason above. */
2177
- transactionSync(callback) {
2178
- requireInputLock(this.ctx, "transactionSync()");
2179
- return this.#cache.transactionSync(callback);
2180
- }
2181
- /**
2182
- * ← `DurableObjectStorage::sync`.
2183
- *
2184
- * Upstream's `awaitIo` rather than `awaitIoWithInputLock`, which is the one
2185
- * storage method that deliberately opens the gate: "we're merely checking if
2186
- * we have any pending or in-flight operations, and providing a promise that
2187
- * resolves when they succeed."
2188
- */
2189
- sync() {
2190
- requireInputLock(this.ctx, "sync()");
2191
- return this.ctx.awaitIo(this.#cache.onNoPendingFlush());
2192
- }
2193
- /**
2194
- * Real, not a boundary: `ActorSqlite`'s is "an ersatz implementation that's
2195
- * good enough for local dev with D1's Session API", built on the metadata
2196
- * table's local-development bookmark. Anything above this package that
2197
- * surfaces it to an application should know it is a counter and not a
2198
- * recovery point — as it is on workerd.
2199
- */
2200
- getCurrentBookmark() {
2201
- requireInputLock(this.ctx, "getCurrentBookmark()");
2202
- return this.ctx.awaitIo(this.#cache.getCurrentBookmark());
2203
- }
2204
- waitForBookmark(bookmark) {
2205
- requireInputLock(this.ctx, "waitForBookmark()");
2206
- return this.ctx.awaitIo(this.#cache.waitForBookmark(bookmark));
2207
- }
2208
- /** Substrate boundary: point-in-time recovery. Upstream reaches the cache directly, as this does. */
2209
- getBookmarkForTime(timestamp) {
2210
- return this.#cache.getBookmarkForTime(timestamp instanceof Date ? timestamp.getTime() : timestamp);
2211
- }
2212
- /** Substrate boundary: point-in-time recovery. */
2213
- onNextSessionRestoreBookmark(bookmark) {
2214
- return this.#cache.onNextSessionRestoreBookmark(bookmark);
2215
- }
2216
- /** Substrate boundary: replication. */
2217
- ensureReplicas() {
2218
- this.#cache.ensureReplicas();
2219
- }
2220
- /** Substrate boundary: replication. */
2221
- disableReplicas() {
2222
- this.#cache.disableReplicas();
2223
- }
2224
- /**
2225
- * ← `DurableObjectStorage::getPrimary` / `isReplica`. `maybePrimary` is set
2226
- * only by the replica constructor, and nothing constructs a replica here, so
2227
- * these answer upstream's own non-replica case rather than a stubbed one.
2228
- */
2229
- getPrimary() {}
2230
- isReplica() {
2231
- return false;
2232
- }
2233
- };
2234
- var DurableObjectTransaction = class extends DurableObjectStorageOperations {
2235
- /** Becomes undefined when committed or rolled back. */
2236
- #cacheTxn;
2237
- #rolledBack = false;
2238
- constructor(ctx, cacheTxn) {
2239
- super(ctx);
2240
- this.#cacheTxn = cacheTxn;
2241
- }
2242
- getCache(op) {
2243
- if (this.#rolledBack) throw new Error(`Cannot ${op} on rolled back transaction`);
2244
- const txn = this.#cacheTxn;
2245
- if (txn === void 0) throw new Error(`Cannot call ${op} on transaction that has already committed: did you move \`txn\` outside of the closure?`);
2246
- return txn;
2247
- }
2248
- /** Called from JS. */
2249
- rollback() {
2250
- if (this.#rolledBack) return;
2251
- this.getCache(OP_ROLLBACK);
2252
- const txn = this.#cacheTxn;
2253
- if (txn !== void 0) {
2254
- txn.rollback();
2255
- txn.drop();
2256
- this.#cacheTxn = void 0;
2257
- }
2258
- this.#rolledBack = true;
2259
- }
2260
- /** Just throws an exception saying this isn't supported. */
2261
- deleteAll() {
2262
- throw new Error("Cannot call deleteAll() within a transaction");
2263
- }
2264
- /**
2265
- * Called from the runtime, not JS, after the transaction callback has
2266
- * completed. Does nothing if the transaction is already committed or rolled
2267
- * back. Synchronous, because `ActorCacheTransaction::commit` is (§1.4).
2268
- */
2269
- maybeCommit() {
2270
- const txn = this.#cacheTxn;
2271
- if (txn === void 0) return;
2272
- this.#cacheTxn = void 0;
2273
- txn.commit();
2274
- txn.drop();
2275
- }
2276
- /** Same, for the failure path. Upstream's drops the transaction, whose destructor rolls back. */
2277
- maybeRollback() {
2278
- const txn = this.#cacheTxn;
2279
- this.#cacheTxn = void 0;
2280
- this.#rolledBack = true;
2281
- txn?.drop();
2282
- }
2283
- };
2284
352
  /**
2285
- * ← `requireValidFacetName` (`actor-state.c++:949-952`).
353
+ * ← `Fetcher::getSubrequestChannel(ioctx)` followed by
354
+ * `channel->requireAllowsTransfer()` (`worker-loader.c++:125-126`, `:144-145`,
355
+ * `:157-158`).
356
+ *
357
+ * **The check has nothing to call, and that is a recorded divergence rather than
358
+ * an omission.** Upstream's `SubrequestChannel` carries `requireAllowsTransfer()`;
359
+ * here a `SubrequestChannel` and the `Fetcher` built over it are one object
360
+ * (`io/io-channels.ts`'s header, and the same collapse `api/actor.ts` and
361
+ * `api/export-loopback.ts` already made), and a `Fetcher` is
362
+ * `@cloudflare/workers-types`' interface with no such member. The one refusal it
363
+ * produces in the open-source runtime is `throwDynamicEntrypointTransferError`
364
+ * (`server.c++:167-173`), raised by `WorkerService::requireAllowsTransfer` when
365
+ * `isDynamic` — an isolate-host fact here, exactly as it is a `server/` fact there.
2286
366
  *
2287
- * The comparison is `name.size()` on a `kj::StringPtr`, which is **UTF-8 bytes**,
2288
- * so it is measured in bytes here too the same `TextEncoder` pass, for the same
2289
- * reason, that `ColoLocalActorNamespace.get`'s `[1, 2048]` bound already costs.
2290
- * Comparing `name.length` accepts a 256-character non-ASCII name that upstream
2291
- * refuses, which is a bound a caller can hit.
367
+ * Kept as a named function rather than inlined so the three call sites read as
368
+ * upstream's do, and so there is one place to put the check if a `Fetcher` seam
369
+ * ever grows one.
2292
370
  */
2293
- function requireValidFacetName(name) {
2294
- if (textEncoder.encode(name).length > 256) throw new TypeError(`Facet name is too long (max 256 characters).`);
371
+ function requireTransferableChannel(fetcher) {
372
+ return fetcher;
2295
373
  }
2296
374
  /**
2297
- * ← the `KJ_SWITCH_ONEOF(options.$class)` lambda (`actor-state.c++:1029-1043`).
375
+ * ← `Frankenvalue::fromJs(js, …)`, whose serializer refuses any object whose JSG
376
+ * resource type declares no `JSG_SERIALIZABLE` (`jsg/ser.c++:175-183`).
377
+ *
378
+ * Four types reachable from this package are in that set, and all four for one
379
+ * reason: `JSG_INHERIT` does not carry serializability. Upstream says so twice — in
380
+ * `export-loopback.h:57-58` ("Note that `LoopbackServiceStub` is intentionally NOT
381
+ * serializable, unlike its parent class Fetcher") and in the test that pins it,
382
+ * whose comment reads "it's more testing LoopbackServiceStub and that
383
+ * serializability is not inherited" (`worker-loader-test.js:91-92`). None of
384
+ * `LoopbackServiceStub`, `LoopbackDurableObjectClass`,
385
+ * `LoopbackDurableObjectNamespace` or `LoopbackColoLocalActorNamespace` declares
386
+ * `JSG_SERIALIZABLE`; every one of them *invoked* produces something that does — a
387
+ * `Fetcher` or a plain `DurableObjectClass` — which is exactly the distinction
388
+ * `worker-loader-test.js:62-107` measures, accepting the invoked form in `props`
389
+ * and refusing the bare binding.
2298
390
  *
2299
- * Three arms, and the order matters for the same reason it does upstream: a
2300
- * `LoopbackDurableObjectClass` *is* a `DurableObjectClass`, so it takes the bare
2301
- * arm here exactly as JSG's `kj::OneOf` unwraps it into the first alternative.
2302
- * The two loopback namespaces are not classes and carry one, which `getClass()`
2303
- * hands back.
391
+ * **This discharges the obligation the README left open on Section 7.** That row
392
+ * says the refusal would have to come from `src/transport/` if a `Fetcher` ever
393
+ * became serializable; the layer upstream refuses at is this one, because
394
+ * `Frankenvalue::fromJs` runs inside `getEntrypoint`, `getDurableObjectClass` and
395
+ * `toDynamicWorkerSource`. Putting it here makes it reachable today rather than
396
+ * conditional on a transport change that has not happened, and it depends on no
397
+ * transport fact — so `api/` still imports no transport library.
2304
398
  *
2305
- * A `ctx.exports` entry is the callable façade `api/export-loopback.ts` produces
2306
- * rather than the instance itself, and every check below is an `instanceof` that
2307
- * the façade's `getPrototypeOf` answers which is why that trap exists.
399
+ * The walk descends into plain objects and arrays only. Everything else is a host
400
+ * object: either one the substrate knows how to carry (a `Fetcher`, a
401
+ * `DurableObjectClass`, an `RpcTarget`) or one of the four above. Walking a host
402
+ * object's own keys would drive proxy traps — a stub's `get` mints an RPC import —
403
+ * which is a cost upstream's serializer never pays, because it asks the type rather
404
+ * than the value.
2308
405
  */
2309
- function requireFacetClass(actorClass) {
2310
- if (actorClass instanceof DurableObjectClass) return actorClass;
2311
- if (actorClass instanceof LoopbackDurableObjectNamespace) return actorClass.getClass();
2312
- if (actorClass instanceof LoopbackColoLocalActorNamespace) return actorClass.getClass();
2313
- throw new TypeError(FACET_CLASS_UNSUPPORTED_MESSAGE);
406
+ function requireSerializableProps(root, field) {
407
+ const seen = /* @__PURE__ */ new Set();
408
+ const visit = (value, path) => {
409
+ if (value === null || typeof value !== "object" && typeof value !== "function") return;
410
+ const subject = value;
411
+ const refused = notSerializableType(subject);
412
+ if (refused !== void 0) throw new DOMException(`${notSerializableMessage(refused)} At ${path}.`, "DataCloneError");
413
+ if (seen.has(subject)) return;
414
+ seen.add(subject);
415
+ if (Array.isArray(subject)) {
416
+ subject.forEach((entry, index) => {
417
+ visit(entry, `${path}[${index}]`);
418
+ });
419
+ return;
420
+ }
421
+ const prototype = Object.getPrototypeOf(subject);
422
+ if (prototype !== Object.prototype && prototype !== null) return;
423
+ for (const [name, entry] of Object.entries(subject)) visit(entry, `${path}.${name}`);
424
+ };
425
+ visit(root, `<${field}>`);
426
+ return root;
2314
427
  }
2315
428
  /**
2316
- * `DurableObjectFacets`.
429
+ * The four `ctx.exports` binding types, named by the class whose name
430
+ * `GetConstructorName()` would report.
2317
431
  *
2318
- * **`clone` is the fourth method, and the vendored C++ snapshot does not have
2319
- * it.** The design record cites `actor-state.h:431-497` and
2320
- * `server.c++:721-749`; neither line range contains it, `DurableObjectFacets`
2321
- * there exposes exactly `get`, `abort` and `delete`, and
2322
- * `Worker::Actor::FacetManager` has exactly `getDepth`, `getFacet`, `abortFacet`
2323
- * and `deleteFacet`. It is real all the same: `@cloudflare/workers-types`
2324
- * 4.20260702.1 — a month newer than the snapshot — declares
2325
- * `clone(src: string, dst: string): void` on `DurableObjectFacets`. So the
2326
- * signature comes from the types and the semantics from §1.10 (abort dst, delete
2327
- * dst storage, recursive copy of the src subtree), and the orchestration is
2328
- * `server/`'s `cloneFacet`. There is nothing upstream to check the body against,
2329
- * which makes it the one method here with no reference — worth knowing when it
2330
- * is wrong.
432
+ * The four are mutually exclusive each extends a different base
433
+ * (`api/export-loopback.ts`) so the order is presentational. What the order does
434
+ * NOT do is reach a base class: `DurableObjectClass`, `DurableObjectNamespace`,
435
+ * `ColoLocalActorNamespace` and `Fetcher` are all serializable upstream
436
+ * (`actor.h:389`), and it is exactly `JSG_INHERIT`'s failure to carry
437
+ * serializability that makes the four subclasses refuse where their bases accept.
2331
438
  */
2332
- var DurableObjectFacets = class {
2333
- #ctx;
2334
- #facetManager;
2335
- #parentId;
2336
- constructor(ctx, facetManager, parentId) {
2337
- this.#ctx = ctx;
2338
- this.#facetManager = facetManager;
2339
- this.#parentId = parentId;
2340
- }
2341
- /**
2342
- * Get a facet by name, starting it if it isn't already running.
2343
- * `getStartupOptions` is invoked only if the facet wasn't already running.
2344
- *
2345
- * Returns a `Fetcher` instead of a `DurableObject` because the returned stub
2346
- * does not have the `id` or `name` methods that a DO stub normally has.
2347
- */
2348
- get(name, getStartupOptions) {
2349
- requireValidFacetName(name);
2350
- const facetManager = this.#getFacetManager();
2351
- if (facetManager.getDepth() + 1 >= 4) throw new Error(`Facet nesting depth limit exceeded. The maximum depth including the root Durable Object is 4.`);
2352
- requireInputLock(this.#ctx, "facets.get()");
2353
- const getStartInfo = this.#ctx.makeReentryCallback(async () => {
2354
- const options = await getStartupOptions();
2355
- const id = options.id;
2356
- return {
2357
- actorClass: requireFacetClass(options.class).getChannel(),
2358
- id: id === void 0 ? this.#parentId : typeof id === "string" ? id : id.name ?? id.toString()
2359
- };
2360
- });
2361
- return facetManager.getFacet(name, getStartInfo);
2362
- }
2363
- abort(name, reason) {
2364
- requireValidFacetName(name);
2365
- this.#getFacetManager().abortFacet(name, reason);
2366
- }
2367
- delete(name) {
2368
- requireValidFacetName(name);
2369
- this.#getFacetManager().deleteFacet(name);
2370
- }
2371
- clone(src, dst) {
2372
- requireValidFacetName(src);
2373
- requireValidFacetName(dst);
2374
- this.#getFacetManager().cloneFacet(src, dst);
2375
- }
2376
- #getFacetManager() {
2377
- const facetManager = this.#facetManager;
2378
- if (facetManager === void 0) throw new Error("This Durable Object does not support creating facets.");
2379
- return facetManager;
2380
- }
2381
- };
2382
- /** The type passed as the first parameter to a Durable Object class's constructor. */
2383
- var DurableObjectState = class {
2384
- #ctx;
2385
- #options;
2386
- #facets;
2387
- constructor(ctx, options) {
2388
- this.#ctx = ctx;
2389
- this.#options = options;
2390
- }
2391
- get id() {
2392
- return this.#options.id;
2393
- }
2394
- get props() {
2395
- return this.#options.props;
2396
- }
2397
- /** ← `JSG_LAZY_INSTANCE_PROPERTY(exports, getExports)`, behind `enableCtxExports` upstream. */
2398
- get exports() {
2399
- return this.#options.exports;
2400
- }
2401
- get version() {
2402
- return this.#options.version;
2403
- }
2404
- /**
2405
- * NO upstream correspondence, because upstream needs none: a
2406
- * `ServiceWorkerGlobalScope` IS the isolate's global object there, so an
2407
- * actor's class reaches its gated `setTimeout` by writing `setTimeout`.
2408
- *
2409
- * Here one realm hosts several actors, so the names on `globalThis` can only
2410
- * be bound to one of them and a continuation cannot be asked which one it
2411
- * belongs to. `ctx` is the one reference every Durable Object class already
2412
- * holds and that already means exactly one actor — the constructor was handed
2413
- * it — so it is where the scope goes. An actor's method writes
2414
- * `this.ctx.globals.setTimeout(…)`; a free function it calls takes the scope
2415
- * as a parameter.
2416
- *
2417
- * `installActorScope` still exists and is still what a host uses for a
2418
- * dynamically-loaded Worker source, which has no `ctx` to reach through and
2419
- * its own module scope to destructure into. The two are the same object.
2420
- */
2421
- get globals() {
2422
- return this.#options.globals;
2423
- }
2424
- get storage() {
2425
- const storage = this.#options.storage;
2426
- if (storage === void 0) throw new Error("This Durable Object does not have storage.");
2427
- return storage;
2428
- }
2429
- /** ← `JSG_LAZY_INSTANCE_PROPERTY(facets, getFacets)`. */
2430
- get facets() {
2431
- this.#facets ??= new DurableObjectFacets(this.#ctx, this.#options.facets, this.#options.id.toString());
2432
- return this.#facets;
2433
- }
2434
- waitUntil(promise) {
2435
- this.#ctx.addWaitUntil(promise.then(() => {}));
2436
- }
2437
- /**
2438
- * ← `DurableObjectState::blockConcurrencyWhile` (`actor-state.c++:1128-1131`),
2439
- * which is a one-line forward and nothing else. The 30-second deadline, the
2440
- * brokenness annotation and the never-settled promise on failure all live in
2441
- * `IoContext::blockConcurrencyWhile`, which Section 2 already implements.
2442
- *
2443
- * Its precondition comes with it: `IoContext::blockConcurrencyWhile` calls
2444
- * `getInputLock()`, which asserts, so this is reachable only from inside a
2445
- * gated slice.
2446
- */
2447
- blockConcurrencyWhile(callback) {
2448
- return this.#ctx.blockConcurrencyWhile(callback);
2449
- }
2450
- /**
2451
- * ← `DurableObjectState::abort`. Reset the object, including breaking the
2452
- * output gate and canceling any writes that haven't been committed yet.
2453
- *
2454
- * `js.terminateExecutionNow()` has no port — there is no isolate to terminate —
2455
- * so the caller's own slice keeps running to its next await, where `IoContext`
2456
- * refuses to re-enter.
2457
- */
2458
- abort(reason) {
2459
- const description = reason === void 0 ? "broken.outputGateBroken; jsg.Error: Application called abort() to reset Durable Object." : `broken.outputGateBroken; jsg.Error: ${reason}`;
2460
- const error = new Error(description);
2461
- setUserErrorDetail(error);
2462
- this.#options.storage?.getActorCacheInterface().shutdown(error);
2463
- this.#ctx.abort(error);
2464
- }
2465
- /** ← `DurableObjectState::getPrimaryStub`. Non-null only for a replica; see the storage note. */
2466
- get primaryStub() {
2467
- return this.#options.storage?.getPrimary();
2468
- }
2469
- /** Substrate boundary: replication. */
2470
- configureReadReplication(options) {
2471
- const storage = this.#options.storage;
2472
- if (storage === void 0) throw new TypeError("This actor does not support read replication.");
2473
- if (storage.isReplica()) throw new Error("Replica Durable Objects cannot call configureReadReplication().");
2474
- if (options.mode !== "auto" && options.mode !== "disabled") throw new TypeError(`configureReadReplication() called with unknown mode setting: ${options.mode}.`);
2475
- return this.#ctx.awaitIo(storage.getActorCacheInterface().configureReadReplication(options.mode === "auto"));
2476
- }
2477
- acceptWebSocket(_ws, _tags) {
2478
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2479
- }
2480
- getWebSockets(_tag) {
2481
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2482
- }
2483
- setWebSocketAutoResponse(_maybeReqResp) {
2484
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2485
- }
2486
- getWebSocketAutoResponse() {
2487
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2488
- }
2489
- getWebSocketAutoResponseTimestamp(_ws) {
2490
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2491
- }
2492
- setHibernatableWebSocketEventTimeout(_timeoutMs) {
2493
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2494
- }
2495
- getHibernatableWebSocketEventTimeout() {
2496
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2497
- }
2498
- getTags(_ws) {
2499
- throw new Error(HIBERNATION_UNIMPLEMENTED_MESSAGE);
2500
- }
2501
- };
439
+ function notSerializableType(value) {
440
+ if (value instanceof LoopbackServiceStub) return "LoopbackServiceStub";
441
+ if (value instanceof LoopbackDurableObjectNamespace) return "LoopbackDurableObjectNamespace";
442
+ if (value instanceof LoopbackColoLocalActorNamespace) return "LoopbackColoLocalActorNamespace";
443
+ if (value instanceof LoopbackDurableObjectClass) return "LoopbackDurableObjectClass";
444
+ }
2502
445
  //#endregion
2503
446
  //#region src/api/http.ts
2504
447
  /**
@@ -3019,10 +962,11 @@ var ActorGlobalScope = class {
3019
962
  #readCurrentExternalEntry;
3020
963
  scheduler;
3021
964
  crypto;
3022
- constructor(ctx, options = {}) {
965
+ constructor(ctx, options) {
3023
966
  this.#ctx = ctx;
3024
967
  this.#fetch = options.fetch;
3025
968
  this.#readCurrentExternalEntry = options.currentExternalEntry;
969
+ installWebSocketGlobals(this, options.webSockets.WebSocketPair);
3026
970
  this.scheduler = new Scheduler(this);
3027
971
  this.crypto = new GatedCrypto((op) => {
3028
972
  this.#requireOwnSlice(op);
@@ -3127,6 +1071,7 @@ var ActorGlobalScope = class {
3127
1071
  * scope instead. A single-actor host simply writes `() => scope`.
3128
1072
  */
3129
1073
  function actorScopeBindings(resolve) {
1074
+ const BoundWebSocketPair = new Proxy(class WebSocketPair {}, { construct: () => new (resolve()).WebSocketPair() });
3130
1075
  return {
3131
1076
  awaitIo: (promise) => resolve().awaitIo(promise),
3132
1077
  scheduler: {
@@ -3143,6 +1088,9 @@ function actorScopeBindings(resolve) {
3143
1088
  },
3144
1089
  fetch: (input, init) => resolve().fetch(input, init),
3145
1090
  crypto: scopeCrypto(resolve),
1091
+ WebSocket: globalThis.WebSocket,
1092
+ WebSocketPair: BoundWebSocketPair,
1093
+ WebSocketRequestResponsePair: RuntimeWebSocketRequestResponsePair,
3146
1094
  get currentExternalEntry() {
3147
1095
  return resolve().currentExternalEntry;
3148
1096
  }
@@ -3153,7 +1101,7 @@ function actorScopeBindings(resolve) {
3153
1101
  * an operation actually runs.
3154
1102
  *
3155
1103
  * That laziness is required rather than tidy, and both lanes proved it. A facet's
3156
- * module destructures its seven names at module scope, which is BEFORE its container
1104
+ * module destructures its actor globals at module scope, which is BEFORE its container
3157
1105
  * exists — so a `crypto` that resolved on read threw at import. And on the root
3158
1106
  * path `globalThis.crypto` is read by things that are not the actor at all: capnweb,
3159
1107
  * the sqlite driver, the test runner. So the binding is a pair of plain objects
@@ -3199,8 +1147,8 @@ var ASYNC_SUBTLE_METHODS = [
3199
1147
  * one that is not.
3200
1148
  *
3201
1149
  * **A host should call this rather than assigning the names itself**, and the
3202
- * reason is the failure it prevents: a host that installs five of the six leaves
3203
- * one primitive ungated, and an ungated primitive that WORKS is invisible until
1150
+ * reason is the failure it prevents: a host that installs only a subset leaves a
1151
+ * primitive ungated, and an ungated primitive that WORKS is invisible until
3204
1152
  * a continuation after it touches storage — possibly never, on the path that
3205
1153
  * matters. The set is the package's, so it can grow without every host growing
3206
1154
  * with it.
@@ -3229,153 +1177,6 @@ function installActorScope(target, resolve) {
3229
1177
  });
3230
1178
  }
3231
1179
  }
3232
- //#endregion
3233
- //#region src/api/web-socket.ts
3234
- /** ← the `JSG_REQUIRE(!native.state.is<Accepted>(), ...)` at the head of `accept()`. */
3235
- var ALREADY_ACCEPTED_MESSAGE = "acceptWebSocket(): this socket has already been accepted by an actor. A socket's frames are delivered by exactly one read loop, and a second accept would deliver them under two gates.";
3236
- /** Sockets this runtime has accepted, so the refusal above is answerable. */
3237
- var accepted = /* @__PURE__ */ new WeakSet();
3238
- /** The four events a `WebSocket` dispatches, which `readLoop` and its `.then` cover upstream. */
3239
- var SOCKET_EVENTS = [
3240
- "open",
3241
- "message",
3242
- "close",
3243
- "error"
3244
- ];
3245
- /**
3246
- * ← `WebSocket::Accepted` (`web-socket.h:~300-360`), reached through
3247
- * `accept()` → `internalAccept(js, IoContext::current().getCriticalSection())`
3248
- * → `startReadLoop` (`web-socket.c++:133`, `:426`, `:429-433`, `:507`).
3249
- *
3250
- * An `EventTarget`, so a consumer registers listeners the way it would on a real
3251
- * socket — but on THIS object rather than on the raw one, because this is what
3252
- * runs them inside a gated slice.
3253
- */
3254
- var AcceptedWebSocket = class extends EventTarget {
3255
- #ctx;
3256
- #socket;
3257
- /**
3258
- * ← `readLoop`'s `cs` parameter, captured at accept and replayed for every
3259
- * frame via `mapAddRef(cs)` (`web-socket.c++:1110`). A socket accepted inside
3260
- * `blockConcurrencyWhile` therefore delivers its messages inside that critical
3261
- * section — §1.8's second bullet, and the reason this is captured here rather
3262
- * than read when a frame arrives.
3263
- */
3264
- #criticalSection;
3265
- /**
3266
- * ← `OutgoingMessagesMap outgoingMessages` plus `ensurePumping`
3267
- * (`web-socket.h:582-590`, `web-socket.c++:948-975`), as a chain.
3268
- *
3269
- * The table is insertion-ordered and the pump awaits each entry's own
3270
- * `outputLock` before sending it, so messages leave in order and message N
3271
- * waits only for the writes outstanding when IT was enqueued. A promise chain
3272
- * is the same two properties with nothing to schedule.
3273
- */
3274
- #pump = Promise.resolve();
3275
- onopen = null;
3276
- onmessage = null;
3277
- onclose = null;
3278
- onerror = null;
3279
- constructor(ctx, socket) {
3280
- super();
3281
- this.#ctx = ctx;
3282
- this.#socket = socket;
3283
- this.#criticalSection = ctx.getCriticalSection();
3284
- for (const type of SOCKET_EVENTS) socket.addEventListener(type, (event) => {
3285
- this.#deliver(type, event);
3286
- });
3287
- }
3288
- /**
3289
- * ← `co_await context.run([...](auto& wLock) { dispatchEventImpl(...) }, mapAddRef(cs))`
3290
- * (`web-socket.c++:1065-1110`).
3291
- *
3292
- * The run rides `addWaitUntil`, as upstream's read loop does ("We put the read
3293
- * loop in a `waitUntil`, since there would otherwise be a race condition
3294
- * between delivering the final close message and the request being canceled",
3295
- * `web-socket.c++:537-541`). That is also what stops a listener's throw
3296
- * becoming an unhandled rejection: it lands in `waitUntilStatus()`.
3297
- */
3298
- #deliver(type, event) {
3299
- this.#ctx.addWaitUntil(this.#ctx.run(() => {
3300
- const delivered = cloneEventFor(type, event);
3301
- this.dispatchEvent(delivered);
3302
- const handler = this[`on${type}`];
3303
- handler?.(delivered);
3304
- }, { input: this.#criticalSection }));
3305
- }
3306
- /**
3307
- * ← `WebSocket::send` (`web-socket.c++:~640`), which inserts a
3308
- * `GatedMessage{IoContext::current().waitForOutputLocksIfNecessary(), …}`.
3309
- *
3310
- * Synchronous, as upstream's is: the wait is the pump's, not the caller's. The
3311
- * output gate is what "blocks all outgoing messages from an actor that would
3312
- * allow the rest of the world to observe the actor's state" (§1.1), and a
3313
- * socket frame is exactly such a message.
3314
- *
3315
- * `waitForOutputLocksIfNecessary()` collapses to `waitForOutputLocks()` here
3316
- * for the reason the whole file collapses `kj::Maybe<Worker::Actor&>`: its
3317
- * body is `actor.map(…)` (`io-context.c++:383-386`) and every context in this
3318
- * runtime is an actor context.
3319
- */
3320
- send(data) {
3321
- this.#enqueue(() => {
3322
- this.#socket.send(data);
3323
- });
3324
- }
3325
- /** ← `WebSocket::close`, which enqueues a `Close` through the same gate. */
3326
- close(code, reason) {
3327
- this.#enqueue(() => {
3328
- this.#socket.close(code, reason);
3329
- });
3330
- }
3331
- #enqueue(write) {
3332
- const outputLock = this.#ctx.waitForOutputLocks();
3333
- this.#pump = this.#pump.then(async () => {
3334
- await outputLock;
3335
- write();
3336
- });
3337
- this.#ctx.addWaitUntil(this.#pump);
3338
- }
3339
- };
3340
- /**
3341
- * ← `accept()` / `state.acceptWebSocket()`, as the one verb.
3342
- *
3343
- * Named for what upstream names it, because the critical-section capture is a
3344
- * property of accepting rather than of constructing: "a socket accepted inside a
3345
- * `blockConcurrencyWhile` delivers its messages inside that critical section"
3346
- * (§1.8).
3347
- */
3348
- function acceptWebSocket(ctx, socket) {
3349
- if (accepted.has(socket)) throw new Error(ALREADY_ACCEPTED_MESSAGE);
3350
- accepted.add(socket);
3351
- return new AcceptedWebSocket(ctx, socket);
3352
- }
3353
- /**
3354
- * An `Event` may be dispatched by exactly one target at a time, so the raw
3355
- * socket's event object cannot be re-dispatched: `dispatchEvent` on an event
3356
- * that is already dispatched throws `InvalidStateError`, and one that has
3357
- * finished carries the raw socket as its `target`. Rebuilding it is what makes
3358
- * `event.target` the accepted socket, which is what a listener expects.
3359
- */
3360
- function cloneEventFor(type, event) {
3361
- if (type === "message") {
3362
- const source = event;
3363
- return new MessageEvent("message", {
3364
- data: source.data,
3365
- origin: source.origin,
3366
- lastEventId: source.lastEventId
3367
- });
3368
- }
3369
- if (type === "close") {
3370
- const source = event;
3371
- return new CloseEvent("close", {
3372
- code: source.code,
3373
- reason: source.reason,
3374
- wasClean: source.wasClean
3375
- });
3376
- }
3377
- return new Event(type);
3378
- }
3379
1180
  /**
3380
1181
  * ← the message every unimplemented `ActorCacheInterface` PITR method throws.
3381
1182
  * `ActorSqlite` overrides two of the four; the other two keep this.
@@ -5608,14 +3409,16 @@ var ActorTree = class {
5608
3409
  * which is the whole mechanism behind §1.10's parent↔child re-entrancy.
5609
3410
  */
5610
3411
  var ActorImpl = class {
5611
- #inputGate = new InputGate();
5612
- #outputGate = new OutputGate();
3412
+ #inputGate;
3413
+ #outputGate;
5613
3414
  #isFacet;
5614
3415
  /** Assigned after construction; `storage` is a WXT auto-import in extension bundles. */
5615
3416
  actorStorage;
5616
3417
  classInstance = { kind: "before-ctor" };
5617
- constructor(isFacet) {
3418
+ constructor(isFacet, hooks = {}) {
5618
3419
  this.#isFacet = isFacet;
3420
+ this.#inputGate = new InputGate(hooks.input);
3421
+ this.#outputGate = new OutputGate(hooks.output);
5619
3422
  }
5620
3423
  getInputGate() {
5621
3424
  return this.#inputGate;
@@ -5925,6 +3728,7 @@ var ActorContainerImpl = class {
5925
3728
  #facets;
5926
3729
  #tree;
5927
3730
  #env;
3731
+ #webSockets;
5928
3732
  state;
5929
3733
  facetTree;
5930
3734
  globals;
@@ -5932,16 +3736,22 @@ var ActorContainerImpl = class {
5932
3736
  #alarmTail = Promise.resolve();
5933
3737
  constructor(options, db, tree, facetTree) {
5934
3738
  const facet = options.facet;
5935
- this.#actor = new ActorImpl(facet !== void 0);
3739
+ this.#actor = new ActorImpl(facet !== void 0, options.gateHooks);
5936
3740
  this.#ctx = new IoContext(this.#actor, options.ports.timer);
5937
3741
  this.#env = options.env;
5938
3742
  this.#tree = tree;
5939
3743
  this.#cache = new ActorSqlite(db, this.#actor.getOutputGate(), async () => {}, facet === void 0 ? options.ports.alarms : DEFAULT_ALARM_OUTLET);
5940
3744
  this.#actor.actorStorage = this.#cache;
5941
3745
  this.#durableStorage = new DurableObjectStorage(this.#ctx, this.#cache);
3746
+ this.#webSockets = new HibernatableWebSocketRegistry(this.#ctx, {
3747
+ message: (socket, message) => this.#runWebSocketHandler("webSocketMessage", socket, message),
3748
+ close: (socket, code, reason, wasClean) => this.#runWebSocketHandler("webSocketClose", socket, code, reason, wasClean),
3749
+ error: (socket, error) => this.#runWebSocketHandler("webSocketError", socket, error)
3750
+ }, options.ports.hibernation, options.webSockets);
5942
3751
  this.globals = new ActorGlobalScope(this.#ctx, {
5943
3752
  fetch: options.ports.fetch,
5944
- currentExternalEntry: () => this.#currentExternalEntry
3753
+ currentExternalEntry: () => this.#currentExternalEntry,
3754
+ webSockets: this.#webSockets
5945
3755
  });
5946
3756
  this.facetTree = facetTree;
5947
3757
  this.#facets = new FacetManagerImpl(this, options.ports.facets, facet?.id ?? 0, facet?.depth ?? 0, this.facetTree);
@@ -5955,7 +3765,8 @@ var ActorContainerImpl = class {
5955
3765
  props: void 0,
5956
3766
  storage: this.#durableStorage,
5957
3767
  facets: this.#facets,
5958
- globals: actorScopeBindings(() => this.globals)
3768
+ globals: actorScopeBindings(() => this.globals),
3769
+ webSockets: this.#webSockets
5959
3770
  });
5960
3771
  }
5961
3772
  get onBroken() {
@@ -6089,6 +3900,14 @@ var ActorContainerImpl = class {
6089
3900
  drainWaitUntil() {
6090
3901
  return this.#ctx.drainWaitUntil();
6091
3902
  }
3903
+ quiescence() {
3904
+ return {
3905
+ armedTimers: this.#ctx.getTimeoutCount(),
3906
+ pendingWaitUntil: this.#ctx.waitUntilTaskCount(),
3907
+ inputLockHeld: this.#ctx.hasCurrent(),
3908
+ outputGateBroken: this.#ctx.isOutputGateBroken()
3909
+ };
3910
+ }
6092
3911
  /** ← `WorkerdApi::compileGlobals`'s `Global::WorkerLoader` arm. */
6093
3912
  workerLoader(channel, options) {
6094
3913
  return new WorkerLoader(this.#ctx, channel, options);
@@ -6182,6 +4001,13 @@ var ActorContainerImpl = class {
6182
4001
  if (!hasAlarmHandler(instance.instance)) throw new TypeError("Your Durable Object class must have an alarm() handler.");
6183
4002
  return instance.instance.alarm(new AlarmInvocationInfo(scheduledTime, retryCount));
6184
4003
  }
4004
+ #runWebSocketHandler(name, socket, ...args) {
4005
+ const instance = this.#actor.classInstance;
4006
+ if (instance.kind !== "running") return void 0;
4007
+ const handler = Reflect.get(instance.instance, name);
4008
+ if (typeof handler !== "function") return void 0;
4009
+ return Reflect.apply(handler, instance.instance, [socket, ...args]);
4010
+ }
6185
4011
  };
6186
4012
  /**
6187
4013
  * Builds one actor: the two gates, the `IoContext` over them, the storage engine
@@ -6203,6 +4029,53 @@ async function createActorContainer(options) {
6203
4029
  return new ActorContainerImpl(options, db, tree, tree);
6204
4030
  }
6205
4031
  //#endregion
4032
+ //#region src/server/hibernation-mirror.ts
4033
+ /** In-memory socket state shared by embedders that replace live actor containers. */
4034
+ var HibernationMirror = class {
4035
+ #entries = /* @__PURE__ */ new Map();
4036
+ #autoResponsePair;
4037
+ constructor(rehydrated = [], autoResponsePair = null) {
4038
+ for (const value of rehydrated) this.#entries.set(value.socket, cloneEntry(value));
4039
+ this.#autoResponsePair = cloneAutoResponse(autoResponsePair);
4040
+ }
4041
+ get autoResponsePair() {
4042
+ return cloneAutoResponse(this.#autoResponsePair);
4043
+ }
4044
+ accepted(socket, tags) {
4045
+ this.#entries.set(socket, {
4046
+ socket,
4047
+ tags: [...tags]
4048
+ });
4049
+ }
4050
+ attachment(socket, bytes) {
4051
+ const entry = this.#entries.get(socket);
4052
+ if (entry === void 0) throw new Error("Hibernation mirror: attachment preceded socket acceptance.");
4053
+ if (bytes === null) delete entry.attachment;
4054
+ else entry.attachment = bytes.slice();
4055
+ }
4056
+ autoResponse(pair) {
4057
+ this.#autoResponsePair = cloneAutoResponse(pair);
4058
+ }
4059
+ closed(socket) {
4060
+ this.#entries.delete(socket);
4061
+ }
4062
+ snapshot() {
4063
+ return [...this.#entries.values()].map(cloneEntry);
4064
+ }
4065
+ };
4066
+ function cloneEntry(value) {
4067
+ const entry = {
4068
+ socket: value.socket,
4069
+ tags: [...value.tags ?? []]
4070
+ };
4071
+ if (value.attachment !== void 0) entry.attachment = value.attachment.slice();
4072
+ if (value.autoResponseTimestamp !== void 0) entry.autoResponseTimestamp = value.autoResponseTimestamp;
4073
+ return entry;
4074
+ }
4075
+ function cloneAutoResponse(pair) {
4076
+ return pair === null ? null : { ...pair };
4077
+ }
4078
+ //#endregion
6206
4079
  //#region src/server/actor-namespace.ts
6207
4080
  /** Assemble the configured namespace binding a host places in `env`. */
6208
4081
  function createDurableObjectNamespace(uniqueKey, channel) {
@@ -6252,6 +4125,6 @@ function newRpcSession(port, localMain) {
6252
4125
  return newMessagePortRpcSession(port, localMain);
6253
4126
  }
6254
4127
  //#endregion
6255
- export { ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE, ALARM_RETRY_MAX_TRIES, ALARM_RETRY_START_SECONDS, ALLOW_EXPERIMENTAL_MESSAGE, ALREADY_ACCEPTED_MESSAGE, AlarmInvocationInfo, AlarmScheduler, BYOB_READER_UNGATABLE_MESSAGE, BrokenActorError, CanceledError, DEAD_LOAD_CONTEXT_MESSAGE, DEFAULT_ALARM_OUTLET, FACET_ALARM_UNIMPLEMENTED_MESSAGE, FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH, FOREIGN_SLICE_MESSAGE, HIBERNATION_UNIMPLEMENTED_MESSAGE, LoopbackDurableObjectClass, NOT_BYTES_MESSAGE, NO_GLOBAL_OUTBOUND_MESSAGE, NO_MODULES_MESSAGE, PITR_UNIMPLEMENTED_MESSAGE, REPLICATION_UNIMPLEMENTED_MESSAGE, RETRY_BACKOFF_MAX, RETRY_JITTER_FACTOR, STREAMING_TAILS_EXPERIMENTAL_MESSAGE, WorkerLoader, WorkerStub, actorScopeBindings, alarmRetryDelayMs, asLoopbackDurableObjectClass, createActorContainer, createDurableObjectNamespace, gateRequestBody, installActorScope, jsModuleInPythonWorkerMessage, moduleFieldCountMessage, moduleNameMessage, newRpcSession, noFacets, notSerializableMessage, pythonModuleInJsWorkerMessage, typeScriptModuleNameMessage };
4128
+ export { ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE, ALARM_RETRY_MAX_TRIES, ALARM_RETRY_START_SECONDS, ALLOW_EXPERIMENTAL_MESSAGE, ALREADY_ACCEPTED_MESSAGE, AlarmInvocationInfo, AlarmScheduler, BYOB_READER_UNGATABLE_MESSAGE, BrokenActorError, CanceledError, DEAD_LOAD_CONTEXT_MESSAGE, DEFAULT_ALARM_OUTLET, FACET_ALARM_UNIMPLEMENTED_MESSAGE, FACET_NAME_MAX_LENGTH, FACET_TREE_MAX_DEPTH, FOREIGN_SLICE_MESSAGE, HibernationMirror, LoopbackDurableObjectClass, NOT_BYTES_MESSAGE, NO_GLOBAL_OUTBOUND_MESSAGE, NO_MODULES_MESSAGE, PITR_UNIMPLEMENTED_MESSAGE, REPLICATION_UNIMPLEMENTED_MESSAGE, RETRY_BACKOFF_MAX, RETRY_JITTER_FACTOR, STREAMING_TAILS_EXPERIMENTAL_MESSAGE, RuntimeWebSocketRequestResponsePair as WebSocketRequestResponsePair, WorkerLoader, WorkerStub, actorScopeBindings, alarmRetryDelayMs, asLoopbackDurableObjectClass, createActorContainer, createDurableObjectNamespace, gateRequestBody, installActorScope, installWebSocketGlobals, jsModuleInPythonWorkerMessage, markWebSocketUsed, moduleFieldCountMessage, moduleNameMessage, newRpcSession, noFacets, notSerializableMessage, pythonModuleInJsWorkerMessage, typeScriptModuleNameMessage };
6256
4129
 
6257
4130
  //# sourceMappingURL=index.js.map