@mcp-b/do-runtime 0.1.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +110 -0
  3. package/LICENSE.workerd +176 -0
  4. package/NOTICE +7 -0
  5. package/README.md +282 -0
  6. package/dist/backends/node-sqlite.d.ts +38 -0
  7. package/dist/backends/node-sqlite.js +335 -0
  8. package/dist/backends/node-sqlite.js.map +1 -0
  9. package/dist/backends/sqlite-wasm.d.ts +130 -0
  10. package/dist/backends/sqlite-wasm.js +259 -0
  11. package/dist/backends/sqlite-wasm.js.map +1 -0
  12. package/dist/chunks/sqlite-DFg92Tgt.js +498 -0
  13. package/dist/chunks/sqlite-DFg92Tgt.js.map +1 -0
  14. package/dist/cloudflare-workers.js +351 -0
  15. package/dist/cloudflare-workers.js.map +1 -0
  16. package/dist/conformance/host.d.ts +58 -0
  17. package/dist/conformance.js +18 -0
  18. package/dist/conformance.js.map +1 -0
  19. package/dist/index.js +7184 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/server/alarm-scheduler.js +513 -0
  22. package/dist/server/alarm-scheduler.js.map +1 -0
  23. package/dist/src/api/actor-state.d.ts +396 -0
  24. package/dist/src/api/actor.d.ts +306 -0
  25. package/dist/src/api/cloudflare-workers.d.ts +259 -0
  26. package/dist/src/api/export-loopback.d.ts +264 -0
  27. package/dist/src/api/global-scope.d.ts +262 -0
  28. package/dist/src/api/http.d.ts +52 -0
  29. package/dist/src/api/sql.d.ts +188 -0
  30. package/dist/src/api/sync-kv.d.ts +51 -0
  31. package/dist/src/api/web-socket.d.ts +93 -0
  32. package/dist/src/api/worker-loader.d.ts +354 -0
  33. package/dist/src/index.d.ts +130 -0
  34. package/dist/src/io/actor-cache.d.ts +203 -0
  35. package/dist/src/io/actor-id.d.ts +74 -0
  36. package/dist/src/io/actor-sqlite.d.ts +298 -0
  37. package/dist/src/io/io-channels.d.ts +191 -0
  38. package/dist/src/io/io-context.d.ts +451 -0
  39. package/dist/src/io/io-gate.d.ts +298 -0
  40. package/dist/src/io/worker-source.d.ts +108 -0
  41. package/dist/src/io/worker.d.ts +88 -0
  42. package/dist/src/server/actor-container.d.ts +525 -0
  43. package/dist/src/server/actor-id-impl.d.ts +118 -0
  44. package/dist/src/server/alarm-scheduler.d.ts +201 -0
  45. package/dist/src/server/facet-deletion.d.ts +156 -0
  46. package/dist/src/server/facet-tree-index.d.ts +94 -0
  47. package/dist/src/server/sha256.d.ts +39 -0
  48. package/dist/src/transport/rpc-session.d.ts +34 -0
  49. package/dist/src/util/sqlite-kv.d.ts +98 -0
  50. package/dist/src/util/sqlite-metadata.d.ts +46 -0
  51. package/dist/src/util/sqlite.d.ts +291 -0
  52. package/package.json +111 -0
