@mcp-b/do-runtime 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2573 @@
1
+ import { c as setUserErrorDetail, s as requireInputLock } from "./io-context-BBgKEsdR.js";
2
+ import { deserialize, serialize } from "@ungap/structured-clone";
3
+ //#region src/api/actor.ts
4
+ /**
5
+ * ← the `[1, 2048]` bound in `ColoLocalActorNamespace::get`.
6
+ *
7
+ * Upstream compares `actorId.size()`, which for a `kj::String` is **bytes**, so
8
+ * this is measured in UTF-8 bytes rather than in UTF-16 code units. That costs
9
+ * one `TextEncoder` pass and buys an exact match on a bound a caller can hit.
10
+ */
11
+ var MAX_COLO_LOCAL_ACTOR_ID_BYTES = 2048;
12
+ /**
13
+ * Upstream never faces this: JSG unwraps a `jsg::Ref<DurableObjectId>` parameter
14
+ * and throws a `TypeError` before the method body runs, so `getInner()` cannot be
15
+ * reached on something that is not one. Here the parameter type is
16
+ * workers-types' structural `DurableObjectId` interface, which any object with a
17
+ * `toString` and an `equals` satisfies — so the unwrap has to be written, and it
18
+ * fails closed rather than guessing at the string form.
19
+ */
20
+ var FOREIGN_ACTOR_ID_MESSAGE = "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.";
21
+ /** Substrate boundary: `jsg::Serializer`, `Frankenvalue` and channel tokens have no port. */
22
+ 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.";
23
+ /**
24
+ * Replication is a named substrate boundary (`io/actor-cache.ts`), so no request
25
+ * this file builds asks for replica routing. Upstream reads
26
+ * `FeatureFlags::get(js).getReplicaRouting()` here.
27
+ */
28
+ var ENABLE_REPLICA_ROUTING = false;
29
+ /**
30
+ * ← `ColoLocalActorNamespace` (`actor.h:25-37`). "A capability to an ephemeral
31
+ * Actor namespace."
32
+ */
33
+ var ColoLocalActorNamespace = class {
34
+ #channel;
35
+ constructor(channel) {
36
+ this.#channel = channel;
37
+ }
38
+ /** ← `ColoLocalActorNamespace::get` (`actor.c++:116-129`). */
39
+ get(actorId) {
40
+ const bytes = utf8Length(actorId);
41
+ if (!(bytes > 0 && bytes <= 2048)) throw new TypeError(`Actor ID length must be in the range [1, ${MAX_COLO_LOCAL_ACTOR_ID_BYTES}].`);
42
+ return this.#channel.getColoLocalActor({ actorId });
43
+ }
44
+ };
45
+ var textEncoder$2 = new TextEncoder();
46
+ /** `kj::String::size()` is bytes; `String.prototype.length` is UTF-16 code units. */
47
+ function utf8Length(value) {
48
+ return textEncoder$2.encode(value).length;
49
+ }
50
+ /**
51
+ * ← `DurableObjectId` (`actor.h:42-84`). "DurableObjectId type seen by
52
+ * JavaScript."
53
+ *
54
+ * `name` and `jurisdiction` are read from the inner id **once, at construction**,
55
+ * where upstream's are `JSG_READONLY_INSTANCE_PROPERTY`s that re-read it on every
56
+ * access. That is not a preference: `@cloudflare/workers-types` declares both
57
+ * `readonly name?: string`, and under `exactOptionalPropertyTypes` a getter
58
+ * returning `string | undefined` does not satisfy an optional `string`. An own
59
+ * property assigned only when the value exists does, and it is what keeps this
60
+ * class assignable to the interface with no cast (§2.4). The one behaviour lost
61
+ * is `ActorIdImpl::clearName()` (`server/actor-id-impl.h`) taking effect on an
62
+ * already-wrapped id — a `server/`-internal that runs before the id reaches JS.
63
+ */
64
+ var DurableObjectId = class {
65
+ #id;
66
+ name;
67
+ jurisdiction;
68
+ constructor(id) {
69
+ this.#id = id;
70
+ const name = id.getName();
71
+ if (name !== void 0) this.name = name;
72
+ const jurisdiction = id.getJurisdiction();
73
+ if (jurisdiction !== void 0) this.jurisdiction = jurisdiction;
74
+ }
75
+ /** ← `getInner()`. Not JS-visible upstream either; the outgoing factories take it. */
76
+ getInner() {
77
+ return this.#id;
78
+ }
79
+ /** "Converts to a string which can be passed back to the constructor to reproduce the same ID." */
80
+ toString() {
81
+ return this.#id.toString();
82
+ }
83
+ equals(other) {
84
+ return this.#id.equals(requireDurableObjectId(other).getInner());
85
+ }
86
+ };
87
+ /** The unwrap JSG performs for a `jsg::Ref<DurableObjectId>` parameter. */
88
+ function requireDurableObjectId(id) {
89
+ if (id instanceof DurableObjectId) return id;
90
+ throw new TypeError(FOREIGN_ACTOR_ID_MESSAGE);
91
+ }
92
+ /**
93
+ * ← `DurableObject` (`actor.h:87-139`). "Stub object used to send messages to a
94
+ * remote durable object."
95
+ *
96
+ * Upstream's carries its whole behaviour by `JSG_INHERIT(Fetcher)` and adds
97
+ * exactly two readonly properties. So does this: the `Fetcher` is the transport's
98
+ * and everything except `id` and `name` belongs to it. `asDurableObjectStub`
99
+ * below is where the inheritance goes.
100
+ */
101
+ var DurableObject = class {
102
+ #id;
103
+ #fetcher;
104
+ constructor(id, fetcher) {
105
+ this.#id = id;
106
+ this.#fetcher = fetcher;
107
+ }
108
+ /** ← `JSG_READONLY_INSTANCE_PROPERTY(id, getId)`. */
109
+ getId() {
110
+ return this.#id;
111
+ }
112
+ /** ← `JSG_READONLY_INSTANCE_PROPERTY(name, getName)`. */
113
+ getName() {
114
+ return this.#id.name;
115
+ }
116
+ /** The `Fetcher` upstream inherits from rather than holds. */
117
+ getFetcher() {
118
+ return this.#fetcher;
119
+ }
120
+ };
121
+ /**
122
+ * ← `js.alloc<DurableObject>(...)` plus `JSG_INHERIT(Fetcher)` plus the
123
+ * `JSG_TS_OVERRIDE` that renames the resource type to `DurableObjectStub`.
124
+ *
125
+ * The named assertion is the same one `io/worker.ts`'s `asFacetStub` makes and
126
+ * for the same reason: `DurableObjectStub<T>` is `Fetcher<T, …> & { id, name }`,
127
+ * and `Fetcher<T>` for an unresolved `T` is `Rpc.Provider<T, …>`, a conditional
128
+ * type TypeScript defers until `T` is known — where `T` is the caller's claim
129
+ * about a class it named, which no value can confirm. Upstream is in the same
130
+ * position and answers it the same way, with the parameter living only inside a
131
+ * `JSG_TS_OVERRIDE`.
132
+ *
133
+ * The `Proxy` is what JSG inheritance costs in JS. Two properties have to answer
134
+ * from the id and every other property — `fetch`, `connect`, and every RPC method
135
+ * name, which are the whole point of a stub — has to reach the transport with
136
+ * `this` still bound to it. Bound methods are memoised so `stub.foo === stub.foo`,
137
+ * which upstream gets for free by there being one object rather than two.
138
+ */
139
+ function asDurableObjectStub(object) {
140
+ const fetcher = object.getFetcher();
141
+ const bound = /* @__PURE__ */ new Map();
142
+ return new Proxy(fetcher, {
143
+ get(target, property) {
144
+ if (property === "id") return object.getId();
145
+ if (property === "name") return object.getName();
146
+ const cached = bound.get(property);
147
+ if (cached !== void 0) return cached;
148
+ const value = Reflect.get(target, property, target);
149
+ if (typeof value !== "function") return value;
150
+ const method = value.bind(target);
151
+ bound.set(property, method);
152
+ return method;
153
+ },
154
+ has(target, property) {
155
+ if (property === "id" || property === "name") return true;
156
+ return Reflect.has(target, property);
157
+ },
158
+ ownKeys(target) {
159
+ return [
160
+ "id",
161
+ "name",
162
+ ...Reflect.ownKeys(target).filter((key) => key !== "id" && key !== "name")
163
+ ];
164
+ },
165
+ getOwnPropertyDescriptor(target, property) {
166
+ if (property === "id" || property === "name") return {
167
+ value: property === "id" ? object.getId() : object.getName(),
168
+ writable: false,
169
+ enumerable: true,
170
+ configurable: true
171
+ };
172
+ return Reflect.getOwnPropertyDescriptor(target, property);
173
+ }
174
+ });
175
+ }
176
+ /**
177
+ * ← `DurableObjectNamespace` (`actor.h:142-291`). "Global durable object class
178
+ * binding type."
179
+ */
180
+ var DurableObjectNamespace = class DurableObjectNamespace {
181
+ #channel;
182
+ #idFactory;
183
+ constructor(channel, idFactory) {
184
+ this.#channel = channel;
185
+ this.#idFactory = idFactory;
186
+ }
187
+ /**
188
+ * "Create a new unique ID for a durable object that will be allocated nearby
189
+ * the calling colo."
190
+ */
191
+ newUniqueId(options) {
192
+ return new DurableObjectId(this.#idFactory.newUniqueId(options?.jurisdiction ?? void 0));
193
+ }
194
+ /**
195
+ * "Create a name-derived ID. Passing in the same `name` (to the same class)
196
+ * will always produce the same ID."
197
+ */
198
+ idFromName(name) {
199
+ return new DurableObjectId(this.#idFactory.idFromName(name));
200
+ }
201
+ /**
202
+ * "Create a DurableObjectId from the stringified form of the ID (as produced by
203
+ * calling `toString()` on a durable object ID). Throws if the ID is not a
204
+ * 64-digit hex number, or if the ID was not originally created for this class."
205
+ */
206
+ idFromString(id) {
207
+ return new DurableObjectId(this.#idFactory.idFromString(id));
208
+ }
209
+ /** "Gets a durable object by ID or creates it if it doesn't already exist." */
210
+ get(id, options) {
211
+ return this.#getImpl("GET_OR_CREATE", id, options);
212
+ }
213
+ /**
214
+ * "Gets a durable object by name or creates it if it doesn't already exist.
215
+ * Short for `idFromName()` followed by `get()`."
216
+ */
217
+ getByName(name, options) {
218
+ return this.#getImpl("GET_OR_CREATE", this.idFromName(name), options);
219
+ }
220
+ /**
221
+ * "Experimental. Gets a durable object by ID if it already exists. Currently,
222
+ * gated for use by cloudflare only."
223
+ *
224
+ * Upstream exposes it only when the `durableObjectGetExisting` compat flag is
225
+ * on, and `@cloudflare/workers-types` 4.20260702.1 does not declare it. It is
226
+ * exposed unconditionally here, which is the current-behaviour reading every
227
+ * other compat flag in this file gets.
228
+ */
229
+ getExisting(id, options) {
230
+ return this.#getImpl("GET_EXISTING", id, options);
231
+ }
232
+ /**
233
+ * "Creates a subnamespace with the jurisdiction hardcoded."
234
+ *
235
+ * The argument is optional because upstream's is a
236
+ * `jsg::Optional<kj::Maybe<kj::String>>`, so both "omitted" and "null" mean the
237
+ * same thing — `cloneWithJurisdiction(kj::none)`, a subnamespace with none.
238
+ */
239
+ jurisdiction(jurisdiction) {
240
+ return new DurableObjectNamespace(this.#channel, this.#idFactory.cloneWithJurisdiction(jurisdiction ?? void 0));
241
+ }
242
+ /** ← `DurableObjectNamespace::getImpl` (`actor.c++:167-213`). */
243
+ #getImpl(mode, id, options) {
244
+ const durableObjectId = requireDurableObjectId(id);
245
+ const inner = durableObjectId.getInner();
246
+ if (!this.#idFactory.matchesJurisdiction(inner)) throw new TypeError("get called on jurisdictional subnamespace with an ID from a different jurisdiction");
247
+ let routingMode = "DEFAULT";
248
+ const requestedRoutingMode = options?.routingMode;
249
+ if (requestedRoutingMode !== void 0) {
250
+ if (requestedRoutingMode !== "primary-only") throw new RangeError(`unknown routingMode: ${requestedRoutingMode}`);
251
+ routingMode = "PRIMARY_ONLY";
252
+ }
253
+ return asDurableObjectStub(new DurableObject(durableObjectId, this.#channel.getGlobalActor({
254
+ id: inner,
255
+ locationHint: options?.locationHint,
256
+ mode,
257
+ enableReplicaRouting: ENABLE_REPLICA_ROUTING,
258
+ routingMode,
259
+ version: actorVersionOf(options?.version)
260
+ })));
261
+ }
262
+ };
263
+ /**
264
+ * ← `version = ActorVersion{.cohort = kj::mv(v.cohort)}` (`actor.c++:186-190`),
265
+ * behind `FeatureFlags::get(js).getEnableVersionApi()` which this file reads as
266
+ * on. A version with no cohort is still a version, which is why the empty object
267
+ * is not collapsed to `undefined`.
268
+ */
269
+ function actorVersionOf(version) {
270
+ if (version === void 0) return void 0;
271
+ return version.cohort === void 0 ? {} : { cohort: version.cohort };
272
+ }
273
+ /**
274
+ * ← `DurableObjectClass` (`actor.h:367-393`). "DurableObjectClass represents a
275
+ * binding to a Durable Object class that can be used as a facet. The only use of
276
+ * this type is to pass to `ctx.facets.get()`."
277
+ *
278
+ * `getChannel()` takes no `IoContext` because the parameter existed to resolve the
279
+ * numbered-channel arm, and there is no numbered-channel arm here.
280
+ */
281
+ var DurableObjectClass = class {
282
+ #channel;
283
+ constructor(channel) {
284
+ this.#channel = channel;
285
+ }
286
+ /** ← `DurableObjectClass::getChannel` (`actor.c++:232-242`). */
287
+ getChannel() {
288
+ return this.#channel;
289
+ }
290
+ /**
291
+ * ← `DurableObjectClass::serialize` (`actor.c++:244-306`). Substrate boundary.
292
+ *
293
+ * `requireAllowsTransfer()` runs first, exactly as upstream's does, so a class
294
+ * that refuses transfer reports that rather than the boundary — the refusal is
295
+ * the more specific answer and it is the one upstream would give too.
296
+ */
297
+ serialize() {
298
+ this.#channel.requireAllowsTransfer();
299
+ throw new Error(ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE);
300
+ }
301
+ /** ← `DurableObjectClass::deserialize` (`actor.c++:308-359`). Substrate boundary. */
302
+ static deserialize() {
303
+ throw new Error(ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE);
304
+ }
305
+ };
306
+ //#endregion
307
+ //#region src/api/export-loopback.ts
308
+ /**
309
+ * ← what JSG's struct unwrapper does with a value that is not an object
310
+ * (`jsg/struct.h:246`). Undefined and null are **not** in that set: a struct
311
+ * whose every field is optional — which both option structs here are — unwraps
312
+ * from either as an empty struct (`jsg/struct.h:236-243`), so `ctx.exports.Foo()`
313
+ * is upstream's own empty-options call and not an error.
314
+ */
315
+ var LOOPBACK_OPTIONS_NOT_AN_OBJECT_MESSAGE = "A ctx.exports binding is invoked with an options object: pass { props }, or nothing at all.";
316
+ /** ← what JSG does unwrapping a `jsg::JsRef<jsg::JsObject>` from a non-object. */
317
+ var LOOPBACK_PROPS_NOT_AN_OBJECT_MESSAGE = "`props` must be an object. Upstream unwraps it as a jsg::JsObject, which refuses anything else.";
318
+ /**
319
+ * ← `LoopbackServiceStub` (`export-loopback.h:18-109`).
320
+ *
321
+ * Upstream is a `Fetcher` on the loopback channel and holds the channel number a
322
+ * second time so `callImpl` can re-specialize it. Here the `Fetcher` is the
323
+ * transport's — `api/http.{h,c++}` is not ported — so the unspecialized stub is
324
+ * what the factory returns for a request with no props and no version, and the
325
+ * factory is the thing held twice over.
326
+ */
327
+ var LoopbackServiceStub = class {
328
+ #channel;
329
+ #fetcher;
330
+ constructor(channel) {
331
+ this.#channel = channel;
332
+ this.#fetcher = channel.getSubrequestChannel({
333
+ props: void 0,
334
+ version: void 0
335
+ });
336
+ }
337
+ /** The `Fetcher` upstream inherits from rather than holds, as `DurableObject`'s is. */
338
+ getFetcher() {
339
+ return this.#fetcher;
340
+ }
341
+ /**
342
+ * ← `LoopbackServiceStub::callImpl` (`export-loopback.c++:11-29`) reached
343
+ * through `callWithVersion` (`export-loopback.h:53-55`), which is the callable
344
+ * when `enableVersionApi` is on. "Create a specialized Fetcher which can be
345
+ * passed over RPC."
346
+ */
347
+ callWithVersion(options) {
348
+ return this.#channel.getSubrequestChannel({
349
+ props: requireProps(options.props),
350
+ version: versionRequestOf(options.version)
351
+ });
352
+ }
353
+ };
354
+ /**
355
+ * ← `LoopbackDurableObjectClass` (`export-loopback.h:116-148`). "Similar to
356
+ * LoopbackServiceStub, but for actor classes … this is used for actor classes
357
+ * that do *not* have any storage configured. If you simply export a class
358
+ * extending `DurableObject` but you don't configure storage for it, it shows up
359
+ * in `ctx.exports` as this type. This can be used to create a Durable Object
360
+ * facet."
361
+ *
362
+ * Upstream's base `DurableObjectClass` holds the channel *number*, and
363
+ * `getChannel(ioctx)` resolves it lazily. There is no numbered arm here, so the
364
+ * unspecialized channel is requested once, in the constructor — which is the same
365
+ * value `getActorClass(channel)` with default props would have produced.
366
+ */
367
+ var LoopbackDurableObjectClass = class extends DurableObjectClass {
368
+ #channel;
369
+ constructor(channel) {
370
+ super(channel.getActorClass({ props: void 0 }));
371
+ this.#channel = channel;
372
+ }
373
+ /**
374
+ * ← `LoopbackDurableObjectClass::call` (`export-loopback.c++:31-40`). "Create a
375
+ * specialized DurableObjectClass which can be passed over RPC."
376
+ *
377
+ * The result is a plain `DurableObjectClass`, as `js.alloc<DurableObjectClass>`
378
+ * is: specializing a loopback class does not produce another loopback class.
379
+ */
380
+ call(options) {
381
+ return new DurableObjectClass(this.#channel.getActorClass({ props: requireProps(options.props) }));
382
+ }
383
+ };
384
+ function asLoopbackDurableObjectClass(actorClass) {
385
+ return asCallable({
386
+ properties: actorClass,
387
+ prototype: Object.getPrototypeOf(actorClass),
388
+ call: (options) => actorClass.call(requireOptions(options))
389
+ });
390
+ }
391
+ /**
392
+ * ← `LoopbackDurableObjectNamespace` (`export-loopback.h:155-189`).
393
+ *
394
+ * Upstream: "used when the class has storage configured. In this case, we want a
395
+ * binding that behaves *both* like a LoopbackDurableObjectClass *and* like a
396
+ * DurableObjectNamespace binding. Easy enough, we'll inherit
397
+ * DurableObjectNamespace, but also make the binding invokable as a function like
398
+ * LoopbackDurableObjectClass."
399
+ */
400
+ var LoopbackDurableObjectNamespace = class extends DurableObjectNamespace {
401
+ #loopbackClass;
402
+ constructor(channel, idFactory, loopbackClass) {
403
+ super(channel, idFactory);
404
+ this.#loopbackClass = loopbackClass;
405
+ }
406
+ /** ← `getClass()`. "getClass() accessor for use from C++ only." */
407
+ getClass() {
408
+ return this.#loopbackClass;
409
+ }
410
+ /** ← `call`. "Invoking the binding creates a specialization of the class -- not the namespace." */
411
+ call(options) {
412
+ return this.#loopbackClass.call(options);
413
+ }
414
+ };
415
+ /**
416
+ * ← `LoopbackColoLocalActorNamespace` (`export-loopback.h:192-220`). "Like
417
+ * LoopbackDurableObjectNamespace, but for colo-local (ephemeral) actor
418
+ * namespaces."
419
+ */
420
+ var LoopbackColoLocalActorNamespace = class extends ColoLocalActorNamespace {
421
+ #loopbackClass;
422
+ constructor(channel, loopbackClass) {
423
+ super(channel);
424
+ this.#loopbackClass = loopbackClass;
425
+ }
426
+ /** ← `getClass()`. "getClass() accessor for use from C++ only." */
427
+ getClass() {
428
+ return this.#loopbackClass;
429
+ }
430
+ /** ← `call`. "Invoking the binding creates a specialization of the class -- not the namespace." */
431
+ call(options) {
432
+ return this.#loopbackClass.call(options);
433
+ }
434
+ };
435
+ /**
436
+ * ← `JSG_CALLABLE`, which makes a JSG resource object invocable while leaving
437
+ * every other property answering from the resource type.
438
+ *
439
+ * `properties` is where reads land, with `this` bound to it so a method reaching
440
+ * a private field still finds one — the same binding, memoised the same way,
441
+ * that `asDurableObjectStub` performs for `JSG_INHERIT`. `prototype` is what
442
+ * `instanceof` sees, and it is separate from `properties` because
443
+ * `LoopbackServiceStub` is one object with two halves: upstream's identity is the
444
+ * resource type while its behaviour is the inherited `Fetcher`'s.
445
+ *
446
+ * The target is an arrow function rather than a plain one because a plain
447
+ * function has a non-configurable own `prototype` property, which a Proxy may not
448
+ * hide from `ownKeys`.
449
+ *
450
+ * This is the one assertion the four producers above need, made once here. It is
451
+ * `asDurableObjectStub`'s, for `asDurableObjectStub`'s reason: the declared value
452
+ * is a `Fetcher<T>` or a `DurableObjectClass<T>` intersected with a call
453
+ * signature, and `T` is the caller's claim about a class it named, which no value
454
+ * can confirm. Upstream states the same shapes the same way, in a
455
+ * `JSG_TS_OVERRIDE` that no C++ value is checked against either.
456
+ */
457
+ var INVOCATION_METHODS = /* @__PURE__ */ new Set([
458
+ "call",
459
+ "apply",
460
+ "bind"
461
+ ]);
462
+ function asCallable(facade) {
463
+ const bound = /* @__PURE__ */ new Map();
464
+ const target = () => {
465
+ throw new Error("unreachable: the apply trap answers every invocation");
466
+ };
467
+ return new Proxy(target, {
468
+ apply(_target, _thisArg, args) {
469
+ return facade.call(args[0]);
470
+ },
471
+ get(target, property, receiver) {
472
+ if (INVOCATION_METHODS.has(property)) return Reflect.get(target, property, receiver);
473
+ const cached = bound.get(property);
474
+ if (cached !== void 0) return cached;
475
+ const value = Reflect.get(facade.properties, property, facade.properties);
476
+ if (typeof value !== "function") return value;
477
+ const method = value.bind(facade.properties);
478
+ bound.set(property, method);
479
+ return method;
480
+ },
481
+ has(_target, property) {
482
+ return Reflect.has(facade.properties, property);
483
+ },
484
+ ownKeys() {
485
+ return Reflect.ownKeys(facade.properties);
486
+ },
487
+ getOwnPropertyDescriptor(_target, property) {
488
+ const descriptor = Reflect.getOwnPropertyDescriptor(facade.properties, property);
489
+ if (descriptor === void 0) return void 0;
490
+ return {
491
+ ...descriptor,
492
+ configurable: true
493
+ };
494
+ },
495
+ getPrototypeOf() {
496
+ return facade.prototype;
497
+ }
498
+ });
499
+ }
500
+ /**
501
+ * ← JSG's struct unwrapper (`jsg/struct.h:236-246`), for the two option structs
502
+ * here: every field of both is optional, so undefined and null yield an empty
503
+ * struct and anything that is not an object is a `TypeError`. V8's `IsObject()`
504
+ * is true for functions, which is why one is not refused here either.
505
+ *
506
+ * `cohort` is not checked, because upstream does not check it: JSG's `kj::String`
507
+ * unwrapper calls `ToString` on whatever it is given (`jsg/value.h:501-506`), so
508
+ * refusing a non-string here would refuse what workerd coerces. That is the same
509
+ * reading `api/actor.ts`'s `actorVersionOf` already takes of the same field.
510
+ */
511
+ function requireOptions(options) {
512
+ if (options !== void 0 && options !== null && typeof options !== "object" && typeof options !== "function") throw new TypeError(LOOPBACK_OPTIONS_NOT_AN_OBJECT_MESSAGE);
513
+ return options ?? {};
514
+ }
515
+ /** ← `jsg::Optional<jsg::JsRef<jsg::JsObject>> props` — present means an object. */
516
+ function requireProps(props) {
517
+ if (props === void 0) return void 0;
518
+ if (props === null || typeof props !== "object" && typeof props !== "function") throw new TypeError(LOOPBACK_PROPS_NOT_AN_OBJECT_MESSAGE);
519
+ return props;
520
+ }
521
+ /** ← `.cohort = kj::mv(version.cohort).orDefault(kj::none)` (`export-loopback.c++:19-23`). */
522
+ function versionRequestOf(version) {
523
+ if (version === void 0) return void 0;
524
+ return { cohort: version.cohort ?? void 0 };
525
+ }
526
+ //#endregion
527
+ //#region src/api/sql.ts
528
+ /**
529
+ * ← workerd `src/workerd/api/sql.{h,c++}`
530
+ *
531
+ * `SqlStorage` and its two nested types. ~330 call sites depend on this — it is
532
+ * the real storage layer, not KV.
533
+ *
534
+ * Four things about the translation, in descending order of how much they cost:
535
+ *
536
+ * 1. **The cursor is materialised, not live.** Upstream's `Cursor` owns a
537
+ * running `SqliteDatabase::Query` and pulls one row at a time; the backend
538
+ * seam this package chose (`SqlDatabase.exec` → `SqlResult`) has already
539
+ * collected every row before a cursor exists. Everything downstream of that
540
+ * follows: there is no statement cache, so `CachedStatement`, the 1 MiB LRU
541
+ * and `reusedCachedQueryForTest` are absent with it; there is no live
542
+ * statement to cancel, so `Cursor::canceled` and `selfRef` — both already
543
+ * dead upstream, written but never assigned — have nothing to guard; and
544
+ * `endQuery`'s job of returning a statement to the cache is nothing here, so
545
+ * the counters it saves off are simply the counters. What is kept is every
546
+ * observable: the position is shared across `next`/`toArray`/`one`/`raw`,
547
+ * and a drained cursor keeps yielding done.
548
+ * 2. **`Cursor` and `Statement` must be constructible with no arguments**, or
549
+ * `SqlStorage` cannot satisfy workers-types without a cast: the interface
550
+ * types them `typeof SqlStorageCursor` / `typeof SqlStorageStatement`, and
551
+ * both are `abstract` there, so their construct signatures take none.
552
+ * Upstream's are unconstructible from JS for the same reason they are
553
+ * `abstract` in the types — JSG nested types have no JS constructor — so the
554
+ * faithful shape is a constructor that refuses. `sql.Cursor` exists for
555
+ * `instanceof`, which is all upstream exposes it for.
556
+ * 3. **The regulator is ported whole, and that is not the whole authorizer.**
557
+ * Four of its five members are here as callbacks, and none needed the
558
+ * authorizer to compute anything — `isAllowedName` is a prefix test,
559
+ * `isAllowedTrigger` is `return true`, `allowTransactions` throws,
560
+ * `shouldAddQueryStats` is a constant. The fifth, `onError`, is not a
561
+ * callback at all in this port: it is every `throw new Error(message)`
562
+ * below, which is all its upstream body does with a refusal message.
563
+ * For those, what the authorizer supplied was the *identifiers*:
564
+ * with none, the statement text is the only source, so `exec` tokenizes it
565
+ * and runs `isAllowedName` over every identifier-shaped token. That is
566
+ * deliberately STRICTER than upstream — see `SQL_RESERVED_PREFIX_MESSAGE`.
567
+ *
568
+ * But the authorizer also makes decisions no callback ever sees, and those
569
+ * do NOT arrive with the regulator: `SQLITE_ATTACH` / `SQLITE_DETACH`, the
570
+ * `SQLITE_CREATE_TEMP_*` family and the `temp` schema, `SQLITE_PRAGMA`, and
571
+ * the `SQLITE_CREATE_VTABLE` module list. Each is refused from the text in
572
+ * `refuseUnauthorizedForms` and `requireAllowedPragmas`. `SQLITE_FUNCTION`
573
+ * is the one still unported — see the README divergence row.
574
+ * 4. **`ingest` stays at upstream's SQLite seam.** `SqliteDatabase.ingest()`
575
+ * executes every complete statement and returns the partial tail, using the
576
+ * same compiled boundaries and regulator as `exec`.
577
+ *
578
+ * Spec: §1.4, §2.4 in docs/decisions.md.
579
+ */
580
+ /**
581
+ * ← `SqlStorageRegulator::allowTransactions()`, copied verbatim. Users match on
582
+ * it and it is the one regulator callback our substrate can still answer.
583
+ */
584
+ 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.";
585
+ /** See translation 2 in the header: the class is exposed for `instanceof` only. */
586
+ var CURSOR_NOT_CONSTRUCTIBLE_MESSAGE = "Illegal invocation: SqlStorage.Cursor cannot be constructed directly. Use sql.exec().";
587
+ /** Same, for the prepared-statement compatibility shim. */
588
+ var STATEMENT_NOT_CONSTRUCTIBLE_MESSAGE = "Illegal invocation: SqlStorage.Statement cannot be constructed directly. Use sql.prepare().";
589
+ /**
590
+ * ← SQLite's own denial text, with the reason appended.
591
+ *
592
+ * There is no upstream string to copy here: `SqlStorageRegulator::onError` just
593
+ * rethrows whatever SQLite produced, and SQLite produces `not authorized` for an
594
+ * authorizer denial (`access to X.Y is prohibited` for the column-read case,
595
+ * which needs a resolved identifier we do not have). The prefix is kept so that
596
+ * anything matching upstream still matches; the rest is here because a bare
597
+ * `not authorized` is not debuggable.
598
+ */
599
+ 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.";
600
+ /**
601
+ * ← the five transaction-control forms `sqlite3_stmt_readonly()` reports
602
+ * read-only and the authorizer reports as `SQLITE_TRANSACTION` /
603
+ * `SQLITE_SAVEPOINT`. The same set `util/sqlite.ts` classifies, read here from
604
+ * the leading keyword because the untrusted path has to refuse them before the
605
+ * trusted one applies them.
606
+ *
607
+ * `;` counts as leading trivia here and in every other leading-keyword refusal
608
+ * below. A statement boundary comes from the backend, and `node:sqlite` reports
609
+ * an empty leading statement as part of the span it compiled: the `sourceSQL`
610
+ * for `;ATTACH ...` is the whole string, so an anchor of `^\s*` would read the
611
+ * keyword as `;` and let the compiled ATTACH through. The browser backend cuts
612
+ * the same input at the first `;` and refuses the empty statement instead.
613
+ */
614
+ var TRANSACTION_CONTROL = /^[\s;]*(?:BEGIN|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i;
615
+ /** Cheap pre-test, so the tokenizer below runs only on a statement that could fail it. */
616
+ var RESERVED_PREFIX_HINT = /_cf_/i;
617
+ /** A SQL identifier. Double-quoted and bracketed forms are still identifiers, so only the
618
+ * delimiters are stripped and the word inside is scanned like any other. */
619
+ var IDENTIFIER = /[A-Za-z_][A-Za-z0-9_$]*/g;
620
+ /**
621
+ * Everything in a statement an identifier cannot come from: single-quoted string
622
+ * literals (SQLite escapes an embedded quote by doubling it), `--` line comments,
623
+ * and `/* *\/` block comments. Replaced with a space before tokenizing, so what is
624
+ * left is code.
625
+ *
626
+ * Backtick-quoted names are NOT here: MySQL-compatible quoting produces an
627
+ * identifier, exactly as the double-quoted form does.
628
+ */
629
+ var NOT_CODE = /'(?:[^']|'')*'|--[^\n]*|\/\*[\s\S]*?\*\//g;
630
+ /**
631
+ * Comments are never code. String literals STAY, for both of this regex's callers: pragma
632
+ * arguments may be quoted, and SQLite's misquoting feature reads a single-quoted string as an
633
+ * identifier — `CREATE TABLE 'temp'.t(x)` really creates a temp-schema table — so the checks
634
+ * that read identifier positions must still see it. `NOT_CODE` blanks literals, which is right
635
+ * for the token scans and would be a bypass for these callers.
636
+ */
637
+ var NOT_COMMENT = /--[^\n]*|\/\*[\s\S]*?\*\//g;
638
+ /**
639
+ * ← `SqlStorageRegulator` (`sql.h:15-22`, `sql.c++:141-165`), whole.
640
+ *
641
+ * Upstream reaches these through the SQLite authorizer while a statement is
642
+ * being compiled. `exec` calls them from the statement text instead, which is
643
+ * the same translation Section 3 made for the write classifier and for
644
+ * transaction state.
645
+ */
646
+ var SqlStorageRegulator = {
647
+ /**
648
+ * Upstream's body is `return !name.startsWith("_cf_")`, with an autogate that
649
+ * makes the comparison case-insensitive and logs a warning until it lands. The
650
+ * case-insensitive form is taken here: it is the direction upstream is moving,
651
+ * and there is no logger for the warning half.
652
+ */
653
+ isAllowedName(name) {
654
+ return name.length < 4 || name.slice(0, 4).toLowerCase() !== "_cf_";
655
+ },
656
+ /** Upstream's body is `return true`. */
657
+ isAllowedTrigger(_name) {
658
+ return true;
659
+ },
660
+ /** Upstream's body is a `JSG_FAIL_REQUIRE` with this message. */
661
+ allowTransactions() {
662
+ throw new Error(SQL_TRANSACTION_REFUSED_MESSAGE);
663
+ },
664
+ /** "Bill for queries executed from JavaScript." Nothing reads it — `SqliteObserver` has no port. */
665
+ shouldAddQueryStats() {
666
+ return true;
667
+ }
668
+ };
669
+ /**
670
+ * The text-level stand-in for the authorizer's `isAllowedName` calls — see
671
+ * `SQL_RESERVED_PREFIX_MESSAGE` and the README row.
672
+ *
673
+ * Upstream refuses a *resolved identifier* that starts with `_cf_`, because it
674
+ * reaches `isAllowedName` through the SQLite authorizer while the statement is
675
+ * being compiled. There is no authorizer here, so this tokenizes the statement
676
+ * text instead, over everything that is not a string literal or a comment —
677
+ * which is the same set of characters an identifier can come from.
678
+ *
679
+ * **The literals were once refused too, and that was wrong.** The first draft
680
+ * scanned the whole statement on the reasoning that no legitimate consumer
681
+ * statement contains the token, so being stricter than upstream was the safe
682
+ * direction. A retained conformance case uses `_cf_keepAliveHeartbeat` as a
683
+ * bound value: real workerd accepts it, so this parser must distinguish data
684
+ * from identifiers. `conformance/suite/sql.spec.ts` pins the rule: refused as a
685
+ * table name and as a quoted identifier, allowed as data.
686
+ *
687
+ * A name that merely CONTAINS the token — `my_cf_thing` — stays allowed, because
688
+ * `isAllowedName` tests a prefix.
689
+ */
690
+ function requireAllowedNames(query) {
691
+ if (!RESERVED_PREFIX_HINT.test(query)) return;
692
+ const code = query.replace(NOT_CODE, " ");
693
+ for (const [token] of code.matchAll(IDENTIFIER)) if (!SqlStorageRegulator.isAllowedName(token)) throw new Error(SQL_RESERVED_PREFIX_MESSAGE);
694
+ }
695
+ /** Refuse transaction control against one SQLite-decided statement boundary. */
696
+ function refuseTransactionControl(statement) {
697
+ const code = statement.replace(NOT_CODE, " ");
698
+ if (TRANSACTION_CONTROL.test(code)) SqlStorageRegulator.allowTransactions();
699
+ }
700
+ /**
701
+ * ← the message a `SQLITE_DENY` from the authorizer surfaces to JavaScript,
702
+ * byte-identical so a caller matching on it ports unchanged.
703
+ */
704
+ var SQL_NOT_AUTHORIZED_MESSAGE = "not authorized: SQLITE_AUTH";
705
+ /**
706
+ * The forms neither the regulator nor any callback sees: the authorizer's own
707
+ * action codes, plus `VACUUM`, which SQLite itself refuses by precondition.
708
+ *
709
+ * Porting `SqlStorageRegulator` whole (point 3 in the file header) carried over its members, but
710
+ * not the authorizer's own action codes: `SQLITE_ATTACH`, `SQLITE_DETACH`, `SQLITE_CREATE_TEMP_*`
711
+ * and `SQLITE_CREATE_VTABLE` consult no callback, so nothing here refused them. Every form below
712
+ * was measured on real workerd through the conformance oracle, not inferred.
713
+ *
714
+ * `ATTACH` and `DETACH` are also the isolation boundary rather than a fidelity detail: both
715
+ * backends open a real file, so on `node:sqlite` an `ATTACH` reads another actor's database and
716
+ * a `VACUUM INTO` writes anywhere the process can. The reserved-name scan does not cover it,
717
+ * because that scan tokenizes the SUBMITTED statement — `other._cf_KV` is caught, and every
718
+ * application table in the same attached database is not.
719
+ */
720
+ var DATABASE_ATTACHMENT = /^[\s;]*(?:ATTACH|DETACH)\b/i;
721
+ /**
722
+ * ← the `SQLITE_CREATE_TEMP_*` denials (`sqlite.c++:1323`) and the `dbName == temp` rule
723
+ * (`sqlite.c++:1073`), which permits a temp-schema database name only `READ` and `UPDATE`.
724
+ * Upstream's own reason to deny them applies here unchanged: a temporary table makes SQLite
725
+ * open a separate temporary file that the storage engine knows nothing about.
726
+ *
727
+ * Two spellings, one refusal each by its own upstream path: `CREATE TEMP TABLE t(x)` is the
728
+ * keyword and hits the action codes; `CREATE TABLE temp.t(x)` is the schema qualifier and hits
729
+ * the `dbName` rule — and the qualifier was the live gap, where the table was created, written
730
+ * and read back here while workerd refused it outright. The qualifier accepts every quoting
731
+ * SQLite does, single quotes included, with or without whitespace after the keyword (measured:
732
+ * workerd refuses `CREATE TABLE 'temp'.t(x)` and `CREATE TABLE"temp".t(x)` the same way), and
733
+ * is matched only in the object-name position, so an application table merely NAMED `tempest`
734
+ * or `temporary_log` is untouched. The keyword spelling needs no qualifier arm of its own here:
735
+ * `TEMP_SCHEMA` already refuses every `CREATE TEMP…` before this pattern is consulted.
736
+ *
737
+ * The rest of the `dbName` rule goes unported on purpose: with no way to create a temp-schema
738
+ * object, `INSERT`/`DELETE`/`DROP` against one die in SQLite as `no such table`, and the one
739
+ * silent form measures identically — workerd allows `DROP TABLE IF EXISTS temp.ghost` too.
740
+ */
741
+ var TEMP_SCHEMA = /^[\s;]*CREATE\s+(?:TEMP|TEMPORARY)\b/i;
742
+ 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;
743
+ /**
744
+ * ← `SQLITE_CREATE_VTABLE` (`sqlite.c++:1298-1316`): a virtual table is native-code callbacks, so
745
+ * upstream allows exactly four modules — FTS5 and its `fts5vocab` companion, R*Tree and its
746
+ * `rtree_i32` variant — and denies every other module SQLite was compiled with.
747
+ *
748
+ * `dbstat` is why this is not only fidelity: it reports a row per table with page counts and
749
+ * byte sizes, so `SELECT name FROM d` enumerates `_cf_KV` and the rest of the runtime's own
750
+ * tables without the statement ever naming them — around `requireAllowedNames`, which can only
751
+ * see the text it was given.
752
+ *
753
+ * The table name and the module accept every quoting SQLite does — double quotes, backticks,
754
+ * brackets, and the misquoting feature's single-quoted string, with or without whitespace
755
+ * before them — because the module has to be read from PAST the name, and a guessed name
756
+ * boundary is a bypass in both directions: an unparseable name skipped the check, and
757
+ * `"a USING fts5 b" USING dbstat` read its module out of the quoted name. Measured: workerd
758
+ * refuses both, refuses `CREATE VIRTUAL TABLE"d"USING dbstat`, resolves `USING "dbstat"` to
759
+ * the same denial, and allows `USING 'fts5'`. A `CREATE VIRTUAL TABLE` whose module the
760
+ * pattern cannot read is refused outright — the deliberately stricter direction the
761
+ * unparseable-PRAGMA fallback below already takes.
762
+ */
763
+ var SQL_IDENTIFIER_SOURCE = /"(?:[^"]|"")*"|'(?:[^']|'')*'|`(?:[^`]|``)*`|\[[^\]]*\]|[A-Za-z_\u0080-\uffff][A-Za-z0-9_$\u0080-\uffff]*/.source;
764
+ var VIRTUAL_TABLE = /^[\s;]*CREATE\s+VIRTUAL\s+TABLE\b/i;
765
+ 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");
766
+ var ALLOWED_VIRTUAL_TABLE_MODULES = /* @__PURE__ */ new Set([
767
+ "fts5",
768
+ "fts5vocab",
769
+ "rtree",
770
+ "rtree_i32"
771
+ ]);
772
+ /**
773
+ * `VACUUM` has no action code of its own, so the authorizer never sees it. What refuses it
774
+ * upstream is SQLite's own `cannot VACUUM from within a transaction` precondition, against the
775
+ * transaction a Durable Object always has open — and that message is what the oracle returned,
776
+ * for `VACUUM`, `VACUUM main` and `VACUUM INTO` alike. Byte-identical for the same reason
777
+ * `SQL_NOT_AUTHORIZED_MESSAGE` is: a caller matching on it ports unchanged.
778
+ *
779
+ * Refused here unconditionally rather than by transaction state, which is a divergence only in
780
+ * mechanism: this runtime never runs a statement where upstream would have allowed it.
781
+ */
782
+ var VACUUM_STATEMENT = /^[\s;]*VACUUM\b/i;
783
+ var SQL_VACUUM_REFUSED_MESSAGE = "cannot VACUUM from within a transaction: SQLITE_ERROR";
784
+ /** Refuse the authorizer-only forms against one SQLite-decided statement boundary. */
785
+ function refuseUnauthorizedForms(statement) {
786
+ const code = statement.replace(NOT_COMMENT, " ");
787
+ if (VACUUM_STATEMENT.test(code)) throw new Error(SQL_VACUUM_REFUSED_MESSAGE);
788
+ if (DATABASE_ATTACHMENT.test(code) || TEMP_SCHEMA.test(code) || TEMP_QUALIFIED.test(code)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
789
+ if (VIRTUAL_TABLE.test(code)) {
790
+ const moduleName = VIRTUAL_TABLE_MODULE.exec(code)?.[1];
791
+ if (moduleName === void 0 || !ALLOWED_VIRTUAL_TABLE_MODULES.has(unquoted(moduleName).toLowerCase())) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
792
+ }
793
+ }
794
+ /** Cheap pre-test; `PRAGMA` and the `pragma_` functions both contain it. */
795
+ var PRAGMA_HINT = /pragma/i;
796
+ /** `PRAGMA [schema.]name`, then `= value`, `(argument)`, or nothing. */
797
+ 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;
798
+ /**
799
+ * ← `ALLOWED_PRAGMAS` (`util/sqlite.c++:543-571`) and `PragmaSignature` (`:528-535`),
800
+ * verbatim. `table_list`, `table_info`, and `table_xinfo` are special-cased
801
+ * ahead of the table in the authorizer, exactly as upstream's `SQLITE_PRAGMA`
802
+ * case does (`util/sqlite.c++:1194-1273`).
803
+ */
804
+ var ALLOWED_PRAGMAS = /* @__PURE__ */ new Map([
805
+ ["data_version", "NO_ARG"],
806
+ ["page_size", "NO_ARG"],
807
+ ["case_sensitive_like", "BOOLEAN"],
808
+ ["foreign_keys", "BOOLEAN"],
809
+ ["defer_foreign_keys", "BOOLEAN"],
810
+ ["ignore_check_constraints", "BOOLEAN"],
811
+ ["legacy_alter_table", "BOOLEAN"],
812
+ ["recursive_triggers", "BOOLEAN"],
813
+ ["reverse_unordered_selects", "BOOLEAN"],
814
+ ["foreign_key_check", "OPTIONAL_OBJECT_NAME"],
815
+ ["foreign_key_list", "OBJECT_NAME"],
816
+ ["index_info", "OBJECT_NAME"],
817
+ ["index_list", "OBJECT_NAME"],
818
+ ["index_xinfo", "OBJECT_NAME"],
819
+ ["quick_check", "NULL_NUMBER_OR_OBJECT_NAME"],
820
+ ["optimize", "NULL_OR_NUMBER"]
821
+ ]);
822
+ /** Upstream compares the eight literal forms as PREFIXES, case-insensitively. */
823
+ var BOOLEAN_PRAGMA_VALUE = /^(?:true|false|yes|no|on|off|1|0)/i;
824
+ /**
825
+ * The pragmas SQLite ships (https://www.sqlite.org/pragma.html), so a
826
+ * `pragma_X` identifier can be told apart: `X` here means the table-valued
827
+ * pragma function and follows the allowlist; any other `pragma_`-prefixed
828
+ * identifier is an ordinary application name, which upstream's authorizer
829
+ * distinguishes by resolution and the conformance suite pins.
830
+ */
831
+ var SQLITE_PRAGMA_NAMES = /* @__PURE__ */ new Set([
832
+ "analysis_limit",
833
+ "application_id",
834
+ "auto_vacuum",
835
+ "automatic_index",
836
+ "busy_timeout",
837
+ "cache_size",
838
+ "cache_spill",
839
+ "case_sensitive_like",
840
+ "cell_size_check",
841
+ "checkpoint_fullfsync",
842
+ "collation_list",
843
+ "compile_options",
844
+ "data_version",
845
+ "database_list",
846
+ "defer_foreign_keys",
847
+ "encoding",
848
+ "foreign_key_check",
849
+ "foreign_key_list",
850
+ "foreign_keys",
851
+ "freelist_count",
852
+ "full_column_names",
853
+ "fullfsync",
854
+ "function_list",
855
+ "hard_heap_limit",
856
+ "ignore_check_constraints",
857
+ "incremental_vacuum",
858
+ "index_info",
859
+ "index_list",
860
+ "index_xinfo",
861
+ "integrity_check",
862
+ "journal_mode",
863
+ "journal_size_limit",
864
+ "legacy_alter_table",
865
+ "legacy_file_format",
866
+ "locking_mode",
867
+ "max_page_count",
868
+ "mmap_size",
869
+ "module_list",
870
+ "optimize",
871
+ "page_count",
872
+ "page_size",
873
+ "pragma_list",
874
+ "query_only",
875
+ "quick_check",
876
+ "read_uncommitted",
877
+ "recursive_triggers",
878
+ "reverse_unordered_selects",
879
+ "schema_version",
880
+ "secure_delete",
881
+ "short_column_names",
882
+ "shrink_memory",
883
+ "soft_heap_limit",
884
+ "synchronous",
885
+ "table_info",
886
+ "table_list",
887
+ "table_xinfo",
888
+ "temp_store",
889
+ "threads",
890
+ "trusted_schema",
891
+ "user_version",
892
+ "wal_autocheckpoint",
893
+ "wal_checkpoint",
894
+ "writable_schema"
895
+ ]);
896
+ /** kj's `tryParseAs` is decimal; keep the same acceptance. */
897
+ var DECIMAL = /^[+-]?\d+$/;
898
+ /**
899
+ * One layer of SQL quoting, any of the four forms — a pragma argument, or the module token
900
+ * `VIRTUAL_TABLE_MODULE` captured. Doubled inner quotes stay doubled, which cannot change a
901
+ * verdict here: no allowlisted comparison target contains a quote character.
902
+ */
903
+ function unquoted(argument) {
904
+ const first = argument[0];
905
+ const last = argument[argument.length - 1];
906
+ if (argument.length >= 2) {
907
+ if ((first === "'" || first === "\"" || first === "`") && last === first) return argument.slice(1, -1);
908
+ if (first === "[" && last === "]") return argument.slice(1, -1);
909
+ }
910
+ return argument;
911
+ }
912
+ /** ← the `SQLITE_PRAGMA` authorizer case (`util/sqlite.c++:1194-1273`), whole. */
913
+ function isAllowedPragma(name, argument) {
914
+ const pragma = name.toLowerCase();
915
+ if (pragma === "table_list") return true;
916
+ if (pragma === "table_info" || pragma === "table_xinfo") {
917
+ if (argument === void 0) return false;
918
+ return SqlStorageRegulator.isAllowedName(unquoted(argument));
919
+ }
920
+ const signature = ALLOWED_PRAGMAS.get(pragma);
921
+ if (signature === void 0) return false;
922
+ switch (signature) {
923
+ case "NO_ARG": return argument === void 0;
924
+ case "BOOLEAN": return argument === void 0 || BOOLEAN_PRAGMA_VALUE.test(unquoted(argument));
925
+ case "OBJECT_NAME": return argument !== void 0 && SqlStorageRegulator.isAllowedName(unquoted(argument));
926
+ case "OPTIONAL_OBJECT_NAME": return argument === void 0 || SqlStorageRegulator.isAllowedName(unquoted(argument));
927
+ case "NULL_OR_NUMBER": return argument === void 0 || DECIMAL.test(argument);
928
+ case "NULL_NUMBER_OR_OBJECT_NAME": return argument === void 0 || DECIMAL.test(argument) || SqlStorageRegulator.isAllowedName(unquoted(argument));
929
+ }
930
+ }
931
+ /**
932
+ * The text-level stand-in for the authorizer's `SQLITE_PRAGMA` case, against
933
+ * one SQLite-decided statement boundary. Load-bearing beyond fidelity:
934
+ * `user_version` is where runtime storage versioning keeps its per-file stamp
935
+ * (`util/sqlite-migrations.ts`), and `writable_schema` would let application
936
+ * SQL rewrite `sqlite_master` out from under the `_cf_` reservation.
937
+ *
938
+ * The `pragma_` table-valued functions reach the same authorizer path
939
+ * upstream, so they follow the same allowlist here — by pragma NAME only. An
940
+ * argument the text cannot see (a string literal or a binding) goes unchecked
941
+ * where upstream's authorizer sees the resolved value; a `_cf_` name smuggled
942
+ * that way reads schema whose shape is public source anyway, while identifier
943
+ * arguments stay covered by `requireAllowedNames`. The README divergence row
944
+ * records this.
945
+ */
946
+ function requireAllowedPragmas(statement) {
947
+ if (!PRAGMA_HINT.test(statement)) return;
948
+ const code = statement.replace(NOT_COMMENT, " ");
949
+ const direct = code.match(PRAGMA_STATEMENT);
950
+ if (direct !== null) {
951
+ const [, name = "", assigned, called] = direct;
952
+ const argument = (assigned ?? called)?.trim();
953
+ if (!isAllowedPragma(name, argument === "" ? void 0 : argument)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
954
+ return;
955
+ }
956
+ if (/^[\s;]*PRAGMA\b/i.test(code)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
957
+ const literalFree = code.replace(NOT_CODE, " ");
958
+ for (const [token] of literalFree.matchAll(IDENTIFIER)) {
959
+ if (token.length <= 7 || token.slice(0, 7).toLowerCase() !== "pragma_") continue;
960
+ const name = token.slice(7).toLowerCase();
961
+ if (!SQLITE_PRAGMA_NAMES.has(name)) continue;
962
+ if (name !== "table_list" && name !== "table_info" && name !== "table_xinfo" && !ALLOWED_PRAGMAS.has(name)) throw new Error(SQL_NOT_AUTHORIZED_MESSAGE);
963
+ }
964
+ }
965
+ /** Everything the untrusted path refuses at one statement boundary. */
966
+ function regulateUntrustedStatement(statement) {
967
+ refuseTransactionControl(statement);
968
+ refuseUnauthorizedForms(statement);
969
+ requireAllowedPragmas(statement);
970
+ }
971
+ /** ← `JSG_INHERIT_INTRINSIC(v8::kIteratorPrototype)` (`jsg/iterator.h:1044`). */
972
+ var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
973
+ /**
974
+ * ← the `JSG_ITERATOR` types (`jsg/iterator.h:1036-1050`): `next` and
975
+ * self-iterability on `%IteratorPrototype%` — which is what carries the ES
976
+ * iterator helpers; `raw().toArray()` is what Drizzle's durable-sqlite driver
977
+ * calls — and NOTHING else. No `return`, no `throw` (only the async variant
978
+ * registers `return_`, `:1069-1085`), so `IteratorClose` after a `break`, a
979
+ * partial destructuring, or a `take()` is a no-op and a retained iterator
980
+ * resumes. Results are `JSG_STRUCT(done, value)` in that key order
981
+ * (`jsg/iterator.h:706-710`).
982
+ */
983
+ var RawIterator = class {
984
+ #pull;
985
+ constructor(pull) {
986
+ this.#pull = pull;
987
+ }
988
+ next() {
989
+ const raw = this.#pull();
990
+ if (raw === void 0) return {
991
+ done: true,
992
+ value: void 0
993
+ };
994
+ return {
995
+ done: false,
996
+ value: asRawRow([...raw])
997
+ };
998
+ }
999
+ [Symbol.iterator]() {
1000
+ return this;
1001
+ }
1002
+ };
1003
+ Object.setPrototypeOf(RawIterator.prototype, IteratorPrototype);
1004
+ Object.defineProperty(RawIterator.prototype, Symbol.toStringTag, {
1005
+ value: "RawIterator",
1006
+ configurable: true
1007
+ });
1008
+ /** ← `RowIterator`, shaped exactly as `RawIterator` above. */
1009
+ var RowIterator = class {
1010
+ #pull;
1011
+ constructor(pull) {
1012
+ this.#pull = pull;
1013
+ }
1014
+ next() {
1015
+ const row = this.#pull();
1016
+ if (row === void 0) return {
1017
+ done: true,
1018
+ value: void 0
1019
+ };
1020
+ return {
1021
+ done: false,
1022
+ value: row
1023
+ };
1024
+ }
1025
+ [Symbol.iterator]() {
1026
+ return this;
1027
+ }
1028
+ };
1029
+ Object.setPrototypeOf(RowIterator.prototype, IteratorPrototype);
1030
+ Object.defineProperty(RowIterator.prototype, Symbol.toStringTag, {
1031
+ value: "RowIterator",
1032
+ configurable: true
1033
+ });
1034
+ /**
1035
+ * ← `SqlStorage::Cursor`.
1036
+ *
1037
+ * `rowsRead` is the one counter that is not upstream's. Upstream reads
1038
+ * `Query::getRowsRead()`, a billing counter sourced from libsql's
1039
+ * `STMTSTATUS_ROWS_READ` that counts index rows and that neither backend
1040
+ * exposes — the same absence the README already records for `SqlResult`. The
1041
+ * interface requires a number, so this returns the rows the cursor has yielded,
1042
+ * which is what today's browser host returns and what its tests assert. It
1043
+ * undercounts any query that scans more rows than it returns.
1044
+ */
1045
+ var Cursor = class {
1046
+ #columnNames;
1047
+ #rawRows;
1048
+ #rowsWritten;
1049
+ #position = 0;
1050
+ constructor(state) {
1051
+ if (state === void 0) throw new Error(CURSOR_NOT_CONSTRUCTIBLE_MESSAGE);
1052
+ this.#columnNames = state.columnNames;
1053
+ this.#rawRows = state.rawRows;
1054
+ this.#rowsWritten = state.rowsWritten;
1055
+ }
1056
+ /**
1057
+ * ← `JSG_READONLY_PROTOTYPE_PROPERTY(columnNames)` (`sql.h:210`): a
1058
+ * prototype accessor, not an own field, so a cursor JSON-stringifies to `{}`.
1059
+ */
1060
+ get columnNames() {
1061
+ return this.#columnNames;
1062
+ }
1063
+ /** ← `Cursor::next`, whose `RowIterator::Next` is this exact shape. */
1064
+ next() {
1065
+ const row = this.#nextRow();
1066
+ if (row === void 0) return {
1067
+ done: true,
1068
+ value: void 0
1069
+ };
1070
+ return {
1071
+ done: false,
1072
+ value: row
1073
+ };
1074
+ }
1075
+ /** ← `Cursor::toArray`, which drains from the current position. */
1076
+ toArray() {
1077
+ const rows = [];
1078
+ for (;;) {
1079
+ const row = this.#nextRow();
1080
+ if (row === void 0) return rows;
1081
+ rows.push(row);
1082
+ }
1083
+ }
1084
+ /** ← `Cursor::one`. Both messages are upstream's, verbatim. */
1085
+ one() {
1086
+ const row = this.#nextRow();
1087
+ if (row === void 0) throw new Error("Expected exactly one result from SQL query, but got no results.");
1088
+ if (this.#position < this.#rawRows.length) {
1089
+ this.#position = this.#rawRows.length;
1090
+ throw new Error("Expected exactly one result from SQL query, but got multiple results.");
1091
+ }
1092
+ return row;
1093
+ }
1094
+ /**
1095
+ * ← `Cursor::raw`, which shares this cursor's position rather than
1096
+ * restarting. The iterator's shape is `RawIterator`'s whole doc comment.
1097
+ */
1098
+ raw() {
1099
+ return new RawIterator(() => this.#nextRaw());
1100
+ }
1101
+ /** ← `JSG_ITERABLE(rows)`, yielding through the same shared position. */
1102
+ [Symbol.iterator]() {
1103
+ return new RowIterator(() => this.#nextRow());
1104
+ }
1105
+ get rowsRead() {
1106
+ return this.#position;
1107
+ }
1108
+ /** ← `Cursor::getRowsWritten`, which is `SqlResult.rowsWritten` here. */
1109
+ get rowsWritten() {
1110
+ return this.#rowsWritten;
1111
+ }
1112
+ #nextRaw() {
1113
+ const raw = this.#rawRows[this.#position];
1114
+ if (raw === void 0) return void 0;
1115
+ this.#position += 1;
1116
+ return raw;
1117
+ }
1118
+ /** ← `Cursor::rowIteratorNext`: zip the column names onto the row. */
1119
+ #nextRow() {
1120
+ const raw = this.#nextRaw();
1121
+ if (raw === void 0) return void 0;
1122
+ const row = {};
1123
+ this.#columnNames.forEach((name, index) => {
1124
+ row[name] = raw[index] ?? null;
1125
+ });
1126
+ return asRow(row);
1127
+ }
1128
+ };
1129
+ /** ← the jsg resource-type tag every workerd API object carries (`resource.h`). */
1130
+ Object.defineProperty(Cursor.prototype, Symbol.toStringTag, {
1131
+ value: "Cursor",
1132
+ configurable: true
1133
+ });
1134
+ /**
1135
+ * ← `SqlStorage::Statement`, which upstream describes as "supported only for
1136
+ * backwards compatibility ... it is actually just a wrapper around `exec()`".
1137
+ * `JSG_CALLABLE(run)` makes the object itself callable, so `prepare()` returns a
1138
+ * function wearing this prototype rather than an object with a `run` method.
1139
+ */
1140
+ var Statement = class {
1141
+ constructor() {
1142
+ throw new Error(STATEMENT_NOT_CONSTRUCTIBLE_MESSAGE);
1143
+ }
1144
+ };
1145
+ var SqlStorage = class {
1146
+ #ctx;
1147
+ #owner;
1148
+ /** ← `kj::Maybe<uint> pageSize`, memoized for the same reason. */
1149
+ #pageSize;
1150
+ constructor(ctx, owner) {
1151
+ this.#ctx = ctx;
1152
+ this.#owner = owner;
1153
+ }
1154
+ /** ← `JSG_NESTED_TYPE(Cursor)`. Exposed so `instanceof` works, as upstream's is. */
1155
+ Cursor = Cursor;
1156
+ /** ← `JSG_NESTED_TYPE(Statement)`. */
1157
+ Statement = Statement;
1158
+ exec(query, ...bindings) {
1159
+ requireInputLock(this.#ctx, "sql.exec()");
1160
+ const db = this.#owner.getSqliteDb();
1161
+ const sqlBindings = bindings.map(toSqlBindingValue);
1162
+ requireAllowedNames(query);
1163
+ const result = db.run({ regulate: regulateUntrustedStatement }, query, ...sqlBindings);
1164
+ return new Cursor({
1165
+ columnNames: [...result.columnNames],
1166
+ rawRows: result.rawRows.map((row) => row.map(toSqlStorageValue)),
1167
+ rowsWritten: result.rowsWritten
1168
+ });
1169
+ }
1170
+ /**
1171
+ * ← `SqlStorage::getDatabaseSize`.
1172
+ *
1173
+ * Upstream's second query is `PRAGMA page_size;`, which `sqlite3_stmt_readonly()`
1174
+ * reports read-only. With no such call the text is the only source and §1.7.1's
1175
+ * rule is write-unless-provably-a-read, so a bare `PRAGMA` would open a
1176
+ * transaction and take an output-gate lock to answer a size question. The
1177
+ * `pragma_page_size` table-valued function is the same value read through the
1178
+ * `SELECT` upstream already uses for the page count.
1179
+ */
1180
+ get databaseSize() {
1181
+ requireInputLock(this.#ctx, "sql.databaseSize");
1182
+ const db = this.#owner.getSqliteDb();
1183
+ return readNumber(db.run("select (select * from pragma_page_count) - (select * from pragma_freelist_count);"), "page count") * this.#getPageSize(db);
1184
+ }
1185
+ /** ← `SqlStorage::prepare`. Experimental and deprecated upstream; `exec` caches for you. */
1186
+ prepare(query) {
1187
+ requireInputLock(this.#ctx, "sql.prepare()");
1188
+ const run = (...bindings) => this.exec(query, ...bindings);
1189
+ Object.setPrototypeOf(run, Statement.prototype);
1190
+ return run;
1191
+ }
1192
+ /** ← `SqlStorage::ingest`. */
1193
+ ingest(query) {
1194
+ requireInputLock(this.#ctx, "sql.ingest()");
1195
+ requireAllowedNames(query);
1196
+ return this.#owner.getSqliteDb().ingest(query, regulateUntrustedStatement);
1197
+ }
1198
+ /** ← `SqlStorage::setMaxPageCountForTest`, which is what its name says. */
1199
+ setMaxPageCountForTest(count) {
1200
+ requireInputLock(this.#ctx, "sql.setMaxPageCountForTest()");
1201
+ this.#owner.getSqliteDb().run(`PRAGMA max_page_count = ${count}`);
1202
+ }
1203
+ /** ← `SqlStorage::getPageSize`. */
1204
+ #getPageSize(db) {
1205
+ const cached = this.#pageSize;
1206
+ if (cached !== void 0) return cached;
1207
+ const size = readNumber(db.run("select * from pragma_page_size;"), "page size");
1208
+ this.#pageSize = size;
1209
+ return size;
1210
+ }
1211
+ };
1212
+ function readNumber(result, what) {
1213
+ const value = result.rawRows[0]?.[0];
1214
+ if (typeof value === "number") return value;
1215
+ if (typeof value === "bigint") return Number(value);
1216
+ throw new Error(`Expected a number for the database's ${what}.`);
1217
+ }
1218
+ /** ← JSG's conversion from JavaScript arguments to `SqlStorage::BindingValue`. */
1219
+ function toSqlBindingValue(value) {
1220
+ if (value === null || value === void 0) return null;
1221
+ if (typeof value === "string" || typeof value === "number") return value;
1222
+ if (typeof value === "boolean") return String(value);
1223
+ if (typeof value === "bigint") throw new TypeError("Cannot convert a BigInt value to a number");
1224
+ if (value instanceof ArrayBuffer) return copyBytes(new Uint8Array(value));
1225
+ if (ArrayBuffer.isView(value)) return copyBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
1226
+ throw new TypeError(`Cannot convert ${Object.prototype.toString.call(value)} to a SQL value`);
1227
+ }
1228
+ function copyBytes(bytes) {
1229
+ const copy = new Uint8Array(bytes.byteLength);
1230
+ copy.set(bytes);
1231
+ return copy;
1232
+ }
1233
+ /**
1234
+ * ← `SqlStorage::wrapSqlValue` plus the `Query::getValue` switch above it.
1235
+ *
1236
+ * Upstream's int64 arm carries its own comment: "int64 will become BigInt, but
1237
+ * most applications won't want all their integers to be BigInt. We will coerce
1238
+ * to a double here." That coercion is kept rather than refused, because it is
1239
+ * the documented behaviour of `sql.exec` and a caller storing an id larger than
1240
+ * 2^53 has already lost on workerd.
1241
+ */
1242
+ function toSqlStorageValue(value) {
1243
+ if (value === null || value === void 0) return null;
1244
+ if (typeof value === "string" || typeof value === "number") return value;
1245
+ if (typeof value === "bigint") return Number(value);
1246
+ if (typeof value === "boolean") return value ? 1 : 0;
1247
+ if (value instanceof Uint8Array) {
1248
+ const copy = new ArrayBuffer(value.byteLength);
1249
+ new Uint8Array(copy).set(value);
1250
+ return copy;
1251
+ }
1252
+ throw new Error(`SQL returned a ${typeof value}, which is not a SqlStorageValue.`);
1253
+ }
1254
+ /**
1255
+ * The two narrowings a generic row type needs. `T` is the caller's claim about
1256
+ * the shape of a row SQLite produced at runtime, so no check can confirm it and
1257
+ * upstream does not try — its `Cursor<T>` is the same claim written in a
1258
+ * `JSG_TS_OVERRIDE`. Confined to these two functions so the claim is one place
1259
+ * rather than sprinkled through the cursor.
1260
+ */
1261
+ function asRow(row) {
1262
+ return row;
1263
+ }
1264
+ function asRawRow(values) {
1265
+ return values;
1266
+ }
1267
+ //#endregion
1268
+ //#region src/api/actor-state.ts
1269
+ /**
1270
+ * ← workerd `src/workerd/api/actor-state.{h,c++}`
1271
+ *
1272
+ * The JS-facing storage objects: `DurableObjectStorageOperations` and its two
1273
+ * subclasses, `DurableObjectFacets`, and `DurableObjectState`. Everything below
1274
+ * this file is reached through one of them.
1275
+ *
1276
+ * **`DurableObjectStorage` satisfies workers-types with no cast (§2.4).** That
1277
+ * was checked rather than asserted, and two shapes here exist only because it
1278
+ * has to: `sql.Cursor` and `sql.Statement` must be constructible with no
1279
+ * arguments (see `sql.ts`), and `storage.kv` is required. The narrowings that
1280
+ * remain are all one thing —
1281
+ * `get<T>` returns the caller's claim about the shape of a value SQLite handed
1282
+ * back as bytes, which no check can confirm and which upstream states the same
1283
+ * way, as a `jsg::JsRef<jsg::JsValue>` behind a `JSG_TS_OVERRIDE`'d
1284
+ * `Promise<T>`. There is no `as unknown as` anywhere in this layer.
1285
+ *
1286
+ * **Every throw is synchronous, including from the promise-returning methods.**
1287
+ * That is upstream's: a `JSG_REQUIRE` inside a method returning `jsg::Promise`
1288
+ * throws into the isolate before the promise exists, so `put(k, undefined)`
1289
+ * throws rather than rejecting. The same goes for a value that will not decode,
1290
+ * because §1.4 makes the SQLite path run the decoder before `Promise.resolve`.
1291
+ *
1292
+ * **What the input gate does and does not do here.** Every entry point calls
1293
+ * `requireInputLock` — see its comment in `io/io-context.ts`, which is the one
1294
+ * place this package decides what an empty invocation stack means. Nothing else
1295
+ * takes a lock: a read returns a value, a write returns a resolved promise, and
1296
+ * `atCheckpointEnd` is what keeps the whole chain inside one transaction
1297
+ * (§1.7.1). The two exceptions are upstream's own — `sync()` and the bookmark
1298
+ * pair release the gate via `awaitIo`, and `transaction()` takes a critical
1299
+ * section.
1300
+ *
1301
+ * **Decision 2's branch has one reachable site**, and it is not where upstream's
1302
+ * is. §1.4 measures that SQLite cache operations are immediate, so their
1303
+ * `kj::OneOf<T, kj::Promise<T>>` branch has nothing to select between.
1304
+ * `transformMaybeBackpressure` keeps the branch because
1305
+ * `DeleteAllResults.backpressure` is still a promise in `io/actor-cache.ts`.
1306
+ *
1307
+ * Not ported, because the substrate has no equivalent: V8's private wire bytes,
1308
+ * replaced by a browser-safe structured-clone encoding with the same public
1309
+ * value semantics; the billing counters
1310
+ * (`billingUnits`, `ActorObserver`, `updateStorageWriteUnit`) and the trace
1311
+ * spans, both already absent throughout; `enableSql`, a workerd namespace option
1312
+ * that exists to simulate a non-SQLite Durable Object; and `ReplicaActorOutgoingFactory`,
1313
+ * whose replication half is a named boundary in `io/actor-cache.ts`.
1314
+ *
1315
+ * Spec: §1.4, §1.5, §1.10, §2.4, §2.5, decisions 2, 4 and 14 in
1316
+ * docs/decisions.md.
1317
+ */
1318
+ /**
1319
+ * ← `MAX_FACET_NAME_LENGTH` / `MAX_FACET_TREE_DEPTH`
1320
+ * (`actor-state.c++:943,947`), in the anonymous namespace beside the facet code
1321
+ * that enforces them. The scaffolding had them in `server/`, which is neither
1322
+ * where upstream puts them nor where they are checked.
1323
+ */
1324
+ var FACET_NAME_MAX_LENGTH = 256;
1325
+ /** Root is at depth 0, so the deepest allowed facet is at depth 3. */
1326
+ var FACET_TREE_MAX_DEPTH = 4;
1327
+ /**
1328
+ * ← what falls off the end of `DurableObjectFacets::get`'s class switch
1329
+ * (`actor-state.c++:1029-1043`).
1330
+ *
1331
+ * Upstream accepts three things as `FacetStartupOptions.class`: a bare
1332
+ * `DurableObjectClass`, a `LoopbackDurableObjectNamespace`, or a
1333
+ * `LoopbackColoLocalActorNamespace`, unwrapping the last two through
1334
+ * `getClass()`. All three are ported — the loopback pair by
1335
+ * `api/export-loopback.ts` — and `KJ_UNREACHABLE` is the fourth case there
1336
+ * because JSG has already refused anything else while unwrapping the
1337
+ * `kj::OneOf`. The check has to be written here because
1338
+ * `@cloudflare/workers-types` declares `interface DurableObjectClass<_T> {}`,
1339
+ * which every object satisfies, so nothing refuses it before the method body.
1340
+ */
1341
+ 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.";
1342
+ /** ← `DurableObjectStorageOperations::OpName`. Named only where an error quotes them. */
1343
+ var OP_GET = "get()";
1344
+ var OP_GET_ALARM = "getAlarm()";
1345
+ var OP_LIST = "list()";
1346
+ var OP_PUT = "put()";
1347
+ var OP_PUT_ALARM = "setAlarm()";
1348
+ var OP_DELETE = "delete()";
1349
+ var OP_DELETE_ALARM = "deleteAlarm()";
1350
+ var OP_ROLLBACK = "rollback()";
1351
+ /** ← `actor-state.c++:455`, verbatim: the one message both overloads' misuse produces. */
1352
+ 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)";
1353
+ /**
1354
+ * ← the `kj::OneOf<kj::String, jsg::Dict<…>>` unwrap on put()'s first parameter: `jsg::Dict`
1355
+ * takes any JS object except an Array — functions and Maps included — and `kj::String` takes
1356
+ * everything else by coercion. A type predicate, so the overload split narrows without a cast.
1357
+ */
1358
+ function isEntriesArgument(value) {
1359
+ return (typeof value === "object" || typeof value === "function") && value !== null && !Array.isArray(value);
1360
+ }
1361
+ /**
1362
+ * ← the struct wrapper (`jsg/struct.h:246-258`), which is NOT the Dict wrapper: `PutOptions` is
1363
+ * all-optional fields, so `null` unwraps to default options, and any object does — arrays and
1364
+ * functions included, because the wrapper checks `IsObject()` with no Array exclusion. Only a
1365
+ * non-null primitive fails to unwrap. Measured on real workerd: `put({k: 1}, null)`, `…, [])`
1366
+ * and `…, function () {})` all write, and `put({k: 1}, "v")` alone is the overload error.
1367
+ */
1368
+ function isPutOptions(value) {
1369
+ return value === null || typeof value === "object" || typeof value === "function";
1370
+ }
1371
+ /** The key immediately after `k` in byte order is `k` plus this. */
1372
+ var NULL_CHARACTER = "\0";
1373
+ /** ← the `0xff` upstream strips from the tail of a prefix, in UTF-16 code units. */
1374
+ var MAX_CODE_UNIT = 65535;
1375
+ var textEncoder$1 = new TextEncoder();
1376
+ var textDecoder = new TextDecoder();
1377
+ /** A byte JSON could never begin with, `DO`, and the local codec version. */
1378
+ var VALUE_CODEC_HEADER = new Uint8Array([
1379
+ 0,
1380
+ 68,
1381
+ 79,
1382
+ 1
1383
+ ]);
1384
+ /**
1385
+ * ← `serializeV8Value`. The wire bytes differ because V8's serializer is not
1386
+ * available in browsers; the public structured-clone value semantics do not.
1387
+ * The short header keeps the new representation unambiguous while old JSON rows
1388
+ * remain readable.
1389
+ */
1390
+ function serializeValue(value) {
1391
+ const body = textEncoder$1.encode(JSON.stringify(serialize(value)));
1392
+ const encoded = new Uint8Array(VALUE_CODEC_HEADER.byteLength + body.byteLength);
1393
+ encoded.set(VALUE_CODEC_HEADER);
1394
+ encoded.set(body, VALUE_CODEC_HEADER.byteLength);
1395
+ return encoded;
1396
+ }
1397
+ /**
1398
+ * ← `deserializeV8Value`.
1399
+ *
1400
+ * Upstream logs "the key (to help find the data in the database if it hasn't
1401
+ * been deleted), the length of the value, and the first three bytes of the value
1402
+ * (which is just the v8-internal version header and the tag that indicates the
1403
+ * type of the value, but not its contents)". Our four-byte header carries only
1404
+ * a marker and version for the same reason.
1405
+ */
1406
+ function deserializeValue(key, buffer) {
1407
+ if (buffer.byteLength === 0) throw new Error(`unexpectedly empty value buffer; key = ${key}`);
1408
+ try {
1409
+ const structured = VALUE_CODEC_HEADER.every((byte, index) => buffer[index] === byte);
1410
+ const bytes = structured ? buffer.subarray(VALUE_CODEC_HEADER.byteLength) : buffer;
1411
+ const parsed = JSON.parse(textDecoder.decode(bytes));
1412
+ return structured ? deserialize(parsed) : parsed;
1413
+ } catch (exception) {
1414
+ throw new Error(`actor storage deserialization failed: failed to deserialize stored value; key = ${key}; size = ${buffer.byteLength}`, { cause: exception });
1415
+ }
1416
+ }
1417
+ /**
1418
+ * ← `transformMaybeBackpressure` (`actor-state.c++:103-119`). THIS is decision
1419
+ * 2's live site: `DeleteAllResults.backpressure` is still `Promise<void> |
1420
+ * undefined`, so the branch has something to select between.
1421
+ *
1422
+ * Upstream's own note, kept because it is the reason the flag is threaded here
1423
+ * at all: "In practice `allowConcurrency` will have no effect on a backpressure
1424
+ * promise since backpressure blocks everything anyway, but we pass the option
1425
+ * through for consistency in case of future changes."
1426
+ */
1427
+ function transformMaybeBackpressure(ctx, options, maybeBackpressure) {
1428
+ if (maybeBackpressure === void 0) return Promise.resolve();
1429
+ if (options.allowConcurrency === true) return ctx.awaitIo(maybeBackpressure);
1430
+ return ctx.awaitIoWithInputLock(maybeBackpressure, () => {});
1431
+ }
1432
+ /**
1433
+ * ← `DurableObjectStorageOperations::compileListOptions`
1434
+ * (`actor-state.c++:314-417`). Returns undefined if the list operation would
1435
+ * provably return no results. `SyncKvStorage` reuses it, exactly as upstream's
1436
+ * comment says it must.
1437
+ *
1438
+ * Two translations. `startAfter` gains ONE null character where upstream's
1439
+ * `kj::String` gains two, because the second of upstream's is the terminator and
1440
+ * a JS string has none. And every comparison here is on UTF-16 code units where
1441
+ * upstream's is on UTF-8 bytes, while the range the database actually applies is
1442
+ * SQLite's `BINARY` collation over UTF-8 — the two orders agree for every key
1443
+ * outside the astral planes, and a key that mixes astral characters with a
1444
+ * prefix can land on the wrong side of a clamp this function computes.
1445
+ */
1446
+ function compileListOptions(options) {
1447
+ let start = "";
1448
+ let end;
1449
+ let reverse = false;
1450
+ let limit;
1451
+ if (options !== void 0) {
1452
+ if (options.start !== void 0) {
1453
+ if (options.startAfter !== void 0) throw new TypeError("list() cannot be called with both start and startAfter values.");
1454
+ start = options.start;
1455
+ }
1456
+ if (options.startAfter !== void 0) start = options.startAfter + NULL_CHARACTER;
1457
+ if (options.end !== void 0) end = options.end;
1458
+ if (options.reverse !== void 0) reverse = options.reverse;
1459
+ if (options.limit !== void 0) {
1460
+ if (!(options.limit > 0)) throw new TypeError("List limit must be positive.");
1461
+ limit = options.limit;
1462
+ }
1463
+ const prefix = options.prefix;
1464
+ if (prefix !== void 0 && prefix.length > 0) {
1465
+ if (start < prefix) start = prefix;
1466
+ else if (start.startsWith(prefix)) {} else return;
1467
+ const keyAfterPrefix = firstKeyAfterPrefix(prefix);
1468
+ if (keyAfterPrefix === void 0) {} else if (end === void 0) end = keyAfterPrefix;
1469
+ else if (end <= prefix) return;
1470
+ else if (end.startsWith(prefix)) {} else end = keyAfterPrefix;
1471
+ }
1472
+ }
1473
+ if (end !== void 0 && end <= start) return;
1474
+ return {
1475
+ start,
1476
+ end,
1477
+ reverse,
1478
+ limit
1479
+ };
1480
+ }
1481
+ /**
1482
+ * ← the `keyAfterPrefix` vector: strip maximal trailing units, then increment.
1483
+ *
1484
+ * Returns undefined when the prefix is nothing but maximal units, which is
1485
+ * upstream's "the prefix is a string of some number of 0xff bytes, so includes
1486
+ * the entire key space up through the last possible key".
1487
+ */
1488
+ function firstKeyAfterPrefix(prefix) {
1489
+ let head = prefix;
1490
+ while (head.length > 0 && head.charCodeAt(head.length - 1) === MAX_CODE_UNIT) head = head.slice(0, -1);
1491
+ if (head.length === 0) return void 0;
1492
+ return head.slice(0, -1) + String.fromCharCode(head.charCodeAt(head.length - 1) + 1);
1493
+ }
1494
+ /**
1495
+ * ← workerd `src/workerd/api/sync-kv.{h,c++}`. The synchronous surface lives
1496
+ * beside the asynchronous storage owner because both share the same codec and
1497
+ * list-option compiler over one `SqliteKv`.
1498
+ */
1499
+ var SyncKvStorage = class {
1500
+ #ctx;
1501
+ #kv;
1502
+ constructor(ctx, kv) {
1503
+ this.#ctx = ctx;
1504
+ this.#kv = kv;
1505
+ }
1506
+ get(key) {
1507
+ requireInputLock(this.#ctx, "kv.get()");
1508
+ const value = this.#kv.get(key);
1509
+ if (value === void 0) return void 0;
1510
+ return deserializeValue(key, value);
1511
+ }
1512
+ list(options) {
1513
+ requireInputLock(this.#ctx, "kv.list()");
1514
+ const compiled = compileListOptions(options);
1515
+ if (compiled === void 0) return [];
1516
+ return listIterator(this.#kv.list(compiled.start, compiled.end, compiled.limit, compiled.reverse ? "REVERSE" : "FORWARD"));
1517
+ }
1518
+ put(key, value) {
1519
+ requireInputLock(this.#ctx, "kv.put()");
1520
+ this.#kv.put(key, serializeValue(value));
1521
+ }
1522
+ delete(key) {
1523
+ requireInputLock(this.#ctx, "kv.delete()");
1524
+ return this.#kv.delete(key);
1525
+ }
1526
+ };
1527
+ /** ← `SyncKvStorage::listNext`, whose cancellation branch is the reason it is not a plain loop. */
1528
+ function* listIterator(cursor) {
1529
+ for (;;) {
1530
+ const pair = cursor.next();
1531
+ if (pair !== void 0) {
1532
+ yield [pair.key, deserializeValue(pair.key, pair.value)];
1533
+ continue;
1534
+ }
1535
+ 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.");
1536
+ return;
1537
+ }
1538
+ }
1539
+ /**
1540
+ * ← `DurableObjectStorageOperations`. "Common implementation of
1541
+ * DurableObjectStorage and DurableObjectTransaction. This class is designed to
1542
+ * be used as a mixin."
1543
+ */
1544
+ var DurableObjectStorageOperations = class {
1545
+ ctx;
1546
+ constructor(ctx) {
1547
+ this.ctx = ctx;
1548
+ }
1549
+ get(keyOrKeys, maybeOptions) {
1550
+ requireInputLock(this.ctx, OP_GET);
1551
+ const options = { ...maybeOptions };
1552
+ if (typeof keyOrKeys === "string") return this.#getOne(keyOrKeys, options);
1553
+ return this.#getMultiple(keyOrKeys, options);
1554
+ }
1555
+ getAlarm(maybeOptions) {
1556
+ requireInputLock(this.ctx, OP_GET_ALARM);
1557
+ const options = {
1558
+ ...maybeOptions,
1559
+ noCache: false
1560
+ };
1561
+ return Promise.resolve(this.getCache(OP_GET_ALARM).getAlarm(options));
1562
+ }
1563
+ list(maybeOptions) {
1564
+ requireInputLock(this.ctx, OP_LIST);
1565
+ const compiled = compileListOptions(maybeOptions);
1566
+ if (compiled === void 0) return Promise.resolve(/* @__PURE__ */ new Map());
1567
+ const options = { ...maybeOptions };
1568
+ const cache = this.getCache(OP_LIST);
1569
+ const result = compiled.reverse ? cache.listReverse(compiled.start, compiled.end, compiled.limit, options) : cache.list(compiled.start, compiled.end, compiled.limit, options);
1570
+ return Promise.resolve(listResultsToMap(result));
1571
+ }
1572
+ put(keyOrEntries, valueOrOptions, maybeOptions) {
1573
+ requireInputLock(this.ctx, OP_PUT);
1574
+ if (!isEntriesArgument(keyOrEntries)) {
1575
+ if (valueOrOptions === void 0) throw new TypeError("put() called with undefined value.");
1576
+ return this.#putOne(`${keyOrEntries}`, valueOrOptions, { ...maybeOptions });
1577
+ }
1578
+ if (valueOrOptions !== void 0 && !isPutOptions(valueOrOptions)) throw new TypeError(PUT_OVERLOAD_MESSAGE);
1579
+ return this.#putMultiple(keyOrEntries, { ...valueOrOptions });
1580
+ }
1581
+ delete(keyOrKeys, maybeOptions) {
1582
+ requireInputLock(this.ctx, OP_DELETE);
1583
+ const options = { ...maybeOptions };
1584
+ if (typeof keyOrKeys === "string") return Promise.resolve(this.getCache(OP_DELETE).delete(keyOrKeys, options));
1585
+ return Promise.resolve(this.getCache(OP_DELETE).deleteMultiple(keyOrKeys, options));
1586
+ }
1587
+ setAlarm(scheduledTime, maybeOptions) {
1588
+ requireInputLock(this.ctx, OP_PUT_ALARM);
1589
+ const when = scheduledTime instanceof Date ? scheduledTime.getTime() : scheduledTime;
1590
+ if (!(when > 0)) throw new TypeError("setAlarm() cannot be called with an alarm time <= 0");
1591
+ this.ctx.getActorOrThrow().assertCanSetAlarm();
1592
+ const options = {
1593
+ ...maybeOptions,
1594
+ noCache: false
1595
+ };
1596
+ this.getCache(OP_PUT_ALARM).setAlarm(Math.max(when, this.ctx.now()), options);
1597
+ return Promise.resolve();
1598
+ }
1599
+ deleteAlarm(maybeOptions) {
1600
+ requireInputLock(this.ctx, OP_DELETE_ALARM);
1601
+ const options = {
1602
+ ...maybeOptions,
1603
+ noCache: false
1604
+ };
1605
+ this.getCache(OP_DELETE_ALARM).setAlarm(null, options);
1606
+ return Promise.resolve();
1607
+ }
1608
+ #getOne(key, options) {
1609
+ const value = this.getCache(OP_GET).get(key, options);
1610
+ return Promise.resolve(value === void 0 ? void 0 : deserializeValue(key, value));
1611
+ }
1612
+ #getMultiple(keys, options) {
1613
+ const result = this.getCache(OP_GET).getMultiple(keys, options);
1614
+ return Promise.resolve(listResultsToMap(result));
1615
+ }
1616
+ #putOne(key, value, options) {
1617
+ this.getCache(OP_PUT).put(key, serializeValue(value), options);
1618
+ return Promise.resolve();
1619
+ }
1620
+ #putMultiple(entries, options) {
1621
+ const pairs = [];
1622
+ for (const [key, value] of Object.entries(entries)) {
1623
+ if (value === void 0) continue;
1624
+ pairs.push({
1625
+ key,
1626
+ value: serializeValue(value)
1627
+ });
1628
+ }
1629
+ this.getCache(OP_PUT).putMultiple(pairs, options);
1630
+ return Promise.resolve();
1631
+ }
1632
+ };
1633
+ /** ← `listResultsToMap` and `getMultipleResultsToMap`, minus the billing halves. */
1634
+ function listResultsToMap(rows) {
1635
+ const map = /* @__PURE__ */ new Map();
1636
+ for (const entry of rows) map.set(entry.key, deserializeValue(entry.key, entry.value));
1637
+ return map;
1638
+ }
1639
+ var DurableObjectStorage = class extends DurableObjectStorageOperations {
1640
+ #cache;
1641
+ #sql;
1642
+ #kv;
1643
+ constructor(ctx, cache) {
1644
+ super(ctx);
1645
+ this.#cache = cache;
1646
+ }
1647
+ /** ← `DurableObjectStorage::getActorCacheInterface`, which `DurableObjectState::abort` needs. */
1648
+ getActorCacheInterface() {
1649
+ return this.#cache;
1650
+ }
1651
+ /** ← `DurableObjectStorage::getSqliteDb`. Always SQLite-backed here; see the header. */
1652
+ getSqliteDb() {
1653
+ return this.#cache.getSqliteDatabase();
1654
+ }
1655
+ getCache() {
1656
+ return this.#cache;
1657
+ }
1658
+ /** ← `JSG_LAZY_INSTANCE_PROPERTY(sql, getSql)`. */
1659
+ get sql() {
1660
+ this.#sql ??= new SqlStorage(this.ctx, this);
1661
+ return this.#sql;
1662
+ }
1663
+ /** ← `JSG_LAZY_INSTANCE_PROPERTY(kv, getKv)`. */
1664
+ get kv() {
1665
+ this.#kv ??= new SyncKvStorage(this.ctx, this.#cache.getSqliteKv());
1666
+ return this.#kv;
1667
+ }
1668
+ /**
1669
+ * ← `DurableObjectStorage::deleteAll`.
1670
+ *
1671
+ * `deleteAlarm` is upstream's `FeatureFlags::get(js).getDeleteAllDeletesAlarm()`,
1672
+ * a compatibility flag that exists so Workers published before it keep the old
1673
+ * behaviour. A runtime with no deployed history takes the current behaviour.
1674
+ */
1675
+ deleteAll(maybeOptions) {
1676
+ requireInputLock(this.ctx, "deleteAll()");
1677
+ const options = { ...maybeOptions };
1678
+ const result = this.#cache.deleteAll(options, { deleteAlarm: true });
1679
+ return transformMaybeBackpressure(this.ctx, options, result.backpressure);
1680
+ }
1681
+ /**
1682
+ * ← `DurableObjectStorage::transaction`.
1683
+ *
1684
+ * The critical section is load bearing and upstream says why: "the call to
1685
+ * `startTransaction()` is when the SQLite-backed implementation will actually
1686
+ * invoke `BEGIN TRANSACTION`, so it's important that we're inside the
1687
+ * blockConcurrencyWhile block before that point so we don't accidentally catch
1688
+ * some other asynchronous event in our transaction."
1689
+ *
1690
+ * The exception is packed into the result rather than thrown out of the
1691
+ * section, and then rethrown outside it. Upstream's reason: "We don't actually
1692
+ * want to reset the object, we only want to roll back the transaction and
1693
+ * propagate the exception." A throw out of a critical section permanently
1694
+ * breaks the input gate (§1.5), so a failing transaction callback would
1695
+ * destroy the actor.
1696
+ */
1697
+ transaction(closure) {
1698
+ requireInputLock(this.ctx, "transaction()");
1699
+ return this.ctx.blockConcurrencyWhile(async () => {
1700
+ const txn = new DurableObjectTransaction(this.ctx, this.#cache.startTransaction());
1701
+ try {
1702
+ const value = await closure(txn);
1703
+ txn.maybeCommit();
1704
+ return {
1705
+ isError: false,
1706
+ value
1707
+ };
1708
+ } catch (exception) {
1709
+ txn.maybeRollback();
1710
+ return {
1711
+ isError: true,
1712
+ exception
1713
+ };
1714
+ }
1715
+ }).then((result) => {
1716
+ if (result.isError) throw result.exception;
1717
+ return result.value;
1718
+ });
1719
+ }
1720
+ /** ← `DurableObjectStorage::transactionSync`, a forward for the reason above. */
1721
+ transactionSync(callback) {
1722
+ requireInputLock(this.ctx, "transactionSync()");
1723
+ return this.#cache.transactionSync(callback);
1724
+ }
1725
+ /**
1726
+ * ← `DurableObjectStorage::sync`.
1727
+ *
1728
+ * Upstream's `awaitIo` rather than `awaitIoWithInputLock`, which is the one
1729
+ * storage method that deliberately opens the gate: "we're merely checking if
1730
+ * we have any pending or in-flight operations, and providing a promise that
1731
+ * resolves when they succeed."
1732
+ */
1733
+ sync() {
1734
+ requireInputLock(this.ctx, "sync()");
1735
+ return this.ctx.awaitIo(this.#cache.onNoPendingFlush());
1736
+ }
1737
+ /**
1738
+ * Real, not a boundary: `ActorSqlite`'s is "an ersatz implementation that's
1739
+ * good enough for local dev with D1's Session API", built on the metadata
1740
+ * table's local-development bookmark. Anything above this package that
1741
+ * surfaces it to an application should know it is a counter and not a
1742
+ * recovery point — as it is on workerd.
1743
+ */
1744
+ getCurrentBookmark() {
1745
+ requireInputLock(this.ctx, "getCurrentBookmark()");
1746
+ return this.ctx.awaitIo(this.#cache.getCurrentBookmark());
1747
+ }
1748
+ waitForBookmark(bookmark) {
1749
+ requireInputLock(this.ctx, "waitForBookmark()");
1750
+ return this.ctx.awaitIo(this.#cache.waitForBookmark(bookmark));
1751
+ }
1752
+ /** Substrate boundary: point-in-time recovery. Upstream reaches the cache directly, as this does. */
1753
+ getBookmarkForTime(timestamp) {
1754
+ return this.#cache.getBookmarkForTime(timestamp instanceof Date ? timestamp.getTime() : timestamp);
1755
+ }
1756
+ /** Substrate boundary: point-in-time recovery. */
1757
+ onNextSessionRestoreBookmark(bookmark) {
1758
+ return this.#cache.onNextSessionRestoreBookmark(bookmark);
1759
+ }
1760
+ /** Substrate boundary: replication. */
1761
+ ensureReplicas() {
1762
+ this.#cache.ensureReplicas();
1763
+ }
1764
+ /** Substrate boundary: replication. */
1765
+ disableReplicas() {
1766
+ this.#cache.disableReplicas();
1767
+ }
1768
+ /**
1769
+ * ← `DurableObjectStorage::getPrimary` / `isReplica`. `maybePrimary` is set
1770
+ * only by the replica constructor, and nothing constructs a replica here, so
1771
+ * these answer upstream's own non-replica case rather than a stubbed one.
1772
+ */
1773
+ getPrimary() {}
1774
+ isReplica() {
1775
+ return false;
1776
+ }
1777
+ };
1778
+ var DurableObjectTransaction = class extends DurableObjectStorageOperations {
1779
+ /** Becomes undefined when committed or rolled back. */
1780
+ #cacheTxn;
1781
+ #rolledBack = false;
1782
+ constructor(ctx, cacheTxn) {
1783
+ super(ctx);
1784
+ this.#cacheTxn = cacheTxn;
1785
+ }
1786
+ getCache(op) {
1787
+ if (this.#rolledBack) throw new Error(`Cannot ${op} on rolled back transaction`);
1788
+ const txn = this.#cacheTxn;
1789
+ if (txn === void 0) throw new Error(`Cannot call ${op} on transaction that has already committed: did you move \`txn\` outside of the closure?`);
1790
+ return txn;
1791
+ }
1792
+ /** Called from JS. */
1793
+ rollback() {
1794
+ if (this.#rolledBack) return;
1795
+ this.getCache(OP_ROLLBACK);
1796
+ const txn = this.#cacheTxn;
1797
+ if (txn !== void 0) {
1798
+ txn.rollback();
1799
+ txn.drop();
1800
+ this.#cacheTxn = void 0;
1801
+ }
1802
+ this.#rolledBack = true;
1803
+ }
1804
+ /** Just throws an exception saying this isn't supported. */
1805
+ deleteAll() {
1806
+ throw new Error("Cannot call deleteAll() within a transaction");
1807
+ }
1808
+ /**
1809
+ * Called from the runtime, not JS, after the transaction callback has
1810
+ * completed. Does nothing if the transaction is already committed or rolled
1811
+ * back. Synchronous, because `ActorCacheTransaction::commit` is (§1.4).
1812
+ */
1813
+ maybeCommit() {
1814
+ const txn = this.#cacheTxn;
1815
+ if (txn === void 0) return;
1816
+ this.#cacheTxn = void 0;
1817
+ txn.commit();
1818
+ txn.drop();
1819
+ }
1820
+ /** Same, for the failure path. Upstream's drops the transaction, whose destructor rolls back. */
1821
+ maybeRollback() {
1822
+ const txn = this.#cacheTxn;
1823
+ this.#cacheTxn = void 0;
1824
+ this.#rolledBack = true;
1825
+ txn?.drop();
1826
+ }
1827
+ };
1828
+ /**
1829
+ * ← `requireValidFacetName` (`actor-state.c++:949-952`).
1830
+ *
1831
+ * The comparison is `name.size()` on a `kj::StringPtr`, which is **UTF-8 bytes**,
1832
+ * so it is measured in bytes here too — the same `TextEncoder` pass, for the same
1833
+ * reason, that `ColoLocalActorNamespace.get`'s `[1, 2048]` bound already costs.
1834
+ * Comparing `name.length` accepts a 256-character non-ASCII name that upstream
1835
+ * refuses, which is a bound a caller can hit.
1836
+ */
1837
+ function requireValidFacetName(name) {
1838
+ if (textEncoder$1.encode(name).length > 256) throw new TypeError(`Facet name is too long (max 256 characters).`);
1839
+ }
1840
+ /**
1841
+ * ← the `KJ_SWITCH_ONEOF(options.$class)` lambda (`actor-state.c++:1029-1043`).
1842
+ *
1843
+ * Three arms, and the order matters for the same reason it does upstream: a
1844
+ * `LoopbackDurableObjectClass` *is* a `DurableObjectClass`, so it takes the bare
1845
+ * arm here exactly as JSG's `kj::OneOf` unwraps it into the first alternative.
1846
+ * The two loopback namespaces are not classes and carry one, which `getClass()`
1847
+ * hands back.
1848
+ *
1849
+ * A `ctx.exports` entry is the callable façade `api/export-loopback.ts` produces
1850
+ * rather than the instance itself, and every check below is an `instanceof` that
1851
+ * the façade's `getPrototypeOf` answers — which is why that trap exists.
1852
+ */
1853
+ function requireFacetClass(actorClass) {
1854
+ if (actorClass instanceof DurableObjectClass) return actorClass;
1855
+ if (actorClass instanceof LoopbackDurableObjectNamespace) return actorClass.getClass();
1856
+ if (actorClass instanceof LoopbackColoLocalActorNamespace) return actorClass.getClass();
1857
+ throw new TypeError(FACET_CLASS_UNSUPPORTED_MESSAGE);
1858
+ }
1859
+ /**
1860
+ * ← `DurableObjectFacets`.
1861
+ *
1862
+ * **`clone` is the fourth method, and the vendored C++ snapshot does not have
1863
+ * it.** The design record cites `actor-state.h:431-497` and
1864
+ * `server.c++:721-749`; neither line range contains it, `DurableObjectFacets`
1865
+ * there exposes exactly `get`, `abort` and `delete`, and
1866
+ * `Worker::Actor::FacetManager` has exactly `getDepth`, `getFacet`, `abortFacet`
1867
+ * and `deleteFacet`. It is real all the same: `@cloudflare/workers-types`
1868
+ * 4.20260702.1 — a month newer than the snapshot — declares
1869
+ * `clone(src: string, dst: string): void` on `DurableObjectFacets`. So the
1870
+ * signature comes from the types and the semantics from §1.10 (abort dst, delete
1871
+ * dst storage, recursive copy of the src subtree), and the orchestration is
1872
+ * `server/`'s `cloneFacet`. There is nothing upstream to check the body against,
1873
+ * which makes it the one method here with no reference — worth knowing when it
1874
+ * is wrong.
1875
+ */
1876
+ var DurableObjectFacets = class {
1877
+ #ctx;
1878
+ #facetManager;
1879
+ #parentId;
1880
+ constructor(ctx, facetManager, parentId) {
1881
+ this.#ctx = ctx;
1882
+ this.#facetManager = facetManager;
1883
+ this.#parentId = parentId;
1884
+ }
1885
+ /**
1886
+ * Get a facet by name, starting it if it isn't already running.
1887
+ * `getStartupOptions` is invoked only if the facet wasn't already running.
1888
+ *
1889
+ * Returns a `Fetcher` instead of a `DurableObject` because the returned stub
1890
+ * does not have the `id` or `name` methods that a DO stub normally has.
1891
+ */
1892
+ get(name, getStartupOptions) {
1893
+ requireValidFacetName(name);
1894
+ const facetManager = this.#getFacetManager();
1895
+ if (facetManager.getDepth() + 1 >= 4) throw new Error(`Facet nesting depth limit exceeded. The maximum depth including the root Durable Object is 4.`);
1896
+ requireInputLock(this.#ctx, "facets.get()");
1897
+ const getStartInfo = this.#ctx.makeReentryCallback(async () => {
1898
+ const options = await getStartupOptions();
1899
+ const id = options.id;
1900
+ return {
1901
+ actorClass: requireFacetClass(options.class).getChannel(),
1902
+ id: id === void 0 ? this.#parentId : typeof id === "string" ? id : id.name ?? id.toString()
1903
+ };
1904
+ });
1905
+ return facetManager.getFacet(name, getStartInfo);
1906
+ }
1907
+ abort(name, reason) {
1908
+ requireValidFacetName(name);
1909
+ this.#getFacetManager().abortFacet(name, reason);
1910
+ }
1911
+ delete(name) {
1912
+ requireValidFacetName(name);
1913
+ this.#getFacetManager().deleteFacet(name);
1914
+ }
1915
+ clone(src, dst) {
1916
+ requireValidFacetName(src);
1917
+ requireValidFacetName(dst);
1918
+ this.#getFacetManager().cloneFacet(src, dst);
1919
+ }
1920
+ #getFacetManager() {
1921
+ const facetManager = this.#facetManager;
1922
+ if (facetManager === void 0) throw new Error("This Durable Object does not support creating facets.");
1923
+ return facetManager;
1924
+ }
1925
+ };
1926
+ /** The type passed as the first parameter to a Durable Object class's constructor. */
1927
+ var DurableObjectState = class {
1928
+ #ctx;
1929
+ #options;
1930
+ #facets;
1931
+ constructor(ctx, options) {
1932
+ this.#ctx = ctx;
1933
+ this.#options = options;
1934
+ }
1935
+ get id() {
1936
+ return this.#options.id;
1937
+ }
1938
+ get props() {
1939
+ return this.#options.props;
1940
+ }
1941
+ /** ← `JSG_LAZY_INSTANCE_PROPERTY(exports, getExports)`, behind `enableCtxExports` upstream. */
1942
+ get exports() {
1943
+ return this.#options.exports;
1944
+ }
1945
+ get version() {
1946
+ return this.#options.version;
1947
+ }
1948
+ /**
1949
+ * NO upstream correspondence, because upstream needs none: a
1950
+ * `ServiceWorkerGlobalScope` IS the isolate's global object there, so an
1951
+ * actor's class reaches its gated `setTimeout` by writing `setTimeout`.
1952
+ *
1953
+ * Here one realm hosts several actors, so the names on `globalThis` can only
1954
+ * be bound to one of them and a continuation cannot be asked which one it
1955
+ * belongs to. `ctx` is the one reference every Durable Object class already
1956
+ * holds and that already means exactly one actor — the constructor was handed
1957
+ * it — so it is where the scope goes. An actor's method writes
1958
+ * `this.ctx.globals.setTimeout(…)`; a free function it calls takes the scope
1959
+ * as a parameter.
1960
+ *
1961
+ * `installActorScope` still exists and is still what a host uses for a
1962
+ * dynamically-loaded Worker source, which has no `ctx` to reach through and
1963
+ * its own module scope to destructure into. The two are the same object.
1964
+ */
1965
+ get globals() {
1966
+ return this.#options.globals;
1967
+ }
1968
+ get storage() {
1969
+ const storage = this.#options.storage;
1970
+ if (storage === void 0) throw new Error("This Durable Object does not have storage.");
1971
+ return storage;
1972
+ }
1973
+ /** ← `JSG_LAZY_INSTANCE_PROPERTY(facets, getFacets)`. */
1974
+ get facets() {
1975
+ this.#facets ??= new DurableObjectFacets(this.#ctx, this.#options.facets, this.#options.id.toString());
1976
+ return this.#facets;
1977
+ }
1978
+ waitUntil(promise) {
1979
+ this.#ctx.addWaitUntil(promise.then(() => {}));
1980
+ }
1981
+ /**
1982
+ * ← `DurableObjectState::blockConcurrencyWhile` (`actor-state.c++:1128-1131`),
1983
+ * which is a one-line forward and nothing else. The 30-second deadline, the
1984
+ * brokenness annotation and the never-settled promise on failure all live in
1985
+ * `IoContext::blockConcurrencyWhile`, which Section 2 already implements.
1986
+ *
1987
+ * Its precondition comes with it: `IoContext::blockConcurrencyWhile` calls
1988
+ * `getInputLock()`, which asserts, so this is reachable only from inside a
1989
+ * gated slice.
1990
+ */
1991
+ blockConcurrencyWhile(callback) {
1992
+ return this.#ctx.blockConcurrencyWhile(callback);
1993
+ }
1994
+ /**
1995
+ * ← `DurableObjectState::abort`. Reset the object, including breaking the
1996
+ * output gate and canceling any writes that haven't been committed yet.
1997
+ *
1998
+ * `js.terminateExecutionNow()` has no port — there is no isolate to terminate —
1999
+ * so the caller's own slice keeps running to its next await, where `IoContext`
2000
+ * refuses to re-enter.
2001
+ */
2002
+ abort(reason) {
2003
+ const description = reason === void 0 ? "broken.outputGateBroken; jsg.Error: Application called abort() to reset Durable Object." : `broken.outputGateBroken; jsg.Error: ${reason}`;
2004
+ const error = new Error(description);
2005
+ setUserErrorDetail(error);
2006
+ this.#options.storage?.getActorCacheInterface().shutdown(error);
2007
+ this.#ctx.abort(error);
2008
+ }
2009
+ /** ← `DurableObjectState::getPrimaryStub`. Non-null only for a replica; see the storage note. */
2010
+ get primaryStub() {
2011
+ return this.#options.storage?.getPrimary();
2012
+ }
2013
+ /** Substrate boundary: replication. */
2014
+ configureReadReplication(options) {
2015
+ const storage = this.#options.storage;
2016
+ if (storage === void 0) throw new TypeError("This actor does not support read replication.");
2017
+ if (storage.isReplica()) throw new Error("Replica Durable Objects cannot call configureReadReplication().");
2018
+ if (options.mode !== "auto" && options.mode !== "disabled") throw new TypeError(`configureReadReplication() called with unknown mode setting: ${options.mode}.`);
2019
+ return this.#ctx.awaitIo(storage.getActorCacheInterface().configureReadReplication(options.mode === "auto"));
2020
+ }
2021
+ acceptWebSocket(ws, tags) {
2022
+ this.#options.webSockets.acceptWebSocket(ws, tags);
2023
+ }
2024
+ getWebSockets(tag) {
2025
+ return tag === void 0 ? this.#options.webSockets.getWebSockets() : this.#options.webSockets.getWebSockets(tag);
2026
+ }
2027
+ setWebSocketAutoResponse(maybeReqResp) {
2028
+ this.#options.webSockets.setWebSocketAutoResponse(maybeReqResp);
2029
+ }
2030
+ getWebSocketAutoResponse() {
2031
+ return this.#options.webSockets.getWebSocketAutoResponse();
2032
+ }
2033
+ getWebSocketAutoResponseTimestamp(ws) {
2034
+ return this.#options.webSockets.getWebSocketAutoResponseTimestamp(ws);
2035
+ }
2036
+ setHibernatableWebSocketEventTimeout(timeoutMs) {
2037
+ this.#options.webSockets.setHibernatableWebSocketEventTimeout(timeoutMs);
2038
+ }
2039
+ getHibernatableWebSocketEventTimeout() {
2040
+ return this.#options.webSockets.getHibernatableWebSocketEventTimeout();
2041
+ }
2042
+ getTags(ws) {
2043
+ return this.#options.webSockets.getTags(ws);
2044
+ }
2045
+ };
2046
+ //#endregion
2047
+ //#region src/api/web-socket.ts
2048
+ var WebSocketRequestResponsePairImpl = class {
2049
+ #request;
2050
+ #response;
2051
+ constructor(request, response) {
2052
+ this.#request = String(request);
2053
+ this.#response = String(response);
2054
+ }
2055
+ get request() {
2056
+ return this.#request;
2057
+ }
2058
+ get response() {
2059
+ return this.#response;
2060
+ }
2061
+ };
2062
+ var RuntimeWebSocketRequestResponsePair = new Proxy(WebSocketRequestResponsePairImpl, { apply() {
2063
+ throw new TypeError("Failed to construct 'WebSocketRequestResponsePair': Please use the 'new' operator, this DOM object constructor cannot be called as a function.");
2064
+ } });
2065
+ /** ← the `JSG_REQUIRE(!native.state.is<Accepted>(), ...)` at the head of `accept()`. */
2066
+ 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.";
2067
+ var HIBERNATION_ALREADY_ACCEPTED_MESSAGE = "Cannot call `acceptWebSocket()` if the WebSocket was already accepted via `accept()`";
2068
+ var HIBERNATION_AFTER_ACCEPT_MESSAGE = "Can't accept() WebSocket after enabling hibernation.";
2069
+ var HIBERNATION_PAIR_USED_MESSAGE = "Cannot call `acceptWebSocket()` on this WebSocket because its pair has already been accepted or used in a Response.";
2070
+ var MAX_HIBERNATABLE_SOCKETS = 32768;
2071
+ var MAX_TAGS = 10;
2072
+ var MAX_TAG_LENGTH = 256;
2073
+ var MAX_ATTACHMENT_BYTES = 16384;
2074
+ var MAX_AUTO_RESPONSE_BYTES = 2048;
2075
+ var MAX_EVENT_TIMEOUT = 6048e5;
2076
+ var MAX_CLOSE_REASON_BYTES = 123;
2077
+ var WEB_SOCKET_READY_STATES = {
2078
+ READY_STATE_CONNECTING: 0,
2079
+ READY_STATE_OPEN: 1,
2080
+ READY_STATE_CLOSING: 2,
2081
+ READY_STATE_CLOSED: 3,
2082
+ CONNECTING: 0,
2083
+ OPEN: 1,
2084
+ CLOSING: 2,
2085
+ CLOSED: 3
2086
+ };
2087
+ var textEncoder = new TextEncoder();
2088
+ var SOCKET_EVENTS = [
2089
+ "open",
2090
+ "message",
2091
+ "close",
2092
+ "error"
2093
+ ];
2094
+ var metadata = /* @__PURE__ */ new WeakMap();
2095
+ function socketMetadata(socket) {
2096
+ let value = metadata.get(socket);
2097
+ if (value === void 0) {
2098
+ value = {};
2099
+ metadata.set(socket, value);
2100
+ }
2101
+ return value;
2102
+ }
2103
+ function isRawWebSocket(value) {
2104
+ return typeof value === "object" && value !== null && "addEventListener" in value && typeof value.addEventListener === "function" && "send" in value && typeof value.send === "function" && "close" in value && typeof value.close === "function";
2105
+ }
2106
+ function requireWebSocket(value, operation) {
2107
+ if (!isRawWebSocket(value)) throw new TypeError(`Failed to execute '${operation}' on 'WebSocket': parameter 1 is not of type 'WebSocket'.`);
2108
+ return value;
2109
+ }
2110
+ function serializeAttachment(socket, value) {
2111
+ try {
2112
+ structuredClone(value);
2113
+ } catch (error) {
2114
+ if (error instanceof DOMException && error.name === "DataCloneError") throw new DOMException(error.message.replace(/^Failed to execute 'structuredClone' on '[^']+': /, ""), "DataCloneError");
2115
+ throw error;
2116
+ }
2117
+ const bytes = serializeValue(value);
2118
+ const measuredBytes = typeof value === "string" ? textEncoder.encode(value).byteLength + 5 : bytes.byteLength;
2119
+ if (measuredBytes > MAX_ATTACHMENT_BYTES) throw new Error(`A WebSocket 'attachment' cannot be larger than ${MAX_ATTACHMENT_BYTES} bytes.'attachment' was ${measuredBytes} bytes.`);
2120
+ const state = socketMetadata(socket);
2121
+ state.attachment = bytes;
2122
+ if (state.accepted?.mode === "hibernatable") state.accepted.registry.attachmentChanged(socket, bytes);
2123
+ }
2124
+ function deserializeAttachment(socket) {
2125
+ const attachment = socketMetadata(socket).attachment;
2126
+ if (attachment === void 0) return null;
2127
+ return deserializeValue("WebSocket attachment", attachment);
2128
+ }
2129
+ function serializeAttachmentMethod(value) {
2130
+ if (arguments.length === 0) throw new TypeError("Failed to execute 'serializeAttachment' on 'WebSocket': parameter 1 is not of type 'Value'.");
2131
+ serializeAttachment(requireWebSocket(this, "serializeAttachment"), value);
2132
+ }
2133
+ function deserializeAttachmentMethod() {
2134
+ return deserializeAttachment(requireWebSocket(this, "deserializeAttachment"));
2135
+ }
2136
+ function cloneMessageData(data) {
2137
+ if (typeof data === "string" || data instanceof Blob) return data;
2138
+ if (data instanceof ArrayBuffer) return data.slice(0);
2139
+ if (ArrayBuffer.isView(data)) return new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice().buffer;
2140
+ return String(data);
2141
+ }
2142
+ var MemoryWebSocketEndpoint = class extends EventTarget {
2143
+ peer;
2144
+ #sentClose = false;
2145
+ send(data) {
2146
+ this.peer.dispatchEvent(new MessageEvent("message", { data: cloneMessageData(data) }));
2147
+ }
2148
+ close(code = 1e3, reason = "") {
2149
+ if (this.#sentClose) return;
2150
+ this.#sentClose = true;
2151
+ this.peer.dispatchEvent(new CloseEvent("close", {
2152
+ code,
2153
+ reason,
2154
+ wasClean: true
2155
+ }));
2156
+ }
2157
+ };
2158
+ /** One public socket identity, in classic or hibernatable mode after acceptance. */
2159
+ var AcceptedWebSocket = class AcceptedWebSocket extends EventTarget {
2160
+ static READY_STATE_CONNECTING = WEB_SOCKET_READY_STATES.READY_STATE_CONNECTING;
2161
+ static READY_STATE_OPEN = WEB_SOCKET_READY_STATES.READY_STATE_OPEN;
2162
+ static READY_STATE_CLOSING = WEB_SOCKET_READY_STATES.READY_STATE_CLOSING;
2163
+ static READY_STATE_CLOSED = WEB_SOCKET_READY_STATES.READY_STATE_CLOSED;
2164
+ static CONNECTING = WEB_SOCKET_READY_STATES.CONNECTING;
2165
+ static OPEN = WEB_SOCKET_READY_STATES.OPEN;
2166
+ static CLOSING = WEB_SOCKET_READY_STATES.CLOSING;
2167
+ static CLOSED = WEB_SOCKET_READY_STATES.CLOSED;
2168
+ bufferedAmount = 0;
2169
+ extensions = "";
2170
+ protocol = "";
2171
+ url = "";
2172
+ #ctx;
2173
+ #socket;
2174
+ #pairState;
2175
+ #delivery = { mode: "pending" };
2176
+ #pump = Promise.resolve();
2177
+ #pending = [];
2178
+ #readyState = AcceptedWebSocket.OPEN;
2179
+ #ownClose = false;
2180
+ #peerClose = false;
2181
+ #binaryType = "blob";
2182
+ onopen = null;
2183
+ onmessage = null;
2184
+ onclose = null;
2185
+ onerror = null;
2186
+ constructor(ctx, socket, pairState) {
2187
+ super();
2188
+ this.#ctx = ctx;
2189
+ this.#socket = socket;
2190
+ this.#pairState = pairState;
2191
+ if (pairState === void 0) this.#enableClassic();
2192
+ for (const type of SOCKET_EVENTS) socket.addEventListener(type, (event) => {
2193
+ this.#receive(type, event);
2194
+ });
2195
+ }
2196
+ get readyState() {
2197
+ return this.#readyState;
2198
+ }
2199
+ get binaryType() {
2200
+ return this.#binaryType;
2201
+ }
2202
+ set binaryType(value) {
2203
+ this.#binaryType = value;
2204
+ }
2205
+ accept() {
2206
+ if (this.#delivery.mode === "hibernatable") throw new TypeError(HIBERNATION_AFTER_ACCEPT_MESSAGE);
2207
+ if (this.#delivery.mode === "classic") throw new Error(ALREADY_ACCEPTED_MESSAGE);
2208
+ if (this.#pairState !== void 0) this.#pairState.used = true;
2209
+ this.#enableClassic();
2210
+ }
2211
+ send(data) {
2212
+ if (this.#delivery.mode === "hibernatable" && this.#ownClose) throw new TypeError("Can't call WebSocket send() after close().");
2213
+ if (this.#peerClose || this.#readyState === AcceptedWebSocket.CLOSED) return;
2214
+ this.#markPairUsed();
2215
+ this.#enqueue(() => this.#socket.send(data));
2216
+ }
2217
+ close(code, reason = "") {
2218
+ if (this.#readyState === AcceptedWebSocket.CLOSED || this.#ownClose) return;
2219
+ if (this.#delivery.mode === "hibernatable" || this.#pairState !== void 0) validateClose(code, reason);
2220
+ this.#markPairUsed();
2221
+ this.#ownClose = true;
2222
+ this.#readyState = this.#peerClose ? AcceptedWebSocket.CLOSED : AcceptedWebSocket.CLOSING;
2223
+ this.#enqueue(() => this.#socket.close(code, reason));
2224
+ }
2225
+ serializeAttachment(value) {
2226
+ if (arguments.length === 0) serializeAttachmentMethod.call(this);
2227
+ else serializeAttachment(this, value);
2228
+ }
2229
+ deserializeAttachment() {
2230
+ return deserializeAttachment(this);
2231
+ }
2232
+ acceptHibernation(registry) {
2233
+ if (this.#delivery.mode !== "pending") throw new Error(HIBERNATION_ALREADY_ACCEPTED_MESSAGE);
2234
+ if (this.#pairState?.used === true && !this.#pairState.hibernationAccepted) throw new Error(HIBERNATION_PAIR_USED_MESSAGE);
2235
+ if (this.#pairState !== void 0) {
2236
+ this.#pairState.used = true;
2237
+ this.#pairState.hibernationAccepted = true;
2238
+ }
2239
+ this.#activateHibernation(registry, this.#ctx);
2240
+ }
2241
+ rehydrateHibernation(registry, ctx) {
2242
+ this.#activateHibernation(registry, ctx);
2243
+ }
2244
+ #activateHibernation(registry, ctx) {
2245
+ this.#ctx = ctx;
2246
+ this.#delivery = {
2247
+ mode: "hibernatable",
2248
+ registry
2249
+ };
2250
+ this.#pending = [];
2251
+ }
2252
+ markPairUsed() {
2253
+ this.#markPairUsed();
2254
+ }
2255
+ #markPairUsed() {
2256
+ if (this.#pairState !== void 0) this.#pairState.used = true;
2257
+ }
2258
+ #enableClassic() {
2259
+ const delivery = {
2260
+ mode: "classic",
2261
+ criticalSection: this.#ctx.getCriticalSection()
2262
+ };
2263
+ this.#delivery = delivery;
2264
+ socketMetadata(this).accepted = { mode: "classic" };
2265
+ const pending = this.#pending;
2266
+ this.#pending = [];
2267
+ for (const item of pending) this.#deliverClassic(item.type, item.event, delivery.criticalSection);
2268
+ }
2269
+ #receive(type, event) {
2270
+ if (type === "close") {
2271
+ this.#receiveClose(event);
2272
+ return;
2273
+ }
2274
+ const delivery = this.#delivery;
2275
+ if (delivery.mode === "pending") this.#pending.push({
2276
+ type,
2277
+ event
2278
+ });
2279
+ else if (delivery.mode === "classic") this.#deliverClassic(type, event, delivery.criticalSection);
2280
+ else delivery.registry.receive(this, type, event);
2281
+ }
2282
+ #receiveClose(event) {
2283
+ if (this.#ownClose) this.#readyState = AcceptedWebSocket.CLOSED;
2284
+ else {
2285
+ this.#peerClose = true;
2286
+ this.#readyState = AcceptedWebSocket.CLOSING;
2287
+ if (this.#delivery.mode === "classic" && this.#pairState !== void 0) {
2288
+ if (event.code === 1005 || event.code === 1006 || event.code === 1015) this.#readyState = AcceptedWebSocket.CLOSED;
2289
+ else this.close(event.code, event.reason);
2290
+ }
2291
+ }
2292
+ const delivery = this.#delivery;
2293
+ if (delivery.mode === "pending") this.#pending.push({
2294
+ type: "close",
2295
+ event
2296
+ });
2297
+ else if (delivery.mode === "classic") this.#deliverClassic("close", event, delivery.criticalSection);
2298
+ else delivery.registry.receive(this, "close", event);
2299
+ }
2300
+ #deliverClassic(type, event, criticalSection) {
2301
+ this.#ctx.addWaitUntil(this.#ctx.run(() => {
2302
+ const delivered = cloneEventFor(type, event);
2303
+ this.dispatchEvent(delivered);
2304
+ const handler = this[`on${type}`];
2305
+ handler?.(delivered);
2306
+ }, { input: criticalSection }));
2307
+ }
2308
+ #enqueue(write) {
2309
+ const outputLock = this.#ctx.waitForOutputLocks();
2310
+ this.#pump = this.#pump.then(async () => {
2311
+ await outputLock;
2312
+ write();
2313
+ });
2314
+ this.#ctx.addWaitUntil(this.#pump);
2315
+ }
2316
+ };
2317
+ for (const [name, value] of Object.entries(WEB_SOCKET_READY_STATES)) Object.defineProperty(AcceptedWebSocket.prototype, name, {
2318
+ value,
2319
+ enumerable: true
2320
+ });
2321
+ var HibernatableWebSocketRegistry = class {
2322
+ #ctx;
2323
+ #dispatch;
2324
+ #host;
2325
+ #entries = [];
2326
+ #autoResponse = null;
2327
+ #eventTimeout = null;
2328
+ #pairConstructor;
2329
+ constructor(ctx, dispatch, host, rehydrated = []) {
2330
+ this.#ctx = ctx;
2331
+ this.#dispatch = dispatch;
2332
+ this.#host = host;
2333
+ const pair = host?.autoResponsePair;
2334
+ if (pair != null) this.setWebSocketAutoResponse(new RuntimeWebSocketRequestResponsePair(pair.request, pair.response));
2335
+ for (const value of rehydrated) this.#rehydrate(value);
2336
+ }
2337
+ get WebSocketPair() {
2338
+ this.#pairConstructor ??= new Proxy(class WebSocketPair {}, { construct: () => this.#createPair() });
2339
+ return this.#pairConstructor;
2340
+ }
2341
+ acceptWebSocket(socket, tags) {
2342
+ if (!isRawWebSocket(socket)) throw new TypeError("Failed to execute 'acceptWebSocket' on 'DurableObjectState': parameter 1 is not of type 'WebSocket'.");
2343
+ const state = socketMetadata(socket);
2344
+ if (state.accepted !== void 0) throw new Error(HIBERNATION_ALREADY_ACCEPTED_MESSAGE);
2345
+ if (this.#entries.length >= MAX_HIBERNATABLE_SOCKETS) throw new Error(`only ${MAX_HIBERNATABLE_SOCKETS} websockets can be accepted on a single Durable Object instance`);
2346
+ const normalizedTags = normalizeTags(tags);
2347
+ if (socket instanceof AcceptedWebSocket) socket.acceptHibernation(this);
2348
+ else this.#listenRaw(socket);
2349
+ state.accepted = {
2350
+ mode: "hibernatable",
2351
+ registry: this
2352
+ };
2353
+ this.#entries.push({
2354
+ socket,
2355
+ tags: normalizedTags
2356
+ });
2357
+ this.#host?.accepted(socket, normalizedTags);
2358
+ }
2359
+ getWebSockets(tag) {
2360
+ if (arguments.length > 0) {
2361
+ if (typeof tag !== "string") return [];
2362
+ return this.#entries.filter((entry) => entry.tags.includes(tag)).map((entry) => entry.socket);
2363
+ }
2364
+ return this.#entries.map((entry) => entry.socket).reverse();
2365
+ }
2366
+ getTags(socket) {
2367
+ const state = isRawWebSocket(socket) ? metadata.get(socket) : void 0;
2368
+ if (state?.accepted === void 0) throw new Error("you must call 'acceptWebSocket()' before attempting to access the tags of a WebSocket.");
2369
+ if (state.accepted.mode !== "hibernatable") throw new Error("only hibernatable websockets can have tags.");
2370
+ const entry = this.#entries.find((candidate) => candidate.socket === socket);
2371
+ if (entry === void 0) throw new Error("you must call 'acceptWebSocket()' before attempting to access the tags of a WebSocket.");
2372
+ return [...entry.tags];
2373
+ }
2374
+ setWebSocketAutoResponse(pair) {
2375
+ if (pair === void 0) {
2376
+ this.#autoResponse = null;
2377
+ this.#host?.autoResponse(null);
2378
+ return;
2379
+ }
2380
+ if (!(pair instanceof WebSocketRequestResponsePairImpl)) throw new TypeError("Failed to execute 'setWebSocketAutoResponse' on 'DurableObjectState': parameter 1 is not of type 'WebSocketRequestResponsePair'.");
2381
+ validateAutoResponseSize("Request", pair.request);
2382
+ validateAutoResponseSize("Response", pair.response);
2383
+ this.#autoResponse = pair;
2384
+ this.#host?.autoResponse({
2385
+ request: pair.request,
2386
+ response: pair.response
2387
+ });
2388
+ }
2389
+ getWebSocketAutoResponse() {
2390
+ const pair = this.#autoResponse;
2391
+ return pair === null ? null : new RuntimeWebSocketRequestResponsePair(pair.request, pair.response);
2392
+ }
2393
+ getWebSocketAutoResponseTimestamp(socket) {
2394
+ if (!isRawWebSocket(socket)) throw new TypeError("Failed to execute 'getWebSocketAutoResponseTimestamp' on 'DurableObjectState': parameter 1 is not of type 'WebSocket'.");
2395
+ const timestamp = this.#entries.find((entry) => entry.socket === socket)?.autoResponseTimestamp;
2396
+ return timestamp === void 0 ? null : new Date(timestamp);
2397
+ }
2398
+ setHibernatableWebSocketEventTimeout(value) {
2399
+ if (value === void 0 || Number(value) === 0) {
2400
+ this.#eventTimeout = null;
2401
+ return;
2402
+ }
2403
+ const number = Number(value);
2404
+ if (Number.isNaN(number)) throw new TypeError("The value cannot be converted because it is not an integer.");
2405
+ if (number < 0) throw new TypeError("The value cannot be converted because it is negative and this API expects a positive number.");
2406
+ if (number > 4294967295) throw new TypeError("Value out of range. Must be less than or equal to 4294967295.");
2407
+ const timeout = Math.trunc(number);
2408
+ if (timeout > MAX_EVENT_TIMEOUT) throw new Error(`Event timeout should not exceed ${MAX_EVENT_TIMEOUT} ms.`);
2409
+ this.#eventTimeout = timeout;
2410
+ }
2411
+ getHibernatableWebSocketEventTimeout() {
2412
+ return this.#eventTimeout;
2413
+ }
2414
+ attachmentChanged(socket, bytes) {
2415
+ if (this.#entries.some((entry) => entry.socket === socket)) this.#host?.attachment(socket, bytes);
2416
+ }
2417
+ receive(socket, type, event) {
2418
+ const entry = this.#entries.find((candidate) => candidate.socket === socket);
2419
+ if (entry === void 0) return;
2420
+ if (type === "message") {
2421
+ const data = event.data;
2422
+ if (typeof data === "string" && data === this.#autoResponse?.request) {
2423
+ entry.autoResponseTimestamp = this.#ctx.now();
2424
+ this.#host?.autoResponseTimestamp?.(socket, entry.autoResponseTimestamp);
2425
+ socket.send(this.#autoResponse.response);
2426
+ return;
2427
+ }
2428
+ const message = cloneMessageData(data);
2429
+ if (message instanceof Blob) {
2430
+ this.#ctx.addWaitUntil(message.arrayBuffer().then((buffer) => {
2431
+ this.#schedule(() => this.#dispatch.message(socket, buffer));
2432
+ }));
2433
+ return;
2434
+ }
2435
+ this.#schedule(() => this.#dispatch.message(socket, message));
2436
+ return;
2437
+ }
2438
+ if (type === "close") {
2439
+ this.#remove(entry);
2440
+ const close = event;
2441
+ this.#schedule(() => this.#dispatch.close(socket, close.code, close.reason, close.wasClean));
2442
+ return;
2443
+ }
2444
+ if (type === "error") this.#schedule(() => this.#dispatch.error(socket, event));
2445
+ }
2446
+ #schedule(handler) {
2447
+ this.#ctx.addWaitUntil(this.#ctx.run(handler).then(() => {}));
2448
+ }
2449
+ #remove(entry) {
2450
+ const index = this.#entries.indexOf(entry);
2451
+ if (index === -1) return;
2452
+ this.#entries.splice(index, 1);
2453
+ this.#host?.closed(entry.socket);
2454
+ }
2455
+ #listenRaw(socket) {
2456
+ const state = socketMetadata(socket);
2457
+ if (state.rawListenersInstalled === true) return;
2458
+ state.rawListenersInstalled = true;
2459
+ for (const type of [
2460
+ "message",
2461
+ "close",
2462
+ "error"
2463
+ ]) socket.addEventListener(type, (event) => {
2464
+ const accepted = socketMetadata(socket).accepted;
2465
+ if (accepted?.mode === "hibernatable") accepted.registry.receive(socket, type, event);
2466
+ });
2467
+ }
2468
+ #rehydrate(value) {
2469
+ const socket = value.socket;
2470
+ if (!isRawWebSocket(socket)) throw new TypeError("ActorContainerOptions.webSockets contains a non-WebSocket value.");
2471
+ const tags = normalizeTags(value.tags);
2472
+ const state = socketMetadata(socket);
2473
+ state.accepted = {
2474
+ mode: "hibernatable",
2475
+ registry: this
2476
+ };
2477
+ if (value.attachment !== void 0) state.attachment = value.attachment.slice();
2478
+ if (socket instanceof AcceptedWebSocket) socket.rehydrateHibernation(this, this.#ctx);
2479
+ else this.#listenRaw(socket);
2480
+ const entry = {
2481
+ socket,
2482
+ tags
2483
+ };
2484
+ if (value.autoResponseTimestamp !== void 0) entry.autoResponseTimestamp = value.autoResponseTimestamp;
2485
+ this.#entries.push(entry);
2486
+ }
2487
+ #createPair() {
2488
+ const pairState = {
2489
+ used: false,
2490
+ hibernationAccepted: false
2491
+ };
2492
+ const left = new MemoryWebSocketEndpoint();
2493
+ const right = new MemoryWebSocketEndpoint();
2494
+ left.peer = right;
2495
+ right.peer = left;
2496
+ return {
2497
+ 0: new AcceptedWebSocket(this.#ctx, left, pairState),
2498
+ 1: new AcceptedWebSocket(this.#ctx, right, pairState)
2499
+ };
2500
+ }
2501
+ };
2502
+ function acceptWebSocket(ctx, socket) {
2503
+ const state = socketMetadata(socket);
2504
+ if (state.accepted !== void 0) throw new Error(ALREADY_ACCEPTED_MESSAGE);
2505
+ if (socket instanceof AcceptedWebSocket) {
2506
+ socket.accept();
2507
+ return socket;
2508
+ }
2509
+ state.accepted = { mode: "classic" };
2510
+ return new AcceptedWebSocket(ctx, socket);
2511
+ }
2512
+ function markWebSocketUsed(socket) {
2513
+ if (socket instanceof AcceptedWebSocket) socket.markPairUsed();
2514
+ }
2515
+ function installWebSocketGlobals(target, pairConstructor) {
2516
+ const constructor = globalThis.WebSocket;
2517
+ for (const [name, value] of Object.entries(WEB_SOCKET_READY_STATES)) {
2518
+ defineValue(constructor, name, value);
2519
+ defineValue(constructor.prototype, name, value);
2520
+ }
2521
+ defineValue(constructor.prototype, "serializeAttachment", serializeAttachmentMethod);
2522
+ defineValue(constructor.prototype, "deserializeAttachment", deserializeAttachmentMethod);
2523
+ defineValue(target, "WebSocket", constructor);
2524
+ defineValue(target, "WebSocketPair", pairConstructor);
2525
+ defineValue(target, "WebSocketRequestResponsePair", RuntimeWebSocketRequestResponsePair);
2526
+ }
2527
+ function defineValue(target, name, value) {
2528
+ if (Object.getOwnPropertyDescriptor(target, name)?.configurable === false) return;
2529
+ Object.defineProperty(target, name, {
2530
+ configurable: true,
2531
+ writable: true,
2532
+ value
2533
+ });
2534
+ }
2535
+ function normalizeTags(tags) {
2536
+ if (tags === void 0) return [];
2537
+ if (!Array.isArray(tags)) throw new TypeError("Failed to execute 'acceptWebSocket' on 'DurableObjectState': parameter 2 is not of type 'Array'.");
2538
+ if (tags.length > MAX_TAGS) throw new Error(`a Hibernatable WebSocket cannot have more than ${MAX_TAGS} tags`);
2539
+ const normalized = [...new Set(tags.map(String))];
2540
+ for (const tag of normalized) if (tag.length > MAX_TAG_LENGTH) throw new Error(`"${tag}" is longer than the max tag length (${MAX_TAG_LENGTH} characters).`);
2541
+ return normalized;
2542
+ }
2543
+ function validateAutoResponseSize(side, value) {
2544
+ const bytes = textEncoder.encode(value).byteLength;
2545
+ if (bytes > MAX_AUTO_RESPONSE_BYTES) throw new RangeError(`${side} cannot be larger than ${MAX_AUTO_RESPONSE_BYTES} bytes. A ${side.toLowerCase()} of size ${bytes} was provided.`);
2546
+ }
2547
+ function validateClose(code, reason) {
2548
+ if (code !== void 0 && code !== 1e3 && (code < 3e3 || code > 4999)) throw new DOMException(`Invalid WebSocket close code: ${code}.`, "InvalidAccessError");
2549
+ if (textEncoder.encode(reason).byteLength > MAX_CLOSE_REASON_BYTES) throw new DOMException(`WebSocket close reason must not be longer than ${MAX_CLOSE_REASON_BYTES} bytes when UTF-8 encoded.`, "SyntaxError");
2550
+ }
2551
+ function cloneEventFor(type, event) {
2552
+ if (type === "message") {
2553
+ const source = event;
2554
+ return new MessageEvent("message", {
2555
+ data: source.data,
2556
+ origin: source.origin,
2557
+ lastEventId: source.lastEventId
2558
+ });
2559
+ }
2560
+ if (type === "close") {
2561
+ const source = event;
2562
+ return new CloseEvent("close", {
2563
+ code: source.code,
2564
+ reason: source.reason,
2565
+ wasClean: source.wasClean
2566
+ });
2567
+ }
2568
+ return new Event(type);
2569
+ }
2570
+ //#endregion
2571
+ export { DurableObjectClass as _, installWebSocketGlobals as a, DurableObjectStorage as c, LoopbackColoLocalActorNamespace as d, LoopbackDurableObjectClass as f, ACTOR_CLASS_SERIALIZATION_UNIMPLEMENTED_MESSAGE as g, asLoopbackDurableObjectClass as h, acceptWebSocket as i, FACET_NAME_MAX_LENGTH as l, LoopbackServiceStub as m, HibernatableWebSocketRegistry as n, markWebSocketUsed as o, LoopbackDurableObjectNamespace as p, RuntimeWebSocketRequestResponsePair as r, DurableObjectState as s, ALREADY_ACCEPTED_MESSAGE as t, FACET_TREE_MAX_DEPTH as u, DurableObjectId as v, DurableObjectNamespace as y };
2572
+
2573
+ //# sourceMappingURL=web-socket-PWFZxlBg.js.map