@@ -0,0 +1,525 @@
1
+ /**
2
+ * ← workerd `src/workerd/server/server.c++` — `ActorNamespace::ActorContainer`
3
+ * (`:2383-2968`), which is at once the supervisor's per-actor record, its
4
+ * `Worker::Actor::FacetManager`, and the code that builds the storage engine.
5
+ *
6
+ * Composes the gates, the context, the storage engine and the facet tree into
7
+ * one actor. Constructs the Durable Object class under workerd's boot
8
+ * semantics, and on break aborts facets, abandons scheduled writes, refuses
9
+ * re-entry, and surfaces `onBroken`.
10
+ *
11
+ * **The three line ranges this file was handed are all in the wrong class, and
12
+ * the right ones are above.** `server.c++:1199-1214`, `:1225-1237` and
13
+ * `:1293-1297` are all inside `Server::DiskDirectoryService` (`:1058`) — the
14
+ * directory-listing branch of a static-file handler, its entry-type switch, and
15
+ * a `sendError(501, "Not Implemented")`. None of them has anything to do with
16
+ * actors. The three things they were offered for are real, and are at
17
+ * `:2864-2877` (alarm hooks installed only `if (parent == kj::none)`, with
18
+ * `// TODO(someday): Support alarms in facets, somehow.`), `:2885-2897`
19
+ * (`afterReset`, where `deleteAll()`'s cascade to descendant facet storage
20
+ * lives) and `:2603-2620` / `:2953-2956` (`getFacetContainer` and
21
+ * `actorClass->newActor`, the two halves of facet actor construction). Every
22
+ * citation in this file was checked line by line against
23
+ * `e8f1e125bd48f048a3e82c48d37e5e3902fffbd6`, which is the tree Section 6a's
24
+ * citations were taken from and agree with.
25
+ *
26
+ * **Ordering, because it is the part that cannot be read off upstream.**
27
+ * Upstream builds the actor lazily inside `getActor()`, so `ctx.storage` does
28
+ * not exist until the first request arrives and nothing can observe the gap.
29
+ * Here `ActorContainer.state` is a plain property that promises a
30
+ * `DurableObjectState` answering `.storage`, and the database behind it opens
31
+ * asynchronously (`SqlDatabaseProvider.open`, Section 3's seam). Something has to
32
+ * give, and the honest one is the factory: `createActorContainer` returns a
33
+ * promise, and everything it returns is fully built. The alternative — a `state`
34
+ * property that throws until `start()` — is a worse lie, because it makes the
35
+ * one declared field of the public interface conditional on a call the type
36
+ * system cannot see.
37
+ *
38
+ * Spec: §1.6, §1.10, decisions 6 and 14 in
39
+ * docs/decisions.md.
40
+ */
41
+ import { DurableObjectState } from "../api/actor-state.js";
42
+ import type { FetchPort } from "../api/global-scope.js";
43
+ import { ActorGlobalScope } from "../api/global-scope.js";
44
+ import type { AcceptedWebSocket, RawWebSocket } from "../api/web-socket.js";
45
+ import type { IsolateChannelFactory, WorkerLoaderOptions } from "../api/worker-loader.js";
46
+ import { WorkerLoader } from "../api/worker-loader.js";
47
+ import type { AlarmOutlet } from "../io/actor-sqlite.js";
48
+ import type { AlarmResult } from "./alarm-scheduler.js";
49
+ import type { Timer } from "../io/io-context.js";
50
+ import type { SqlDatabase, SqlDatabaseProvider } from "../util/sqlite.js";
51
+ import type { IndexFile } from "./facet-tree-index.js";
52
+ import { FacetReferenceEpochs } from "./facet-deletion.js";
53
+ export type FacetId = number;
54
+ export type FacetStartRequest = {
55
+ id: FacetId;
56
+ /** The `facets.get` name, `class\0name` form preserved. */
57
+ name: string;
58
+ /** Resolved against `ctx.exports` by `api/actor-state.ts`. */
59
+ className: string;
60
+ /** This facet's depth. The container enforces <= 4 including the root. */
61
+ depth: number;
62
+ /**
63
+ * The `ctx.id` the child was told to take, present only when the startup
64
+ * options supplied one. Absent means the child inherits the parent's, which
65
+ * is upstream's `ioCtx.getActorOrThrow().cloneId()` (`actor-state.c++:1026`).
66
+ *
67
+ * The scaffolding called this "the DurableObjectId name". It is not
68
+ * necessarily a name: `FacetStartupOptions.id` is `DurableObjectId | string`
69
+ * (`actor-state.h:453`), so this carries a 64-hex id string whenever the app
70
+ * passed a `DurableObjectId`, and whatever the app chose whenever it passed a
71
+ * string. The host decides what to do with it, exactly as upstream's
72
+ * `Worker::Actor::Id` leaves that to the supervisor.
73
+ */
74
+ routedId?: string;
75
+ };
76
+ export interface FacetHandle {
77
+ /**
78
+ * The placement's outcome, resolving to what `facets.get` will call —
79
+ * Fetcher-shaped per §1.10: no id, no name.
80
+ *
81
+ * `start` is synchronous because `facets.get` is, but placing an actor is not: a database
82
+ * has to open and a constructor has to run. Upstream has exactly this shape and
83
+ * does not have to say so — `getFacetContainer` hands `ActorChannelImpl` a
84
+ * `kj::Promise<ClassAndId>` (`server.c++:2603-2620`) and the channel is
85
+ * returned before it resolves, so a failure to construct reaches the caller at
86
+ * the first call on the stub. Measured on workerd 1.20260722.1: a facet whose
87
+ * constructor throws returns a stub from `facets.get()`, rejects the first call
88
+ * with the constructor's own message, and leaves the parent untouched.
89
+ *
90
+ * Keeping the placement promise here makes construction failures observable at
91
+ * the first call and leaves `asFacetTransport` as the sole deferral layer.
92
+ */
93
+ stub: Promise<object>;
94
+ /**
95
+ * Rejects when a RUNNING facet breaks — the signal upstream gets from
96
+ * `actor.onBroken()` and monitors in `monitorOnBroken`
97
+ * (`server.c++:2767-2800`).
98
+ *
99
+ * **A break travels DOWN and never up, so nothing here reaches the parent.**
100
+ * `ActorContainer::abort` (`:2565-2589`) and `monitorOnBroken` each loop
101
+ * `for (auto& facet: facets)` and abort the container's OWN children; a broken
102
+ * child takes itself and its subtree, and nothing above it notices.
103
+ * `conformance/suite/facets.spec.ts` guards this against workerd: the parent and
104
+ * its other facets remain live.
105
+ *
106
+ * `FacetManagerImpl` consumes this signal for `monitorOnBroken`'s two local
107
+ * effects: tell the host to tear down the broken placement (which aborts its
108
+ * descendants), and erase it from the PARENT's facet map (`:2777-2780`,
109
+ * `:2794-2798`). That frees the name for a fresh placement without changing
110
+ * the parent or its siblings.
111
+ *
112
+ * **A placement that never completed does not belong here** — it is a start
113
+ * that failed, not a break, and it travels through `stub`. Measured on
114
+ * workerd: a facet whose construction fails does not break its parent, and the
115
+ * next `facets.get` runs the startup callback again.
116
+ */
117
+ broken: Promise<never>;
118
+ }
119
+ /**
120
+ * The facet port. Substrate is only placement, the callable stub across that
121
+ * placement, and physical storage deletion for children a parent cannot open.
122
+ *
123
+ * NOT substrate, and therefore not in this interface: naming, ids, the
124
+ * depth/name/count limits, deletion receipts and epochs, and `clone()`
125
+ * orchestration. Those are package-owned. In particular there is no
126
+ * addressing-strategy port: once
127
+ * the package speaks facet ids, the app-address mapping is the browser
128
+ * adapter's private business and needs no interface at all.
129
+ *
130
+ * This is NOT the interface `api/actor-state.ts` consumes. That one is
131
+ * `io/worker.ts`'s `FacetManager`, upstream's own `Worker::Actor::FacetManager`,
132
+ * which this file implements on top of this port — see that file's header for
133
+ * why the two were conflated and why the fix was to widen rather than reshuffle.
134
+ * The `clone()` orchestration named above is `cloneFacet`'s body, and the
135
+ * limits named above are enforced by `DurableObjectFacets` where upstream
136
+ * enforces them.
137
+ *
138
+ * One host serves a whole actor tree, as upstream's `ActorNamespace` does: every
139
+ * `FacetId` it is handed is an id in the root's index, so a facet's own host
140
+ * must be able to place and remove any of its descendants.
141
+ */
142
+ export interface FacetHost {
143
+ /**
144
+ * Synchronous, like `facets.get` itself — but the placement it begins is not,
145
+ * and `FacetHandle.stub` is where that is declared. A host reports a placement
146
+ * it could not finish by rejecting that promise, and reports nothing about it
147
+ * on `broken`: see `FacetHandle`.
148
+ *
149
+ * A synchronous throw is still legal and still means the same thing — the
150
+ * facet cannot be started — and `getFacet` turns it into the same rejected
151
+ * stub. What is no longer possible is a host that fails asynchronously and has
152
+ * nowhere to say so.
153
+ */
154
+ start(request: FacetStartRequest): FacetHandle;
155
+ /**
156
+ * Kills the instance; storage survives (measured on workerd).
157
+ *
158
+ * **Never called while a placement for `id` is in flight**, and never before
159
+ * that placement's `stub` has settled. The container orders the two against
160
+ * each other, so a host needs no queue of its own and is never handed an abort
161
+ * for a facet it is still placing — which it could only no-op, its placement
162
+ * map having no entry for the id yet.
163
+ *
164
+ * It may still be called for an id that never ran: a placement that failed, or
165
+ * one refused before `start` was reached. Upstream's is too — `abortFacet`
166
+ * finds the container whether or not its `kj::Promise<ClassAndId>` ever
167
+ * resolved (`server.c++:2635-2640`) — so a host that has nothing to kill
168
+ * should return, not throw.
169
+ */
170
+ abort(id: FacetId, reason?: string): void;
171
+ /**
172
+ * Physical removal, descendants included. `subtree` is the descendants alone,
173
+ * deepest first — upstream removes children before the parent
174
+ * (`server.c++:2754-2759`) — and `id` is the facet itself.
175
+ */
176
+ deleteStorage(id: FacetId, subtree: readonly FacetId[]): Promise<void>;
177
+ /**
178
+ * Physical copy of one facet's storage onto another, for `cloneFacet`. The
179
+ * recursive walk of the source subtree is this file's; copying one database
180
+ * is the substrate's, the same split `deleteStorage` already makes.
181
+ */
182
+ copyStorage(src: FacetId, dst: FacetId): Promise<void>;
183
+ }
184
+ /**
185
+ * The `FacetHost` for a host that places no facets — the shape of every first
186
+ * integration, provided so hosts stop re-typing it. `start` refuses by name.
187
+ * `abort` returns, per its contract above: a host with nothing to kill returns.
188
+ * The storage operations refuse, because a call to either proves a facet once
189
+ * existed — which this host could not have placed.
190
+ */
191
+ export declare const noFacets: FacetHost;
192
+ /**
193
+ * The four ports. Each one is a seam workerd itself takes as a constructor
194
+ * input; a port that would exist only because our code is currently shaped
195
+ * badly is an invented seam and was rejected. Rejected, for the record:
196
+ * a transport port (one implementation per substrate, forever), a logger port
197
+ * (fail closed — errors throw, breakage surfaces on `onBroken`), a value-codec
198
+ * port (giving lanes different codecs would make the Node lane lie about the
199
+ * browser), and an addressing-strategy port (unnecessary once the package
200
+ * speaks facet ids).
201
+ *
202
+ * A fifth, `isolates?: IsolateHost`, was here from the scaffolding and Section 7b
203
+ * removed it. A Worker Loader is a **binding**, not a port: upstream builds it
204
+ * from `Global::WorkerLoader{channel}` alongside every other binding
205
+ * (`server/workerd-api.c++:748`) and it reaches an application through `env`,
206
+ * exactly as `DurableObjectNamespace` and `ctx.exports` already do here. A host
207
+ * constructs `WorkerLoader` over its own `IsolateChannelFactory` and puts it in
208
+ * `env`; the container never sees one. See `api/worker-loader.ts`'s header.
209
+ */
210
+ export type ActorPorts = {
211
+ sql: SqlDatabaseProvider;
212
+ /**
213
+ * Root containers only — facets have no alarm slot (§1.10).
214
+ *
215
+ * `AlarmScheduler.hooks(actorId)` is the implementation to put here: upstream
216
+ * builds one `AlarmScheduler` per namespace and gives each actor a
217
+ * three-line `ActorSqliteHooks` adapter over it (`server.c++:2325-2350`,
218
+ * `:3199-3219`), which is the same composition.
219
+ */
220
+ alarms: AlarmOutlet;
221
+ facets: FacetHost;
222
+ timer: Timer;
223
+ /**
224
+ * ← the global outbound `Fetcher` a Worker's `fetch` resolves
225
+ * (`api/global-scope.c++:1160`), which `container.globals.fetch` gates.
226
+ *
227
+ * Optional, and absence is upstream's `globalOutbound: null` posture rather
228
+ * than a missing port: a Worker configured that way has no ambient `fetch` at
229
+ * all, which is how Code Mode forces every I/O through connectors (§1.11).
230
+ * `fetch` then refuses by name instead of reaching a `fetch` this package does
231
+ * not own — an ungated one that works is the failure this layer exists to
232
+ * prevent.
233
+ */
234
+ fetch?: FetchPort;
235
+ };
236
+ /**
237
+ * The whole-tree facet state, which belongs to the root container and is shared
238
+ * by every container in one actor tree.
239
+ *
240
+ * Upstream keeps the same thing in the same place — "FacetTreeIndex for this
241
+ * actor. Only initialized on the root" (`server.c++:2680-2681`), reached from a
242
+ * facet by `root.ensureFacetTreeIndex()` (`:2697`) — and can do so with a plain
243
+ * reference because every facet of an actor is an object in one process. None of
244
+ * these methods can cross a worker boundary: `facets.get()` is synchronous all
245
+ * the way down, so `getId` has to answer without yielding. A facet in another
246
+ * worker cannot be handed this object and therefore cannot have facets of its
247
+ * own. Every host here
248
+ * now places in the parent's realm and passes the root's object straight through.
249
+ *
250
+ * It is an interface rather than a plain reference anyway, because a facet
251
+ * container is constructed on its own and the root's index is the one piece of
252
+ * state it cannot build for itself: ids are sequential across the whole tree. A
253
+ * host that does not supply one gets a facet that cannot have facets of its own
254
+ * and says so, rather than a per-parent counter that would collide the storage.
255
+ */
256
+ export interface FacetTree {
257
+ /** ← `FacetTreeIndex::getId`. Assigns on first sight, stable thereafter. */
258
+ getId(parent: FacetId, name: string): FacetId;
259
+ /** ← `FacetTreeIndex::forEachChild`, collected. Ordered by the child's UTF-8 name. */
260
+ children(parent: FacetId): readonly {
261
+ readonly id: FacetId;
262
+ readonly name: string;
263
+ }[];
264
+ /** ← `deleteDescendantStorage`'s recursion, as a list: descendants only, deepest first. */
265
+ descendants(id: FacetId): FacetId[];
266
+ /**
267
+ * Records the intent durably now, then removes `id` and its descendants after
268
+ * the current parent and descendant operations represented by `waitBeforeDelete`.
269
+ */
270
+ deleteSubtree(id: FacetId, waitBeforeDelete: Promise<unknown>): Promise<void>;
271
+ /** Copies the whole `src` subtree onto `dst`, minting `dst`'s children as it goes. */
272
+ copySubtree(src: FacetId, dst: FacetId): Promise<void>;
273
+ /** Runs one placement or abort after every earlier operation on the same stable facet id. */
274
+ runOperation(id: FacetId, operation: () => Promise<void>): void;
275
+ /** Snapshots the current operation tail for `id` and every indexed descendant. */
276
+ subtreeOperationBarrier(id: FacetId): Promise<void>;
277
+ /**
278
+ * Resolves once no queued deletion still covers `id`, which is what makes a
279
+ * facet re-created under a name that is still being deleted safe to start.
280
+ */
281
+ settled(id: FacetId): Promise<void>;
282
+ /** Every capability captured before an ancestor abort or delete goes stale here. */
283
+ readonly epochs: FacetReferenceEpochs;
284
+ /** ← boot. Carries out every deletion a previous session recorded and did not finish. */
285
+ recoverDeletions(): Promise<void>;
286
+ }
287
+ /**
288
+ * ← `IoContext::awaitIo`, as the one primitive a host needs in order to build a
289
+ * platform async primitive of its own.
290
+ */
291
+ export type ActorContainerOptions = {
292
+ /** The DurableObjectId name. */
293
+ id: string;
294
+ /**
295
+ * The namespace's unique key, upstream's `uniqueKey` configuration field
296
+ * (`server.c++:2919`, read from `config::Worker::DurableObjectNamespace::Durable`).
297
+ * `ActorIdFactoryImpl` derives its factory key as `SHA256(uniqueKey)` and an
298
+ * id as 16 bytes of base plus 16 bytes of `HMAC-SHA256(key, base)`.
299
+ *
300
+ * **The host must keep this stable forever.** `ctx.id` is
301
+ * `idFromName(options.id)` under this key, and the id names the actor's
302
+ * storage, so a key that changes across a restart changes every id and every
303
+ * actor loses its data. There is no default and it is not optional, because a
304
+ * default is exactly the shape that would let a host acquire this obligation
305
+ * without noticing it. This package cannot check the property for itself —
306
+ * nothing it can observe distinguishes "a new key" from "a new actor" — so
307
+ * this comment is the whole of the enforcement.
308
+ */
309
+ uniqueKey: string;
310
+ /** The `ctx.exports` class registry. Keys are the consumer's concern. */
311
+ exports: Record<string, unknown>;
312
+ env: unknown;
313
+ ports: ActorPorts;
314
+ /** Present when this container hosts a facet rather than a root. */
315
+ facet?: {
316
+ /** Root is 0, a direct child of the root is 1. `getDepth()` answers with it. */
317
+ depth: number;
318
+ /** This facet's own id, the one its parent allocated from the tree index. */
319
+ id: FacetId;
320
+ /** The root-owned tree this facet and every descendant share. */
321
+ tree: FacetTree;
322
+ };
323
+ };
324
+ /** The local entry proxy: data properties stay local; every method becomes one async event. */
325
+ export type ActorEntry<T extends object> = {
326
+ [K in keyof T]: T[K] extends (...args: infer Args) => infer Result ? (...args: Args) => Promise<Awaited<Result>> : T[K];
327
+ };
328
+ export interface ActorContainer {
329
+ /** Implements the workers-types interface. No `as unknown as` cast (§2.4). */
330
+ readonly state: DurableObjectState;
331
+ /**
332
+ * The actor tree this container belongs to, which a root builds for itself and
333
+ * a facet is handed.
334
+ *
335
+ * It is on the interface because the host is the only thing that can carry it
336
+ * from a parent to a child: `FacetHost.start` builds the nested container, and
337
+ * a facet that may have facets of its own needs the root's index rather than
338
+ * one of its own (see `FacetTree`). Upstream needs no equivalent because a
339
+ * facet reaches `root.ensureFacetTreeIndex()` through a plain reference
340
+ * (`server.c++:2697`), and every facet of an actor is an object in one process.
341
+ *
342
+ */
343
+ readonly facetTree: FacetTree;
344
+ /**
345
+ * Whether this container owns the synchronous actor slice on the JS stack.
346
+ * This is the narrow identity check a host loopback needs to call the raw
347
+ * instance instead of queueing behind the lock it already holds. It resolves
348
+ * no container and carries no state into continuations.
349
+ */
350
+ isCurrentSlice(): boolean;
351
+ /**
352
+ * Construct the instance under workerd's boot semantics: the input gate is
353
+ * held for the constructor's synchronous slice, and boot-time
354
+ * deletion-receipt replay precedes it.
355
+ */
356
+ start<T extends object>(construct: (ctx: DurableObjectState, env: unknown) => T): Promise<T>;
357
+ /**
358
+ * THE door for RPC targets. A proxy whose every method invocation is one
359
+ * gated event. This single wrapper is what replaces the serialised tail,
360
+ * all three dispatch tables, and the 33 hand-written exemptions.
361
+ *
362
+ * EVERY call queues, including one made while this actor is holding its own
363
+ * lock across an await. That is §1.2's whole content and the suite pins it: a
364
+ * second event posted while a storage await holds the gate must not interleave,
365
+ * and a door that reused the held lock could not tell that event apart from a
366
+ * call the actor made to itself. Telling them apart needs to know WHO is
367
+ * calling, which is a host's question rather than a container's — see the
368
+ * extension host's `loopbackStub`, where an actor reaching its own
369
+ * `DurableObjectNamespace` binding skips this door entirely because the lock it
370
+ * would take is the one it is already holding.
371
+ */
372
+ entry<T extends object>(target: T): ActorEntry<T>;
373
+ /**
374
+ * The door for events that are not method calls — one WebSocket frame, one
375
+ * host-originated callback. Upstream: `IoContext::run`.
376
+ */
377
+ run<T>(event: () => T | PromiseLike<T>): Promise<T>;
378
+ /**
379
+ * ← `IoContext::awaitIo`. The form a HOST-PROVIDED async primitive must take,
380
+ * and the only gate primitive this package makes public.
381
+ *
382
+ * Every platform async thing an application can await — `scheduler.wait`, a
383
+ * `fetch`, a WebSocket round trip — is an io-context primitive upstream, which
384
+ * is why "resuming from an await re-enters the isolate with a fresh input
385
+ * lock" needs saying nowhere in workerd: there is no other kind of await.
386
+ * There is here. A raw `setTimeout` resolves a promise the runtime does not
387
+ * own, the application's continuation resumes with an empty invocation stack,
388
+ * and its next `ctx.storage` call throws `no input lock available in this
389
+ * context` — the README's divergence 147. Wrapping the promise in this makes
390
+ * the continuation resume inside a gated slice, which is what upstream's does.
391
+ *
392
+ * It releases the input gate for the duration, per §1.3, so the actor stays
393
+ * re-entrant while it waits. The holding form,
394
+ * `IoContext::awaitIoWithInputLock`, is deliberately NOT public: that one is
395
+ * the transaction boundary of §1.7.1, it belongs to the four async storage
396
+ * calls, and a host holding it by hand is the serialised tail growing back.
397
+ */
398
+ awaitIo<T>(promise: Promise<T>): Promise<T>;
399
+ /**
400
+ * ← `ServiceWorkerGlobalScope`, the async-primitive half: this actor's
401
+ * `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval`, `fetch` and
402
+ * `scheduler` (`api/global-scope.ts`).
403
+ *
404
+ * **This is what "own the primitives in the worker global" means, and the
405
+ * ownership is the point.** `awaitIo` above is the primitive a host needs to
406
+ * build one of these; this is the set already built, so a host installs rather
407
+ * than reimplements the surface.
408
+ *
409
+ * **One scope per actor, installed lexically, and never resolved from an
410
+ * ambient.** Upstream's globals read `IoContext::current()` because acquisition
411
+ * is structural there; this one holds its context. A host puts it where its
412
+ * actor's code reads it — `globalThis` for a class in the worker's own module
413
+ * graph, a module-scoped binding for a class that arrived as a
414
+ * dynamically-loaded Worker source, which is upstream's own arrangement (§1.11:
415
+ * a dynamic Worker has its own global scope bound to its own context). What
416
+ * happens when a facet reaches past its binding to a parent's scope is
417
+ * `ActorGlobalScope`'s `requireOwnSlice`.
418
+ */
419
+ readonly globals: ActorGlobalScope;
420
+ /**
421
+ * ← `WebSocket::accept()` / `state.acceptWebSocket()`
422
+ * (`api/web-socket.c++:133`, `:426`), whose gating is `api/web-socket.ts`.
423
+ *
424
+ * Separate from `globals` because accepting is an act rather than a binding:
425
+ * the critical section is captured at THIS call, so a socket accepted inside
426
+ * `blockConcurrencyWhile` delivers its frames inside that section (§1.8).
427
+ */
428
+ acceptWebSocket(socket: RawWebSocket): AcceptedWebSocket;
429
+ /**
430
+ * ← `WorkerInterface::runAlarm(scheduledTime, retryCount)`
431
+ * (`io/worker-interface.h:107`), which is what `AlarmScheduler` calls and what
432
+ * `ServiceWorkerGlobalScope::runAlarm` answers. Strictly serialised (§1.8).
433
+ *
434
+ * It reports rather than throws, because the two bits the scheduler's ladder
435
+ * turns on — retry, and whether the retry counts against the limit — are not
436
+ * derivable from "the promise rejected".
437
+ *
438
+ * `retryCount` reaches the handler as `AlarmInvocationInfo`. It is the
439
+ * scheduler's `countedRetry` for this alarm, so the container takes it as an
440
+ * argument exactly as upstream's `runAlarm` does.
441
+ */
442
+ deliverAlarm(scheduledTime: number, retryCount: number): Promise<AlarmResult>;
443
+ /**
444
+ * ← `WorkerInterface::abandonAlarm` (`io/worker-interface.h:114`), forwarded to
445
+ * `ActorSqlite::abandonAlarm` (`io/actor-sqlite.c++:1039-1060`).
446
+ *
447
+ * Called when the scheduler has given up retrying, so the actor clears its own
448
+ * alarm state and `getAlarm()` stops reporting a time that will never fire.
449
+ * Answers the actor's stored alarm time when it differs from `scheduledTime` —
450
+ * meaning the application set a different one — and null when the alarm was
451
+ * cleared or there was none.
452
+ */
453
+ abandonAlarm(scheduledTime: number): Promise<number | null>;
454
+ /**
455
+ * Output-gate wait for outbound sends that do not ride an `entry()` reply.
456
+ * A broadcast path calls this before each frame (decision 5).
457
+ */
458
+ waitOutputLocks(): Promise<void>;
459
+ /** For the host's idle check — today's `drainWaitUntil`. */
460
+ drainWaitUntil(): Promise<void>;
461
+ /**
462
+ * ← `WorkerdApi::compileGlobals`'s `Global::WorkerLoader` arm
463
+ * (`server/workerd-api.c++:748-752`), which is the step that turns a configured
464
+ * loader channel into the JS binding an application finds in `env`.
465
+ *
466
+ * **The one binding the runtime has to construct, and the reason is the
467
+ * context.** Upstream's `WorkerLoader` holds a channel number and a
468
+ * validation mode and nothing else, because `get()` and `load()` read
469
+ * `IoContext::current()` when they are called. This package deliberately has no
470
+ * ambient of any kind, so the context is a constructor input — and it must be
471
+ * *this* container's, since `makeReentryCallback` inherits the critical section
472
+ * a `blockConcurrencyWhile` is holding (decision 13). `IoContext` is not
473
+ * exported, on purpose (see `src/index.ts`'s header), so this method is how a
474
+ * host gets a loader bound to the right one.
475
+ *
476
+ * The host still owns the name: assign the result onto the `env` object it
477
+ * passed in, which is what upstream's binding compilation does one layer down.
478
+ * A container whose host never calls this simply has no loader binding, exactly
479
+ * as a Worker with no `workerLoader` in its config does.
480
+ */
481
+ workerLoader(channel: IsolateChannelFactory, options: WorkerLoaderOptions): WorkerLoader;
482
+ /**
483
+ * Rejects when either gate breaks, or when a facet the parent did not abort
484
+ * breaks (decisions 6 and 14). The host consumes this: terminate the worker,
485
+ * respawn on the next event.
486
+ */
487
+ readonly onBroken: Promise<never>;
488
+ /** The programmatic `ctx.abort()` path. */
489
+ abort(reason?: unknown): void;
490
+ }
491
+ /**
492
+ * A facet has no alarm slot, and this is where that becomes visible.
493
+ *
494
+ * `server.c++:2864-2877` installs alarm hooks only `if (parent == kj::none)` and
495
+ * gives a facet `ActorSqlite::Hooks::getDefaultHooks()`, whose `scheduleRun`
496
+ * throws — so on workerd a `setAlarm()` inside a facet appears to succeed and
497
+ * then breaks the whole actor asynchronously, which is open bug
498
+ * https://github.com/cloudflare/workerd/issues/6810. This runtime refuses at the
499
+ * call instead, as recorded in §2.7. A deliberate semantic
500
+ * divergence: the observable behaviour is a synchronous throw naming the facet
501
+ * where workerd's is a destroyed actor three turns later.
502
+ */
503
+ export declare const FACET_ALARM_UNIMPLEMENTED_MESSAGE: string;
504
+ /**
505
+ * ← the `kj::File` `FacetTreeIndex` is constructed over, backed by one BLOB.
506
+ *
507
+ * `datasync()` is a no-op and that is a property of the substrate rather than a
508
+ * shortcut: nothing else ever opens a transaction on this connection, so every
509
+ * statement below is its own implicit SQLite transaction and is durable by the
510
+ * time `exec` returns. The read-modify-write is O(file) per append, which is
511
+ * what upstream's own bound makes affordable — the format is four bytes plus a
512
+ * name per facet and there can be at most 65,535 facets over an actor's whole
513
+ * lifetime (`facet-tree-index.h:19-22`).
514
+ */
515
+ export declare function newDatabaseIndexFile(db: SqlDatabase): IndexFile;
516
+ /**
517
+ * Builds one actor: the two gates, the `IoContext` over them, the storage engine
518
+ * over the actor's database, the facet tree, the id factory, and the `api/`
519
+ * classes on top.
520
+ *
521
+ * Asynchronous because `SqlDatabaseProvider.open` is — see this file's header for
522
+ * why that surfaces here rather than being hidden behind a lazily-opening
523
+ * `state`.
524
+ */
525
+ export declare function createActorContainer(options: ActorContainerOptions): Promise<ActorContainer>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * ← workerd `src/workerd/server/actor-id-impl.{h,c++}`
3
+ *
4
+ * The implementation behind `io/actor-id.ts`'s two interfaces, which that file's
5
+ * header names as Section 6's problem: "upstream's is a keyed SHA-256
6
+ * construction … a faithful port of it needs a synchronous digest, which the
7
+ * browser does not expose."
8
+ *
9
+ * **What upstream computes.** The factory's key is `SHA256(uniqueKey)`, where
10
+ * `uniqueKey` is the namespace's configured string (`workerd-api.c++:675`).
11
+ * An id is 32 bytes in two halves: a 16-byte base and the first 16 bytes of
12
+ * `HMAC-SHA256(key, base)` — `computeMac` (`actor-id-impl.c++:115-125`), which
13
+ * writes a full 32-byte HMAC into a 48-byte working buffer of which only the
14
+ * first 32 bytes ever become the id. `idFromName` derives the base from
15
+ * `HMAC-SHA256(key, name)` (`:71-83`); `newUniqueId` draws it from the entropy
16
+ * source (`:48-69`); `idFromString` takes it from the supplied hex and refuses
17
+ * the string unless the MAC it recomputes matches the half that came with it
18
+ * (`:85-100`). `toString` is `kj::encodeHex` of the 32 bytes.
19
+ *
20
+ * **The digest is written out; the bytes are unchanged.** Every method on
21
+ * `ActorIdFactory` is synchronous (`io/actor-id.h:66-71`) and no browser API
22
+ * offers a synchronous digest, so `server/sha256.ts` supplies SHA-256 and
23
+ * HMAC-SHA-256 in place of BoringSSL. It computes what FIPS 180-4 and RFC 2104
24
+ * specify, so this is decision 16's **substrate** divergence and not a semantic
25
+ * one: `idFromName` here produces the identical 64 hex digits workerd produces
26
+ * from the same unique key, which keeps workerd usable as an oracle for ids.
27
+ *
28
+ * That equality is why the construction is ported whole rather than narrowed.
29
+ * The reduced threat model would have tolerated much less — ids never cross the
30
+ * host boundary, there is no colo, no jurisdiction routing and no second worker
31
+ * re-deriving an id from the same key, so nothing here forges an id — but only
32
+ * two of the contract's properties are cheap to satisfy any other way. The other
33
+ * two are not: `idFromName` must be **stable forever**, because the id names the
34
+ * actor's storage and a name that hashed differently after a restart loses its
35
+ * data; and `idFromString` must **refuse a string this namespace did not mint**,
36
+ * which is a decision only the keyed MAC half can make. A narrower construction
37
+ * would have satisfied both and cost the oracle, turning every future question
38
+ * about an id into original research on a bespoke artifact — the failure the
39
+ * "Porting philosophy" section describes.
40
+ *
41
+ * **`isPredictableModeForTest()` is absent** (`actor-id-impl.c++:59-62`). It is a
42
+ * `util/thread-scopes.h` process-global test hack with no port here, it makes
43
+ * `newUniqueId` return a counter, and `actor-id-impl-test.c++` does not use it.
44
+ * Its body is also wrong upstream: `kj::arrayPtr(id).slice(counter)` slices by
45
+ * the counter's *value*, which is a no-op for every counter it can legitimately
46
+ * reach and out of bounds once the counter passes the buffer's 48 bytes.
47
+ *
48
+ * Spec: §1.10 in docs/decisions.md.
49
+ */
50
+ import type { ActorId, ActorIdFactory } from "../io/actor-id.js";
51
+ /**
52
+ * ← `JSG_REQUIRE(jurisdiction == kj::none, Error, …)` (`actor-id-impl.c++:50-51`,
53
+ * `:108`).
54
+ *
55
+ * Verbatim, "in workerd" and all: the conformance suite runs one assertion
56
+ * against both runtimes, so a reworded message would be a difference where there
57
+ * is none.
58
+ */
59
+ export declare const JURISDICTION_UNIMPLEMENTED_MESSAGE = "Jurisdiction restrictions are not implemented in workerd.";
60
+ /** ← the first `JSG_REQUIRE` in `idFromString` (`actor-id-impl.c++:87-89`). */
61
+ export declare const INVALID_ACTOR_ID_MESSAGE = "Invalid Durable Object ID: must be 64 hex digits";
62
+ /** ← the second `JSG_REQUIRE` in `idFromString` (`actor-id-impl.c++:96-97`). */
63
+ export declare const WRONG_NAMESPACE_ACTOR_ID_MESSAGE = "Durable Object ID is not valid for this namespace.";
64
+ /**
65
+ * Upstream `kj::downcast`s the argument of `equals` (`actor-id-impl.c++:33`),
66
+ * which asserts in a debug build and is undefined in a release one. There is one
67
+ * `ActorId` implementation here, so a correct caller cannot reach this; it fails
68
+ * closed rather than comparing something meaningless.
69
+ */
70
+ export declare const FOREIGN_ACTOR_ID_MESSAGE = "This actor id was not created by this runtime, so it cannot be compared with one that was.";
71
+ /** ← `ActorIdFactoryImpl::ActorIdImpl` (`actor-id-impl.h:13-30`). */
72
+ export declare class ActorIdImpl implements ActorId {
73
+ #private;
74
+ /**
75
+ * ← the constructor (`actor-id-impl.c++:14-18`). Its parameter is declared
76
+ * `const kj::byte idParam[SHA256_DIGEST_LENGTH]` and its body `memcpy`s
77
+ * exactly `sizeof(id)` bytes, so a caller may hand over the longer working
78
+ * buffer and only its first 32 bytes become the id. The copy is upstream's
79
+ * too — a later write to the caller's buffer is not seen here.
80
+ */
81
+ constructor(id: Uint8Array, name: string | undefined);
82
+ /** ← `toString()` — `kj::encodeHex`, which is lowercase (`actor-id-impl.c++:20-22`). */
83
+ toString(): string;
84
+ /** ← `getName()` (`actor-id-impl.c++:24-26`). */
85
+ getName(): string | undefined;
86
+ /** ← `getJurisdiction()`, which is unconditionally none (`actor-id-impl.c++:28-30`). */
87
+ getJurisdiction(): string | undefined;
88
+ /**
89
+ * ← `equals()` (`actor-id-impl.c++:32-34`). The id bytes only — the name is
90
+ * deliberately not part of identity, which is what upstream's own table
91
+ * asserts by giving two equal ids different names.
92
+ */
93
+ equals(other: ActorId): boolean;
94
+ /** ← `clearName()` (`actor-id-impl.h:23-25`). Not JS-visible; `server/` calls it. */
95
+ clearName(): void;
96
+ }
97
+ /** ← `ActorIdFactoryImpl` (`actor-id-impl.h:8-46`). */
98
+ export declare class ActorIdFactoryImpl implements ActorIdFactory {
99
+ #private;
100
+ /**
101
+ * ← both constructors (`actor-id-impl.c++:40-46`). C++ overloads on the
102
+ * parameter type; one constructor branching on it is the same two behaviours.
103
+ * The string form is the namespace's configured `uniqueKey`
104
+ * (`workerd-api.c++:675`, `:680`); the byte form exists for
105
+ * `cloneWithJurisdiction`, which passes the already-derived key.
106
+ */
107
+ constructor(uniqueKey: string | Uint8Array);
108
+ /** ← `newUniqueId()` (`actor-id-impl.c++:48-69`). */
109
+ newUniqueId(jurisdiction: string | undefined): ActorId;
110
+ /** ← `idFromName()` (`actor-id-impl.c++:71-83`). */
111
+ idFromName(name: string): ActorId;
112
+ /** ← `idFromString()` (`actor-id-impl.c++:85-100`). */
113
+ idFromString(str: string): ActorId;
114
+ /** ← `cloneWithJurisdiction()` (`actor-id-impl.c++:102-109`). */
115
+ cloneWithJurisdiction(maybeJurisdiction: string | undefined): ActorIdFactory;
116
+ /** ← `matchesJurisdiction()`, which is unconditionally true (`actor-id-impl.c++:111-113`). */
117
+ matchesJurisdiction(_id: ActorId): boolean;
118
+ }