iterate 0.3.0 → 0.4.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.
- package/README.md +164 -82
- package/dist/{next/api.d.ts → api.d.ts} +197 -33
- package/dist/{next/app-server.d.ts → app-server.d.ts} +7 -0
- package/dist/{next/app-server.mjs → app-server.mjs} +17 -15
- package/dist/app-server.mjs.map +1 -0
- package/dist/{next/app-session.mjs → app-session.mjs} +12 -15
- package/dist/app-session.mjs.map +1 -0
- package/dist/{next/app.mjs → app.mjs} +53 -14
- package/dist/app.mjs.map +1 -0
- package/dist/{next/client → client}/live-state.d.ts +11 -11
- package/dist/client/oauth.d.ts +17 -0
- package/dist/{next/client → client}/react.d.ts +10 -42
- package/dist/{next/client → client}/socket.d.ts +1 -0
- package/dist/client.mjs +156 -4
- package/dist/client.mjs.map +1 -0
- package/dist/{next/expression.d.ts → expression.d.ts} +11 -69
- package/dist/{next/expression.mjs → expression.mjs} +11 -109
- package/dist/expression.mjs.map +1 -0
- package/dist/lib-BWr-5mFO.mjs +36 -0
- package/dist/lib-BWr-5mFO.mjs.map +1 -0
- package/dist/{next/lib.d.ts → lib.d.ts} +16 -2
- package/dist/{next/lib.mjs → lib.mjs} +32 -3
- package/dist/lib.mjs.map +1 -0
- package/dist/node.d.ts +15 -3
- package/dist/node.mjs +36 -174
- package/dist/node.mjs.map +1 -1
- package/dist/{next/oauth-scopes.mjs → oauth-scopes.mjs} +1 -1
- package/dist/oauth-scopes.mjs.map +1 -0
- package/dist/{next/oauth.mjs → oauth.mjs} +14 -2
- package/dist/oauth.mjs.map +1 -0
- package/dist/principal.d.ts +8 -0
- package/dist/principal.mjs +8 -0
- package/dist/principal.mjs.map +1 -0
- package/dist/project-ingress.d.ts +58 -0
- package/dist/project-ingress.mjs +104 -0
- package/dist/project-ingress.mjs.map +1 -0
- package/dist/{next/react.mjs → react.mjs} +12 -12
- package/dist/react.mjs.map +1 -0
- package/dist/sdk/auth.d.ts +25 -0
- package/dist/sdk/index.d.ts +155 -0
- package/dist/sdk/record-pipelined-steps.d.ts +19 -0
- package/dist/sdk.mjs +245 -2
- package/dist/sdk.mjs.map +1 -0
- package/dist/{next/stream → stream}/processor.d.ts +28 -23
- package/dist/{next/stream → stream}/processor.mjs +48 -25
- package/dist/stream/processor.mjs.map +1 -0
- package/dist/{next/stream → stream}/run.d.ts +9 -6
- package/dist/{next/stream → stream}/run.mjs +13 -8
- package/dist/stream/run.mjs.map +1 -0
- package/dist/stream/test-support.d.ts +45 -0
- package/dist/stream/test-support.mjs +196 -0
- package/dist/stream/test-support.mjs.map +1 -0
- package/package.json +65 -219
- package/THIRD_PARTY_NOTICES.md +0 -55
- package/bin/iterate.js +0 -94
- package/dist/api-url-B6404M82.mjs +0 -17
- package/dist/api-url-B6404M82.mjs.map +0 -1
- package/dist/app-ref-BipL0feU.mjs +0 -35
- package/dist/app-ref-BipL0feU.mjs.map +0 -1
- package/dist/app-ref-C1CrgXqX.mjs +0 -7
- package/dist/app-ref-C1CrgXqX.mjs.map +0 -1
- package/dist/app-ref-DYai_om1.mjs +0 -7
- package/dist/app-ref-DYai_om1.mjs.map +0 -1
- package/dist/cli-D0c-pDL_.mjs +0 -1010
- package/dist/cli-D0c-pDL_.mjs.map +0 -1
- package/dist/client.d.ts +0 -3
- package/dist/cloudflare-BTm90gQ4.mjs +0 -951
- package/dist/cloudflare-BTm90gQ4.mjs.map +0 -1
- package/dist/contract-s4FW4eES.mjs +0 -309
- package/dist/contract-s4FW4eES.mjs.map +0 -1
- package/dist/document-review/index.d.ts +0 -5
- package/dist/document-review/types.d.ts +0 -107
- package/dist/document-review.mjs +0 -7015
- package/dist/document-review.mjs.map +0 -1
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs +0 -205
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +0 -1
- package/dist/idempotency-DleloJNt.mjs +0 -28
- package/dist/idempotency-DleloJNt.mjs.map +0 -1
- package/dist/index.d.mts +0 -5
- package/dist/index.mjs +0 -8
- package/dist/index.mjs.map +0 -1
- package/dist/itx/api-url.d.ts +0 -6
- package/dist/itx/itx-node-client.d.ts +0 -65
- package/dist/itx/itx-session.d.ts +0 -215
- package/dist/itx/owned-rpc-session.d.ts +0 -14
- package/dist/itx/query-client.d.ts +0 -10
- package/dist/itx-api.generated.d.ts +0 -6195
- package/dist/itx-session-sjud8GiT.mjs +0 -534
- package/dist/itx-session-sjud8GiT.mjs.map +0 -1
- package/dist/live-state-BJNqOwFw.mjs +0 -299
- package/dist/live-state-BJNqOwFw.mjs.map +0 -1
- package/dist/next/app-server.mjs.map +0 -1
- package/dist/next/app-session.mjs.map +0 -1
- package/dist/next/app.mjs.map +0 -1
- package/dist/next/client/oauth.d.ts +0 -12
- package/dist/next/client.mjs +0 -156
- package/dist/next/client.mjs.map +0 -1
- package/dist/next/expression.mjs.map +0 -1
- package/dist/next/lib.mjs.map +0 -1
- package/dist/next/oauth-scopes.mjs.map +0 -1
- package/dist/next/oauth.mjs.map +0 -1
- package/dist/next/principal.d.ts +0 -64
- package/dist/next/principal.mjs +0 -98
- package/dist/next/principal.mjs.map +0 -1
- package/dist/next/project-ingress.d.ts +0 -37
- package/dist/next/project-ingress.mjs +0 -75
- package/dist/next/project-ingress.mjs.map +0 -1
- package/dist/next/react.mjs.map +0 -1
- package/dist/next/sdk/auth.d.ts +0 -5
- package/dist/next/sdk/index.d.ts +0 -112
- package/dist/next/sdk.mjs +0 -139
- package/dist/next/sdk.mjs.map +0 -1
- package/dist/next/stream/processor.mjs.map +0 -1
- package/dist/next/stream/run.mjs.map +0 -1
- package/dist/next-node.d.ts +0 -15
- package/dist/next-node.mjs +0 -51
- package/dist/next-node.mjs.map +0 -1
- package/dist/processor-host-capabilities-BMFH3KTM.mjs +0 -56
- package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +0 -1
- package/dist/processors/cloudflare.d.ts +0 -3
- package/dist/processors/durable-object-processor-durability.d.ts +0 -79
- package/dist/processors/event-consumption-metrics.d.ts +0 -82
- package/dist/processors/idempotency.d.ts +0 -13
- package/dist/processors/index.d.ts +0 -12
- package/dist/processors/processor-contracts.d.ts +0 -342
- package/dist/processors/processor-facet.d.ts +0 -186
- package/dist/processors/processor-host-capabilities.d.ts +0 -60
- package/dist/processors/prompt-sections.d.ts +0 -17
- package/dist/processors/rpc-types.d.ts +0 -515
- package/dist/processors/schemas.d.ts +0 -102
- package/dist/processors/stream-handle.d.ts +0 -45
- package/dist/processors/stream-processor-keepalive.d.ts +0 -95
- package/dist/processors/stream-processor-registry.d.ts +0 -233
- package/dist/processors/stream-processor-runner.d.ts +0 -289
- package/dist/processors/stream-processor.d.ts +0 -339
- package/dist/processors/stream-runtime-metrics.d.ts +0 -107
- package/dist/processors/testing.d.ts +0 -302
- package/dist/processors-BoNyeBfQ.mjs +0 -10
- package/dist/processors-BoNyeBfQ.mjs.map +0 -1
- package/dist/processors-cloudflare.mjs +0 -3
- package/dist/processors-testing.mjs +0 -435
- package/dist/processors-testing.mjs.map +0 -1
- package/dist/processors.mjs +0 -52
- package/dist/processors.mjs.map +0 -1
- package/dist/protocol-DnK_f2m6.mjs +0 -251
- package/dist/protocol-DnK_f2m6.mjs.map +0 -1
- package/dist/sdk/capnweb/index.d.ts +0 -2
- package/dist/sdk/capnweb/live-state/compact.d.ts +0 -5
- package/dist/sdk/capnweb/live-state/diff.d.ts +0 -41
- package/dist/sdk/capnweb/live-state/engine.d.ts +0 -44
- package/dist/sdk/capnweb/live-state/index.d.ts +0 -41
- package/dist/sdk/capnweb/live-state/protocol.d.ts +0 -87
- package/dist/sdk/capnweb/live-state/retain.d.ts +0 -23
- package/dist/sdk/capnweb/live-state/store.d.ts +0 -20
- package/dist/sdk/capnweb/live-state/types.d.ts +0 -11
- package/dist/sdk/capnweb/react.d.ts +0 -45
- package/dist/sdk/capnweb/react.mjs +0 -316
- package/dist/sdk/capnweb/react.mjs.map +0 -1
- package/dist/sdk/capnweb.mjs +0 -4
- package/dist/sdk/itx/react.d.ts +0 -191
- package/dist/sdk/itx/react.mjs +0 -383
- package/dist/sdk/itx/react.mjs.map +0 -1
- package/dist/sdk-DMB-IM11.mjs +0 -933
- package/dist/sdk-DMB-IM11.mjs.map +0 -1
- package/dist/sdk.d.ts +0 -339
- package/dist/serve-itx.d.ts +0 -46
- package/dist/starter-apps/flake-dashboard/app-ref.d.ts +0 -31
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs +0 -1055
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/flake-dashboard/contract.d.ts +0 -4839
- package/dist/starter-apps/flake-dashboard/contract.mjs +0 -2
- package/dist/starter-apps/flake-dashboard/index.d.ts +0 -17
- package/dist/starter-apps/flake-dashboard/index.mjs +0 -56
- package/dist/starter-apps/flake-dashboard/index.mjs.map +0 -1
- package/dist/starter-apps/flake-dashboard/worker.d.ts +0 -4607
- package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +0 -8914
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs +0 -17987
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/github-ai-linter/contract.d.ts +0 -9193
- package/dist/starter-apps/github-ai-linter/index.d.ts +0 -10
- package/dist/starter-apps/github-ai-linter/index.mjs +0 -36
- package/dist/starter-apps/github-ai-linter/index.mjs.map +0 -1
- package/dist/starter-apps/github-ai-linter/prompt.d.ts +0 -13
- package/dist/starter-apps/github-ai-linter/review-bot.d.ts +0 -808
- package/dist/starter-apps/github-ai-linter/rules.d.ts +0 -34
- package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +0 -19
- package/dist/starter-apps/github-ai-linter/worker.d.ts +0 -19
- package/dist/starter-apps/github-ai-linter/worker.mjs +0 -947
- package/dist/starter-apps/github-ai-linter/worker.mjs.map +0 -1
- package/dist/starter-apps/guestbook/app-ref.d.ts +0 -27
- package/dist/starter-apps/guestbook/client.d.ts +0 -7
- package/dist/starter-apps/guestbook/client.mjs +0 -59
- package/dist/starter-apps/guestbook/configured-worker.mjs +0 -205
- package/dist/starter-apps/guestbook/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/guestbook/index.d.ts +0 -9
- package/dist/starter-apps/guestbook/index.mjs +0 -31
- package/dist/starter-apps/guestbook/index.mjs.map +0 -1
- package/dist/starter-apps/guestbook/processor.d.ts +0 -2267
- package/dist/starter-apps/guestbook/worker.d.ts +0 -26
- package/dist/starter-apps/guestbook/worker.mjs +0 -191
- package/dist/starter-apps/guestbook/worker.mjs.map +0 -1
- package/dist/starter-apps/media/configured-worker.mjs +0 -577
- package/dist/starter-apps/media/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/media/index.mjs +0 -36
- package/dist/starter-apps/media/index.mjs.map +0 -1
- package/dist/starter-apps/media/ref.mjs +0 -20
- package/dist/starter-apps/media/ref.mjs.map +0 -1
- package/dist/starter-apps/media/worker.mjs +0 -579
- package/dist/starter-apps/media/worker.mjs.map +0 -1
- package/dist/starter-apps/notes/configured-worker.mjs +0 -6134
- package/dist/starter-apps/notes/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/notes/index.mjs +0 -23
- package/dist/starter-apps/notes/index.mjs.map +0 -1
- package/dist/starter-apps/notes/ref.mjs +0 -21
- package/dist/starter-apps/notes/ref.mjs.map +0 -1
- package/dist/starter-apps/notes/worker.mjs +0 -427
- package/dist/starter-apps/notes/worker.mjs.map +0 -1
- package/dist/starter-apps/todo/client.mjs +0 -59
- package/dist/starter-apps/todo/configured-worker.mjs +0 -2864
- package/dist/starter-apps/todo/configured-worker.mjs.map +0 -1
- package/dist/starter-apps/todo/index.d.ts +0 -8
- package/dist/starter-apps/todo/index.mjs +0 -29
- package/dist/starter-apps/todo/index.mjs.map +0 -1
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs +0 -2082
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +0 -1
- package/dist/usingCtx-mZx5nsAW.mjs +0 -11800
- package/dist/usingCtx-mZx5nsAW.mjs.map +0 -1
- package/dist/worker-ref-DZxPDmb_.mjs +0 -390
- package/dist/worker-ref-DZxPDmb_.mjs.map +0 -1
- package/dist/worker.d.mts +0 -33
- package/dist/worker.mjs +0 -18
- package/dist/worker.mjs.map +0 -1
- package/menubar/Iterate.entitlements +0 -12
- package/menubar/Iterate.swift +0 -914
- package/menubar/IterateIcon.swift +0 -145
- package/menubar/README.md +0 -28
- package/menubar/build-menubar-app.sh +0 -59
- /package/dist/{next/api.mjs → api.mjs} +0 -0
- /package/dist/{next/app-session.d.ts → app-session.d.ts} +0 -0
- /package/dist/{next/app.d.ts → app.d.ts} +0 -0
- /package/dist/{next/oauth-scopes.d.ts → oauth-scopes.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"processor.mjs","names":["#contract","#stream","#storage","#waitUntilProcessedWaiters","#liveState","#reducedState","#reducedThroughOffset","#staleCheckpoint","#reducedThroughPushedHead","#pushedThroughOffset","#runOnSerialChain","#rereduceIfVersionChanged","#reduceAndCommitEventBatch","#serialBatchChain","#latchedRefusal","#validateNormalizeAndReduce","#writeCheckpointOrLatch","#resolveWaitUntilProcessedWaiters","#reduceAndProcessEvent","#lastBatchAtHead","#backgroundWorkInFlight","#claim","#revivesWhileBusy","#claimChain","#reduceOrKeep","#runInBackground","#sql","#liveStateSink","#liveStateKey","#state","#lastSerializedState","#liveStateRev","#liveStateDeltaAppendChain"],"sources":["../../../src/next/stream/processor.ts"],"sourcesContent":["import type { SqlStorageValue } from \"@cloudflare/workers-types\";\n// stream/processor.ts — THE PROCESSOR: `StreamProcessor`, the PURE class an author writes (a\n// contract and three hooks, no constructor arguments, no storage, no stream — a unit test constructs\n// it with `new`), and `ProcessorEngine`, which drives ONE such instance against a stream and a\n// storage; the SDK's `StreamProcessorDurableObject` builds one per hosted facet. The author surface\n// mirrors apps/os so processors port both ways. Node-testable; bundled into every loaded isolate as\n// `processor.js` (sdk/index.ts), so nothing here imports cloudflare:workers. Four concepts ride with it:\n// events — `StreamEventInput` / `StreamEvent`, the envelope, and the idempotency rules\n// reduce checkpoint — `ReduceCheckpointTable`, THE ONE spelling of a persisted reduce checkpoint\n// live state — `LiveState`, one value, its revision chain and the diff→emit delta\n// processor contract — `defineProcessorContract`, the zod contract helper (zod rides the SDK bundle)\n//\n// THE CONCURRENCY CONTRACT:\n// 1. ONE SERIAL CHAIN per processor — batches never interleave.\n// 2. ONE EVENT AT A TIME inside a batch: this event's `blockProcessorWhile` work completes\n// before the next event's `processEvent` starts (a FIFO chain, awaited per event).\n// 3. `runInBackground` work is deliberately NOT awaited — it may overtake later events; it is\n// a droppable attempt whose outcome must be recoverable from state at the next at-head pass —\n// and that pass is OWED: while any attempt is in flight the engine holds a CLAIM on its\n// context's alarm (\"come back by T\"), released when the last attempt settles; a host that dies\n// mid-attempt is revived by the alarm (`revive()`: catch up, run the at-head pass), and an\n// attempt still in flight at a revive claims again, later each time, so a hung attempt costs a\n// few wakes an hour and never a loop.\n// 4. ONE DURABLE COMMIT PER BATCH, after every event's blocking work settled — persist BEFORE\n// advancing past the last DURABLE offset. A failed batch persists nothing, retried whole.\n// 5. The at-head pass: the last consumable event of a batch that reaches the stream head\n// carries `delivery.caughtUp: true`; a batch that reaches the head without one gets a single\n// extra `processEvent({ event: null, delivery: { caughtUp: true } })` call.\n//\n// DELIVERY IS PUSH-FIRST with a SCANNED-RANGE PROOF: the cursor advances on the RANGE a push\n// carries, never by counting events, so ephemeral holes, consumes-filters and reboot gaps are all\n// the same non-event. A non-contiguous push triggers GAP REPAIR: read durable rows from the own\n// cursor up to the push start, then process the push. Ephemeral events ride pushes ONLY (an\n// ephemeral missed while a facet rebuilds is gone by design) and NEVER trigger a checkpoint write,\n// so a pure-ephemeral flood costs this class ZERO storage writes.\n//\n// `reduce` is a PURE reduce (new object out, its arguments immutable), CHECKPOINTED\n// (`ReduceCheckpointTable` below) with the offset and contract version it was reduced under; bumping\n// `contract.version` re-reduces from offset 0 through `reduce` only, never re-running side effects —\n// over durable rows only, which is why durable product truth must never derive from an ephemeral.\n\nimport { z } from \"zod\";\nimport { reportIssue, jsonEqual, codedError, diff } from \"../lib.ts\";\n\n/** What a processor declares: its checkpoint slug and reducer version, what it consumes and emits,\n * and its initial state (`defineProcessorContract` below builds one from zod schemas). */\nexport type ProcessorContract<State = unknown> = {\n slug: string;\n /** Bumping this re-reduces state from offset 0 (reduce only — side effects never re-run). */\n version: string;\n description?: string;\n /** What it reacts to: type strings, or \"*\" for every DURABLE event. Ephemeral events are\n * delivered ONLY when their type is named here — `\"*\"` never sweeps them. */\n consumes: readonly string[];\n /** What its `append` is allowed to emit. */\n emits: readonly string[];\n /** The schema-initial state (\"{} with every field defaulted\" for zod contracts). */\n initialState: () => State;\n /** The zod payload schema for a consumed event type (owned or a dep's), or undefined if the type\n * is unknown or the contract declares no `events` catalog. The engine validates a consumed event's\n * payload against it before reducing (a malformed payload for a KNOWN event is skipped, never\n * folded). Present on `defineProcessorContract` contracts; a hand-built core contract omits it and\n * reduces unvalidated. */\n payloadSchemaFor?: (type: string) => z.ZodType | undefined;\n};\n\n/** The stream a processor reduces. `read` answers durable rows plus the proof: `scannedThroughOffset`\n * is how far the read is CONTIGUOUSLY known (never past the durable mark — stream.ts), and `atHead`\n * says whether the page was cut; its length says nothing (a budget cut is short of `limit`). */\nexport type ProcessorStream = {\n append(...events: StreamEventInput[]): Promise<StreamEvent[]> | StreamEvent[];\n /** Append onto ANOTHER context of the same project, by path — how an entity's processor lands its\n * birth certificate on `/` for the project catalog. A stand-in with one path may omit it. */\n appendTo?(path: string, ...events: StreamEventInput[]): Promise<StreamEvent[]> | StreamEvent[];\n read(\n afterOffset?: number,\n limit?: number,\n ): Promise<{ events: StreamEvent[]; scannedThroughOffset: number; atHead: boolean }>;\n /** This processor's claim on its context's alarm: \"come back by `at`\" (the context's alarm pass\n * then calls `revive()`), or `null` to release it. Durable on the context, never a log event. */\n claim(at: number | null): Promise<unknown>;\n};\n\n/** How long after an attempt starts a dead host is revived: the recovery bound. A revive that finds\n * the attempt still in flight claims again with the delay doubled, up to `REVIVE_AFTER_MAX_MS`. */\nexport const REVIVE_AFTER_MS = 20_000;\nexport const REVIVE_AFTER_MAX_MS = 30 * 60_000;\n\n/** The contiguity proof a delivery carries: the half-open offset window `(after, through]`. A chain\n * of these (each `after` === the previous `through`) is how a subscriber proves it missed nothing. */\nexport type ScannedRange = { after: number; through: number };\n\nexport type ReduceArgs<State, Event = StreamEvent> = { event: Event; state: State };\n\nexport type ProcessEventArgs<\n State,\n Event = StreamEvent,\n /** What `append`/`appendTo` take: `EmittedEventInput<typeof Contract>` for a processor that\n * declares one — each type the contract `emits`, its payload as the catalog spells it. */\n Emitted extends StreamEventInput = StreamEventInput,\n> = {\n /** The consumed event — or `null` for the eventless at-head pass. */\n event: Event | null;\n state: State;\n previousState: State;\n /** Emit (validated against `emits`, provenance-stamped) onto this processor's own stream. Declared\n * as METHODS (not arrow-typed properties) on purpose: a subclass that narrows `Emitted` must stay\n * assignable to `StreamProcessor<State>` (the host's field), and only method parameters are\n * compared bivariantly. */\n append(...events: Emitted[]): Promise<StreamEvent[]>;\n /** The same, onto the context at `path` (apps/os's `appendTo`): a certificate cross-posted to `/`. */\n appendTo(path: string, ...events: Emitted[]): Promise<StreamEvent[]>;\n /** Hold the cursor until `work` settles; FIFO with other blockers of the SAME event. */\n blockProcessorWhile: (work: () => Promise<unknown>) => void;\n /** Fire-and-forget attempt; may overtake later events; outcome must be state-recoverable. */\n runInBackground: (work: () => Promise<unknown>) => void;\n delivery: { caughtUp: boolean };\n};\n\n/** THE ONE consumes rule — the processor engine, the subscription delivery loop, and the inline\n * reduces all call this; there is no second copy to drift. `consumes` undefined = every durable event\n * (a subscriber's default). \"*\" = every durable event. A NAMED type opts that type in, INCLUDING\n * ephemerals (\"*\" NEVER sweeps ephemerals) — so a live-state watcher spells\n * `consumes: [\"events.iterate.com/live-state/changed\"]` and filters `payload.key` itself. The wake\n * record (`stream/woken`) is a durable event like any other: a \"*\" row receives every incarnation's. */\nexport function consumesEvent(\n consumes: readonly string[] | undefined,\n event: { type: string; ephemeral?: boolean },\n): boolean {\n if (event.ephemeral) return consumes?.includes(event.type) ?? false;\n return !consumes || consumes.includes(\"*\") || consumes.includes(event.type);\n}\n\n/** What the ENGINE reduces: the contract's consumes, minus the one type no processor may ever reduce or\n * react to — a live-state delta. Deltas are notifications ABOUT state; letting one feed a reduce is\n * the feedback-loop class, made unspellable here rather than discouraged. */\nconst reducesEvent = (consumes: readonly string[], event: { type: string; ephemeral?: boolean }) =>\n event.type !== \"events.iterate.com/live-state/changed\" && consumesEvent(consumes, event);\n\n/** THE AUTHOR CLASS: a contract, three hooks and one helper. Deps an effect needs arrive through\n * the subclass's own constructor, as for any class. One instance lives as long as its host; a field\n * on it is RUNTIME state (gone with the host), which `projectLiveState` may reduce into the live view. */\nexport abstract class StreamProcessor<State, Event extends StreamEvent = StreamEvent> {\n abstract readonly contract: ProcessorContract<State>;\n\n /** Pure reduce. Return the NEXT state (a new object) — or null/undefined to keep the current. The\n * `Event` type param — a discriminated union of the events the contract consumes — narrows\n * `event.payload` per `event.type` inside the body, so no cast is needed; it defaults to the\n * untyped `StreamEvent` for processors that don't declare one. */\n reduce(_args: ReduceArgs<State, Event>): State | null | undefined {\n return undefined;\n }\n\n /** Side-effect hook. Synchronous by design: register async work via the two helpers on args.\n * `append`/`appendTo` take what THIS class's `contract` emits (`EmittedEventInput<this[\"contract\"]>`:\n * a subclass whose `contract` is a defined one gets each emitted type's payload as its catalog\n * spells it; the base, and a hand-built contract, take any input). */\n processEvent(\n _args: ProcessEventArgs<State, Event, EmittedEventInput<this[\"contract\"]>>,\n ): undefined {}\n\n /** The live-state PROJECTION — the shape clients see and the diffs are computed over. DEFAULT: the\n * reduced state verbatim, so every processor is live out of the box; that is deliberate — the\n * delta is an EPHEMERAL event, so \"always live\" costs an offset and a cheap diff, nothing durable.\n * Override to redact, or to REDUCE IN RUNTIME FIELDS (`return { ...state, lastSeenMs: this.lastSeenMs }`);\n * the engine re-projects after EVERY batch, and a field changed outside a batch needs the host's\n * `publishLiveState()`. */\n projectLiveState(state: State): unknown {\n return state;\n }\n\n /** Stable idempotency key namespaced by slug; pass the event being processed for a per-event key. */\n idempotencyKey(key: string, event?: StreamEvent): string {\n return event ? `${this.contract.slug}/${key}@${event.offset}` : `${this.contract.slug}/${key}`;\n }\n}\n\n/** THE ENGINE: everything below the author's three hooks — the serial chain, the checkpoint, gap\n * repair, the at-head pass, version re-reduces, live-state publishing. Constructed by the host\n * (`StreamProcessorDurableObject`; a test with the stand-ins in test-support.ts). */\nexport class ProcessorEngine<State> {\n readonly processor: StreamProcessor<State>;\n readonly #contract: ProcessorContract<State>;\n readonly #stream: ProcessorStream;\n readonly #storage: ReduceCheckpointTable;\n\n /** Rule 1: every batch runs on this chain, one after another. */\n #serialBatchChain: Promise<void> = Promise.resolve();\n /** The reduced state and the durable offset it was reduced through — checkpointed on the batches\n * that carried a durable. */\n #reducedState: State;\n #reducedThroughOffset: number;\n /** A checkpoint found under ANOTHER contract version: the input to the one re-reduce the chain\n * runs before anything else; cleared once it ran. */\n #staleCheckpoint?: { reducedThroughOffset: number; state: State };\n /** The highest `range.through` ever SHOWN to this processor (see processEventBatch). */\n #pushedThroughOffset?: number;\n /** A refusal that can only repeat — the checkpoint over its cell (stamped `retryable: false`):\n * LATCHED for this incarnation, so every later batch, catch-up and read verb rejects with it at\n * once instead of re-reducing into the same wall on every push and wake. A fresh incarnation\n * tries once more. */\n #latchedRefusal?: Error;\n /** waitUntilProcessed's waiting callers, resolved as the cursor advances. */\n readonly #waitUntilProcessedWaiters: { offset: number; resolve: () => void }[] = [];\n /** Born with the engine, so its epoch is minted once per incarnation. */\n readonly #liveState: LiveState<unknown>;\n /** Rule 3's claim: attempts in flight, and how many revives found one still in flight (the\n * backoff of the next claim; reset when the last attempt settles). The claim calls ride ONE\n * chain, so a release never overtakes the claim of the attempt that followed it. */\n #backgroundWorkInFlight = 0;\n #revivesWhileBusy = 0;\n #claimChain: Promise<unknown> = Promise.resolve();\n /** Whether the last batch this engine ran carried the at-head pass (rule 5) — what `revive()`\n * reads to know if its catch-up already ran one. */\n #lastBatchAtHead = false;\n\n constructor(\n processor: StreamProcessor<State>,\n deps: { stream: ProcessorStream; storage: ReduceCheckpointTable },\n ) {\n this.processor = processor;\n this.#contract = processor.contract;\n this.#stream = deps.stream;\n this.#storage = deps.storage;\n // ONE row, so cursor and state never disagree; one written under another contract version is\n // kept as #staleCheckpoint for the chain's first work.\n const { slug, version } = this.#contract;\n const checkpoint = this.#storage.read<State>(slug);\n if (checkpoint?.reducerVersion === version) {\n this.#reducedState = checkpoint.state ?? this.#contract.initialState();\n this.#reducedThroughOffset = checkpoint.reducedThroughOffset;\n } else {\n this.#reducedState = this.#contract.initialState();\n this.#reducedThroughOffset = 0;\n if (checkpoint)\n this.#staleCheckpoint = {\n reducedThroughOffset: checkpoint.reducedThroughOffset,\n state: checkpoint.state ?? this.#reducedState,\n };\n }\n // Seeded with the projection of the state this incarnation starts from — after a version bump\n // the OLD version's, so the publish that follows the re-reduce emits the one heal delta clients\n // synced to the old state need. The projection is the author's code: a throw here costs the\n // seed, never the engine.\n let seed: unknown;\n try {\n seed = processor.projectLiveState(\n this.#staleCheckpoint ? this.#staleCheckpoint.state : this.#reducedState,\n );\n } catch (error) {\n reportIssue(\"processor.live-state\", error, { slug });\n seed = undefined;\n }\n this.#liveState = new LiveState(this.#stream, slug, seed);\n }\n\n /** THE SEED DOOR for live-state clients (LiveState.snapshot), caught up first. */\n async liveSnapshot(): Promise<{ rev: number; state: unknown }> {\n if (!this.#reducedThroughPushedHead()) await this.catchUpFromLog();\n return this.#liveState.snapshot();\n }\n\n /** Emit a delta for the CURRENT projection (reduced + any runtime fields) if it changed. The engine\n * calls this after every batch; the host calls it after a runtime field moved outside a batch. A\n * throwing projection loses only its notification (the client re-seeds on the chain gap). */\n publishLiveState(): void {\n let projection: unknown;\n try {\n projection = this.processor.projectLiveState(this.#reducedState);\n } catch (error) {\n reportIssue(\"processor.live-state\", error, { slug: this.#contract.slug });\n return;\n }\n this.#liveState.set(projection);\n }\n\n // ── the drive doors ──\n\n /** THE push door: contiguous → reduce it directly (no read); anything else → gap repair from the\n * own cursor first. Fire-and-forget safe: enqueues on the serial chain. */\n processEventBatch(events: StreamEvent[], range: ScannedRange): Promise<void> {\n // Recorded SYNCHRONOUSLY: the head this processor has been SHOWN. Read verbs skip their catch-up\n // when the reduce has provably reached it — the fast path that deletes one parent read RPC from\n // every capability dispatch once caught up.\n this.#pushedThroughOffset = Math.max(this.#pushedThroughOffset ?? 0, range.through);\n return this.#runOnSerialChain(async () => {\n await this.#rereduceIfVersionChanged();\n // GAP REPAIR heals the durable prefix from the log FIRST — up to the push start and no\n // further, because the push carries fresh ephemerals the log cannot return, so the push\n // itself is processed afterwards, never replaced by a catch-up. The repair never delivers\n // caughtUp: the push decides at-head. The log can run out below `range.after` only when that\n // offset was handed to an ephemeral — then there is nothing durable left to heal.\n while (this.#reducedThroughOffset < range.after) {\n const after = this.#reducedThroughOffset;\n const page = await this.#stream.read(after, 500);\n if (page.scannedThroughOffset <= after) break;\n await this.#reduceAndCommitEventBatch(\n page.events.filter((event) => event.offset <= range.after),\n { after, through: Math.min(page.scannedThroughOffset, range.after) },\n false,\n );\n }\n // ALWAYS process the push — no push is ever discarded (a wholly-behind one reduces nothing and\n // just delivers its ephemerals). At head iff this push reaches the head shown so far.\n await this.#reduceAndCommitEventBatch(\n events,\n range,\n range.through >= this.#pushedThroughOffset!,\n );\n });\n }\n\n /** Catch up from the own checkpoint (a cold boot, the read verbs, the barrier), page by page — a\n * failed batch, a missed push, or a fresh incarnation can never skip a durable event. */\n catchUpFromLog(): Promise<void> {\n return this.#runOnSerialChain(async () => {\n await this.#rereduceIfVersionChanged();\n for (;;) {\n const after = this.#reducedThroughOffset;\n const page = await this.#stream.read(after, 500);\n if (page.scannedThroughOffset <= after) return; // nothing beyond the cursor: at head already\n // The page says whether it reached the head — never judge by its length (rule 5's caught-up\n // pass rides the last page).\n await this.#reduceAndCommitEventBatch(\n page.events,\n { after, through: page.scannedThroughOffset },\n page.atHead,\n );\n if (page.atHead) return;\n }\n });\n }\n\n // ── the read surface ──\n\n /** Reduce-and-effects caught up through the log, then `{ offset, state }`. */\n async snapshot(): Promise<{ offset: number; state: State }> {\n if (!this.#reducedThroughPushedHead()) await this.catchUpFromLog();\n return { offset: this.#reducedThroughOffset, state: this.#reducedState };\n }\n\n /** Provably reduced through the head SHOWN so far → the read verbs skip their catch-up read. */\n #reducedThroughPushedHead(): boolean {\n return (\n this.#pushedThroughOffset !== undefined &&\n this.#reducedThroughOffset >= this.#pushedThroughOffset\n );\n }\n\n /** THE barrier verb (read-your-writes): resolves once processed AT LEAST through `offset`. An\n * offset ABOVE the durable mark (an ephemeral's) is reached only if this processor was pushed it —\n * the log cannot prove past the mark, so a wake alone never advances there. */\n waitUntilProcessed(input: { offset: number; timeoutMs?: number }): Promise<void> {\n const { offset, timeoutMs = 10_000 } = input;\n return new Promise<void>((resolve, reject) => {\n if (this.#reducedThroughOffset >= offset) return resolve();\n const waiter = {\n offset,\n resolve: () => {\n clearTimeout(timer);\n resolve();\n },\n };\n const timer = setTimeout(() => {\n // A timed-out waiter LEAVES the list — otherwise every later batch re-scans it forever.\n this.#waitUntilProcessedWaiters.splice(this.#waitUntilProcessedWaiters.indexOf(waiter), 1);\n reject(\n new Error(\n `processor \"${this.#contract.slug}\" did not reach offset ${offset} in ${timeoutMs}ms`,\n ),\n );\n }, timeoutMs);\n this.#waitUntilProcessedWaiters.push(waiter);\n // A rejecting catch-up (read threw) rejects THIS waiter promptly with the real error, not a\n // wait-until-timeout with a generic message.\n void this.catchUpFromLog().catch((error) => {\n const i = this.#waitUntilProcessedWaiters.indexOf(waiter);\n if (i === -1) return; // already resolved/timed-out\n this.#waitUntilProcessedWaiters.splice(i, 1);\n clearTimeout(timer);\n reject(error instanceof Error ? error : new Error(String(error)));\n });\n });\n }\n\n // ── the engine (private) ──\n\n /** Serialize on the chain. THE RULE: never await your own chain from inside a batch — a\n * processor that appends during its batch would deadlock, which is why every append→drive\n * caller is fire-and-forget. */\n #runOnSerialChain(work: () => Promise<void>): Promise<void> {\n const run = this.#serialBatchChain.then(() => {\n if (this.#latchedRefusal) throw this.#latchedRefusal;\n return work();\n });\n this.#serialBatchChain = run.catch(() => {}); // a failed batch never wedges the chain; retry via wake\n return run;\n }\n\n /** The one-time cost of a contract version bump: re-reduce the durable log from offset 0 through\n * the OLD cursor (`reduce` only — those effects already ran) and checkpoint under the new\n * version. Never past the old cursor: re-reducing to the head instead would judge an\n * already-queued in-flight push stale and swallow its effects. */\n async #rereduceIfVersionChanged(): Promise<void> {\n if (!this.#staleCheckpoint) return;\n const target = this.#staleCheckpoint.reducedThroughOffset;\n let state = this.#contract.initialState();\n let reducedThroughOffset = 0;\n while (reducedThroughOffset < target) {\n const page = await this.#stream.read(reducedThroughOffset, 500);\n for (const event of page.events)\n if (event.offset <= target && reducesEvent(this.#contract.consumes, event))\n // The SAME validate+normalize+fold as the live flow — a replay must reproduce it exactly,\n // else a coercion applied live but not here would make a version bump rewrite state.\n state = this.#validateNormalizeAndReduce(event, state).state;\n if (page.scannedThroughOffset <= reducedThroughOffset) break; // nothing left below the target\n reducedThroughOffset = Math.min(page.scannedThroughOffset, target);\n }\n this.#writeCheckpointOrLatch(\n this.#contract.slug,\n { reducerVersion: this.#contract.version, reducedThroughOffset: target },\n state,\n true,\n );\n this.#reducedState = state;\n this.#reducedThroughOffset = target;\n this.#staleCheckpoint = undefined;\n // The one heal delta for clients synced to the OLD version's projection (the constructor's seed).\n this.publishLiveState();\n this.#resolveWaitUntilProcessedWaiters(target);\n }\n\n /** Rules 2–5 over one range (the caller has healed any durable prefix gap first). DURABLES reduce\n * at-most-once (`offset > cursor`); EPHEMERALS ALWAYS deliver — each rides exactly one push and\n * can never be a redelivery, so a durable-only wake that clamped the cursor PAST an ephemeral\n * offset must not suppress it. The cursor is a DURABLE-reduce watermark and never regresses. */\n async #reduceAndCommitEventBatch(\n events: StreamEvent[],\n range: ScannedRange,\n atHead: boolean,\n ): Promise<void> {\n const reducedThroughOffsetBefore = this.#reducedThroughOffset;\n const stateBefore = this.#reducedState;\n let state = stateBefore;\n const consumableEvents = events.filter(\n (event) =>\n reducesEvent(this.#contract.consumes, event) &&\n (event.ephemeral || event.offset > reducedThroughOffsetBefore),\n );\n let caughtUpDelivered = false;\n for (let i = 0; i < consumableEvents.length; i++) {\n const last = i === consumableEvents.length - 1;\n const r = await this.#reduceAndProcessEvent(consumableEvents[i], state, atHead && last);\n state = r.state;\n // A skipped (malformed) last event never delivered caught-up — the eventless pass below does.\n if (atHead && last && r.processed) caughtUpDelivered = true;\n }\n // Rule 5: reached the head with no caught-up event → one eventless at-head pass.\n if (atHead && !caughtUpDelivered)\n state = (await this.#reduceAndProcessEvent(null, state, true)).state;\n\n // Rule 4: ONE persist per range, iff a DURABLE actually ADVANCED the cursor — `advanced`\n // excludes a stale re-push (a no-op write), `sawDurable` the ephemeral-only range (the flood\n // stays free).\n const reducedThroughOffset = Math.max(reducedThroughOffsetBefore, range.through);\n const advanced = reducedThroughOffset > reducedThroughOffsetBefore;\n const sawDurable = events.some((event) => !event.ephemeral);\n if (sawDurable && advanced)\n this.#writeCheckpointOrLatch(\n this.#contract.slug,\n { reducerVersion: this.#contract.version, reducedThroughOffset },\n state,\n state !== stateBefore,\n );\n this.#reducedState = state;\n this.#reducedThroughOffset = reducedThroughOffset;\n this.#lastBatchAtHead = atHead;\n this.#resolveWaitUntilProcessedWaiters(reducedThroughOffset);\n // Persist FIRST, emit the live-state delta second: a crash between loses only a notification,\n // healed by the chain gap, never state. Re-projected after EVERY batch, not only when the reduce\n // moved, so a runtime field bumped inside `processEvent` publishes on its own.\n this.publishLiveState();\n }\n\n // ── rule 3's claim: work in flight ⇒ a claim on the context's alarm ──\n\n /** Start an attempt: the first in flight claims the alarm (not awaited — a claim that has not\n * landed when the host dies revives nothing either way, and the attempt must not wait on it);\n * the last to settle releases the claim. */\n #runInBackground(work: () => Promise<unknown>): void {\n this.#backgroundWorkInFlight += 1;\n if (this.#backgroundWorkInFlight === 1) this.#claim(REVIVE_AFTER_MS);\n void work()\n .catch((error) => reportIssue(\"processor.background\", error, { slug: this.#contract.slug }))\n .finally(() => {\n this.#backgroundWorkInFlight -= 1;\n if (this.#backgroundWorkInFlight === 0) {\n this.#revivesWhileBusy = 0;\n this.#claim(null);\n }\n });\n }\n\n #claim(afterMs: number | null): void {\n const at = afterMs === null ? null : Date.now() + afterMs;\n this.#claimChain = this.#claimChain\n .then(() => this.#stream.claim(at))\n .catch((error) => reportIssue(\"processor.claim\", error, { slug: this.#contract.slug }));\n }\n\n /** THE REVIVE — the context's alarm pass calls this for a due claim (spent by then): catch up\n * from the log and run the at-head pass, so a processor restarts what state says is still owed\n * (rule 3). A fresh incarnation finds nothing in flight and starts it; an attempt still in flight\n * here claims again, later each time (20 s, 40 s, … `REVIVE_AFTER_MAX_MS`). */\n async revive(): Promise<void> {\n this.#lastBatchAtHead = false;\n await this.catchUpFromLog();\n // A catch-up that reached the head already ran the at-head pass (rule 5); one that found\n // nothing to read runs it here, once.\n if (!this.#lastBatchAtHead)\n await this.#runOnSerialChain(async () => {\n this.#reducedState = (\n await this.#reduceAndProcessEvent(null, this.#reducedState, true)\n ).state;\n this.publishLiveState();\n });\n if (this.#backgroundWorkInFlight === 0) return;\n this.#revivesWhileBusy += 1;\n this.#claim(Math.min(REVIVE_AFTER_MS * 2 ** this.#revivesWhileBusy, REVIVE_AFTER_MAX_MS));\n // Awaited: the pass that called this derives its next deadline as soon as it returns, so the\n // claim must have landed by then — one alarm write, not a delete and a set.\n await this.#claimChain;\n }\n\n /** THE GUARDED REDUCE, shared by the live flow and the version replay. A reducer that throws on an\n * event (malformed, or one an OLDER version accepted) must never wedge the processor: on a version\n * replay it would fail the catch-up before the new checkpoint is written, every incarnation. */\n #reduceOrKeep(event: StreamEvent, state: State): State {\n try {\n return this.processor.reduce({ event, state }) ?? state;\n } catch (error) {\n reportIssue(\"processor.reduce\", error, { slug: this.#contract.slug, offset: event.offset });\n return state;\n }\n }\n\n /** Validate a consumed event's payload against the contract's declared schema, then reduce it — or,\n * for a malformed payload, skip the fold and report (it must never corrupt reduced state, the\n * exported view a live client parses). Returns the next state AND the event to carry onward,\n * NORMALIZED to the schema's `z.output` (coercions/defaults applied) when it validated — so the\n * reducer, the effect hook, and the version replay all see exactly what `ConsumedEvent<Contract>`\n * promises. SHARED by the live flow and `#rereduceIfVersionChanged`, so the two can never diverge\n * (a coercion applied live but not on replay would make a version bump rewrite state). A payload-less\n * event validates as `{}` (the \"empty defaults\" convention the contract requires of its stateSchema);\n * a contract with no `events` catalog (the kernel-generic processors) folds unvalidated, as before. */\n #validateNormalizeAndReduce(\n event: StreamEvent,\n state: State,\n ): { state: State; event: StreamEvent; valid: boolean } {\n // oxlint-disable-next-line iterate/simple-truthiness-check -- only a null/undefined payload defaults to {}; a falsy NON-object payload off the wire (0, false, \"\") must fail schema validation and be reported, not be folded as an empty object\n const parsed = this.#contract.payloadSchemaFor?.(event.type)?.safeParse(event.payload ?? {});\n if (parsed && !parsed.success) {\n reportIssue(\"processor.reduce.payload\", parsed.error, {\n slug: this.#contract.slug,\n offset: event.offset,\n type: event.type,\n });\n return { state, event, valid: false }; // malformed: not folded, and the caller skips its effect\n }\n // Owned-event payloads are object schemas, so `z.output` is a record.\n const normalized = parsed\n ? { ...event, payload: parsed.data as Record<string, unknown> }\n : event;\n return { state: this.#reduceOrKeep(normalized, state), event: normalized, valid: true };\n }\n\n /** THE per-event primitive (rules 2–3) — the batch loop and the eventless at-head pass both come\n * here: a GUARDED reduce, then `processEvent` with a FIFO blocker chain drained to a FIXED POINT.\n * Returns the next state and whether the effect ran — a malformed payload for a KNOWN event is\n * SKIPPED for BOTH reduce and effect (the effect hook is typed against `ConsumedEvent`'s `z.output`,\n * so handing it garbage would throw and wedge the batch — checkpoints never advance, catch-up\n * refails the same row); `processed: false` lets the batch fall back to the eventless caught-up pass.\n * Owns NO cursor / persist / waiter — the caller does. */\n async #reduceAndProcessEvent(\n event: StreamEvent | null,\n state: State,\n caughtUp: boolean,\n ): Promise<{ state: State; processed: boolean }> {\n const { slug, version, emits } = this.#contract;\n const previousState = state;\n if (event) {\n // Validate + normalize + fold via the ONE shared path (so replay can't diverge). On the valid\n // path the reducer AND the effect hook below both see the NORMALIZED event (schema `z.output`);\n // a malformed payload is neither folded nor delivered to the (typed) effect hook.\n const reduced = this.#validateNormalizeAndReduce(event, state);\n if (!reduced.valid) return { state: reduced.state, processed: false };\n state = reduced.state;\n event = reduced.event;\n }\n // FIFO blocker chain for THIS event (rule 2); background work escapes it (rule 3).\n let blockers: Promise<unknown> = Promise.resolve();\n // Validated against the declared `emits` and stamped with provenance — here, or on another\n // context of the project (`appendTo`), the same way.\n const stamped = (emittedEvents: StreamEventInput[]): StreamEventInput[] => {\n for (const emitted of emittedEvents) {\n if (!emits.includes(emitted.type))\n throw new Error(\n `processor \"${slug}\" emits ${JSON.stringify(emitted.type)} without declaring it`,\n );\n emitted.source = {\n processor: {\n slug,\n version,\n ...(event && { whileProcessing: { offset: event.offset, type: event.type } }),\n },\n };\n }\n return emittedEvents;\n };\n this.processor.processEvent({\n event,\n state,\n previousState,\n append: async (...emittedEvents) => await this.#stream.append(...stamped(emittedEvents)),\n appendTo: async (path, ...emittedEvents) => {\n if (!this.#stream.appendTo)\n throw new Error(`processor \"${slug}\": this host reaches no other context (appendTo)`);\n return await this.#stream.appendTo(path, ...stamped(emittedEvents));\n },\n blockProcessorWhile: (work) => {\n blockers = blockers.then(() => work());\n },\n runInBackground: (work) => this.#runInBackground(work),\n delivery: { caughtUp },\n });\n // STRICT PER-EVENT ORDERING (rule 2): drain the blocker chain to a FIXED POINT. A\n // blockProcessorWhile called from INSIDE a running blocker extends the chain (still THIS event's\n // blocking work), so re-await until it stops growing — latching the pre-nesting snapshot would\n // let the next event's processEvent (and the batch commit) overtake it.\n for (let awaited: Promise<unknown> | undefined; awaited !== blockers; ) {\n awaited = blockers;\n await awaited;\n }\n return { state, processed: true };\n }\n\n /** The checkpoint write, with the latch: a refusal stamped `retryable: false` can only repeat. */\n #writeCheckpointOrLatch(\n slug: string,\n cursor: { reducerVersion: string; reducedThroughOffset: number },\n state: State,\n stateChanged: boolean,\n ): void {\n try {\n this.#storage.write(slug, cursor, state, stateChanged);\n } catch (error) {\n if ((error as { retryable?: unknown } | null)?.retryable === false)\n this.#latchedRefusal = error instanceof Error ? error : new Error(String(error));\n throw error;\n }\n }\n\n /** Resolve the waiters a cursor advance satisfies; keep the rest. */\n #resolveWaitUntilProcessedWaiters(reducedThroughOffset: number): void {\n for (const w of this.#waitUntilProcessedWaiters.splice(0)) {\n if (reducedThroughOffset >= w.offset) w.resolve();\n else this.#waitUntilProcessedWaiters.push(w);\n }\n }\n}\n\n// ── events ── the stream event envelope + idempotency rules. Zod-FREE: the envelope carries no\n// runtime validator (the processor contract section below has the zod half).\n\n// THE one deep-equal lives in lib.ts; re-exported here for the SDK bundle.\nexport { jsonEqual };\n\n/** What `append` accepts: the event body, before the stream assigns its committed identity. The\n * door checks ONE rule by hand: `type` is a non-empty string. */\nexport type StreamEventInput = {\n /** Convention: `events.iterate.com/<domain>/<fact>`. */\n type: string;\n payload?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n /** Provenance: which processor (while processing what) appended this — stamped by the engine's\n * `append` — and WHO: the session's verified principal (src/principal.ts), set by the DO's append\n * root from the session's project token and never taken from a client. */\n source?: {\n /** The durable schedule definition responsible for this occurrence. */\n schedule?: {\n key: string;\n scheduledAtOffset: number;\n at: string;\n /** Attribution of the definition, distinct from the platform writing the occurrence. */\n definedBy?: Omit<NonNullable<StreamEventInput[\"source\"]>, \"schedule\">;\n };\n processor?: {\n slug: string;\n version: string;\n whileProcessing?: { offset: number; type: string };\n };\n principal?: { actor: string; email?: string };\n /** THE CONNECTION the principal acted through: the OAuth grant's id — one per\n * connected client (a Claude Code install, a dash sign-in, a personal token). Stamped beside\n * `principal` by the DO's append root (src/principal.ts); absent for the admin secret and the kernel. */\n grant?: string;\n };\n /** Same key + same body = dedupe (the existing event is returned); different body = loud error. */\n idempotencyKey?: string;\n /** OPTIONAL PRECONDITION (apps/os): land at exactly this offset or refuse the whole batch with\n * OFFSET_CONFLICT — \"nothing has happened since I last looked\". Never stored in the body. */\n offset?: number;\n /** An EPHEMERAL event rides the stream to live subscribers but is NEVER persisted: it consumes an\n * offset, triggers zero writes, and its body is gone the moment the incarnation ends — nobody can\n * redeliver it (stream.ts, the zero-write contract). A durable OMITS the field. */\n ephemeral?: true;\n};\n\n/** A committed event: the input plus the identity the stream assigned at its commit point. */\nexport type StreamEvent = Omit<StreamEventInput, \"offset\"> & {\n offset: number;\n createdAt: string;\n path: string;\n};\n\n// ── idempotency (apps/os semantics, message text kept greppable across RPC hops) ──\n\nexport function idempotencyConflictMessage(idempotencyKey: string, existingOffset: number): string {\n return `idempotency key \"${idempotencyKey}\" already names a different event at offset ${existingOffset}`;\n}\n\n/** Structural equality of the parts an idempotent retry must not change. */\nexport function sameIdempotentEvent(\n existingEvent: StreamEventInput,\n requestedEvent: StreamEventInput,\n): boolean {\n return (\n existingEvent.type === requestedEvent.type &&\n jsonEqual(existingEvent.payload, requestedEvent.payload) &&\n jsonEqual(existingEvent.metadata, requestedEvent.metadata)\n );\n}\n\n// ── reduce checkpoint ── THE ONE spelling of a persisted reduce checkpoint, shared by BOTH hosts (the\n// stream's core reduce and the facet-hosted `ProcessorEngine`). ONE ROW per slug: the reducer\n// version, the offset reduced through, and the state as JSON (NULL = the reduce never changed it =\n// `initialState()` — a pure side-effect processor reusing its cursor never re-fires its effect\n// history). ONE statement per write, so a checkpoint can never tear; the state column is rewritten\n// only when the reduce changed it (`COALESCE`).\n//\n// THE CELL CEILING: a checkpoint is one SQLite cell — 2 MB in production, SQLITE_TOOBIG past it. A\n// state whose JSON would not fit is refused BEFORE the write with a coded error, so the caller sees\n// why instead of the platform's raw message, and nothing lands. No cloudflare:workers import on\n// purpose: this module rides the SDK bundle into every facet isolate.\n\n/** Under the 2 MB cell, with room for the row's other columns. */\nconst REDUCE_CHECKPOINT_STATE_MAX_CHARS = 2 * 1024 * 1024 - 4096;\n\n/** Sync SQLite as the platform hands it over (`ctx.storage.sql`): a query is a LAZY cursor —\n * iterate it, or `toArray()`. Spelled structurally so a node:sqlite stand-in satisfies it. */\nexport type SqlStorageHandle = {\n exec<T extends Record<string, SqlStorageValue>>(\n query: string,\n ...bindings: unknown[]\n ): Iterable<T> & { toArray(): T[] };\n};\n\n/** A persisted checkpoint as read back: the version it was reduced under (the caller gates on it),\n * the offset reduced through, and the state — `undefined` when the reduce never changed it. */\nexport type ReduceCheckpoint<State> = {\n reducerVersion: string;\n reducedThroughOffset: number;\n state: State | undefined;\n};\n\n/** What BOTH hosts read and write their checkpoints through — the stream's storage and a facet's\n * own (the unit lane drives it over node:sqlite, stream/test-support.ts). */\nexport class ReduceCheckpointTable {\n readonly #sql: SqlStorageHandle;\n\n /** `createTable: false` when the caller knows the table exists (the stream's storage skips every\n * CREATE on a re-wake); a facet host constructs one per incarnation and lets it create. */\n constructor(sql: SqlStorageHandle, options: { createTable: boolean } = { createTable: true }) {\n this.#sql = sql;\n if (options.createTable) ReduceCheckpointTable.createTable(sql);\n }\n\n static createTable(sql: SqlStorageHandle): void {\n sql.exec(\n `CREATE TABLE IF NOT EXISTS reduce_checkpoints (\n slug TEXT PRIMARY KEY,\n reducer_version TEXT NOT NULL,\n reduced_through_offset INTEGER NOT NULL,\n state TEXT\n )`,\n );\n }\n\n read<State>(slug: string): ReduceCheckpoint<State> | undefined {\n const row = this.#sql\n .exec<{ reducer_version: string; reduced_through_offset: number; state: string | null }>(\n \"SELECT reducer_version, reduced_through_offset, state FROM reduce_checkpoints WHERE slug = ?\",\n slug,\n )\n .toArray()[0];\n if (!row) return undefined;\n return {\n reducerVersion: String(row.reducer_version),\n reducedThroughOffset: Number(row.reduced_through_offset),\n state: row.state ? (JSON.parse(String(row.state)) as State) : undefined,\n };\n }\n\n /** ALWAYS the cursor; the state ONLY when `stateChanged` — one write either way. */\n write<State>(\n slug: string,\n cursor: { reducerVersion: string; reducedThroughOffset: number },\n state: State,\n stateChanged: boolean,\n ): void {\n const serializedState = stateChanged ? (JSON.stringify(state) ?? null) : null;\n // Stamped `retryable: false` (the flag workerd itself uses): the same state serializes to the\n // same size on every retry — a delivery loop halts on it instead of climbing its ladder.\n if (serializedState && serializedState.length > REDUCE_CHECKPOINT_STATE_MAX_CHARS)\n throw Object.assign(\n codedError(\n \"REDUCE_CHECKPOINT_TOO_LARGE\",\n `checkpoint \"${slug}\": the reduced state serializes to ${serializedState.length} chars, over the ${REDUCE_CHECKPOINT_STATE_MAX_CHARS}-char ceiling of one storage cell (2 MB) — a reduce must keep a summary, not the events; nothing was written`,\n { slug, chars: serializedState.length, maxChars: REDUCE_CHECKPOINT_STATE_MAX_CHARS },\n ),\n { retryable: false },\n );\n this.#sql.exec(\n `INSERT INTO reduce_checkpoints (slug, reducer_version, reduced_through_offset, state)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(slug) DO UPDATE SET\n reducer_version = excluded.reducer_version,\n reduced_through_offset = excluded.reduced_through_offset,\n state = COALESCE(excluded.state, reduce_checkpoints.state)`,\n slug,\n cursor.reducerVersion,\n cursor.reducedThroughOffset,\n serializedState,\n );\n }\n}\n\n// ── live state ── THE live-state primitive: one value, its revision chain, and the diff→emit\n// dance Phoenix LiveView does, over the project's stream. Used two ways: a mini-app DO owns one\n// directly as its store; the ProcessorEngine owns one per processor and `set`s the projection after\n// every batch (sdk/index.ts shows both).\n//\n// MUTATION AND NOTIFICATION ARE INSEPARABLE: `set(next)` diffs the held value → next; on a real\n// change it bumps the revision and appends the ephemeral `live-state/changed` delta carrying\n// `{key, from, to, patch}` onto the stream. `snapshot()` is the SEED DOOR. The stream keeps no\n// per-subscriber state — the CLIENT owns its chain: seed through the door, apply a payload whose\n// `from` matches its held rev, re-read the door on any mismatch (live-state-chains-client-side.e2e).\n//\n// The revision is seeded from a per-incarnation EPOCH (not 0): a reborn holder mints a fresh epoch,\n// so every stale client rev mismatches and re-reads the door instead of applying a patch onto a\n// diverged base. Lossy by contract — a dropped delta append is a chain gap the client heals, never\n// state loss. HARD RULE: no processor can ever REDUCE the delta (processor.ts `reducesEvent`), so a\n// state-change notification can never feed a reduce; a SUBSCRIPTION may name the type to watch it.\n\n/** A delta whose patch is over this many chars is not sent: a whole-array replace of a large\n * projection would cost every watcher the projection per set, and past the event ceiling the door\n * would refuse it outright. The delta rides with `patch: null` instead — the rev moved, re-seed. */\nconst LIVE_STATE_PATCH_MAX_CHARS = 1024 * 1024;\n\n/** The only thing a LiveState needs from its host: somewhere to append the delta. A\n * `ProcessorStream` and the itx scope both satisfy it. */\nexport type LiveStateSink = {\n append(event: { type: string; ephemeral?: true; payload?: Record<string, unknown> }): unknown;\n};\n\nexport class LiveState<S> {\n readonly #liveStateSink: LiveStateSink;\n readonly #liveStateKey: string;\n #state: S;\n /** The DIFF BASE: the last value that serialized — what a client that applied every delta holds.\n * Kept apart from `#state` so a value the wire cannot carry, adopted without an emit, never\n * becomes the base every later diff would throw against. */\n #lastSerializedState: S;\n #liveStateRev: number;\n /** THE DELTA APPEND CHAIN — at most one delta append in flight, so commit order = mint order for a\n * CROSS-HOP sink: `env.ITX.get().append(e)` mints a FRESH capability per call, so two deltas\n * issued in different turns race across the hop and the second can commit first — ~14% of rapid\n * pairs on the deployed edge (never locally, the hop is sub-ms). Nothing is dropped by a reorder,\n * but it costs every watcher the full door re-read the deltas exist to avoid. A lone delta (the\n * chain idle) is issued synchronously; only when an append is already in flight does the next\n * queue behind it. Nobody waits on this. */\n #liveStateDeltaAppendChain: Promise<unknown> = Promise.resolve();\n\n constructor(sink: LiveStateSink, key: string, initial: S) {\n this.#liveStateSink = sink;\n this.#liveStateKey = key;\n this.#state = initial;\n this.#lastSerializedState = initial;\n this.#liveStateRev = Date.now() * 4096 + Math.floor(Math.random() * 4096);\n }\n\n /** The current value (reflects every `set`). */\n get(): S {\n return this.#state;\n }\n\n /** THE seed door: `{rev, state}` read together (single-threaded ⇒ atomically), which is what lets\n * a client chain patches exactly instead of guessing which changes its snapshot already contains. */\n snapshot(): { rev: number; state: S } {\n return { rev: this.#liveStateRev, state: this.#state };\n }\n\n /** Replace the value: diff the last serialized base → next; on a real change bump the revision\n * and append the delta. Build a NEW value (don't mutate `next` in place) — the diff is over JSON.\n * A diff/append failure degrades to a LOST notification (the client re-seeds on the chain gap),\n * never a throw the caller sees. */\n set(next: S): void {\n // The SAME object is the same JSON (the contract forbids in-place mutation, which is what makes\n // identity a proof of equality): no diff, no delta, no rev move — the common case for a\n // processor whose projection is its unchanged reduced state.\n if (next === this.#lastSerializedState) return;\n // A `next` the wire cannot carry (a BigInt, a cycle) throws in the diff: adopt it anyway, and\n // STILL advance the rev — the bump mints the chain gap that forces a stale client's re-seed\n // (without it, a later emit's `from` would match the client's held rev and it would apply a\n // patch computed against a base it never received: silent corruption). The serialized base\n // stays put, so the next serializable value emits as a diff from what the client last saw.\n let patch;\n try {\n patch = diff(this.#lastSerializedState, next);\n } catch {\n this.#state = next;\n this.#liveStateRev += 1;\n return;\n }\n this.#state = next;\n this.#lastSerializedState = next;\n if (!patch) return;\n const from = this.#liveStateRev;\n const to = from + 1; // a LOCAL: a later set's rev must not be read into this delta's payload\n this.#liveStateRev = to;\n const wirePatch = JSON.stringify(patch).length > LIVE_STATE_PATCH_MAX_CHARS ? null : patch;\n const emitDelta = () =>\n this.#liveStateSink.append({\n type: \"events.iterate.com/live-state/changed\",\n ephemeral: true,\n payload: { key: this.#liveStateKey, from, to, patch: wirePatch },\n });\n // A cross-hop sink mints a FRESH capability per call, so two deltas can race on the wire: every\n // delta rides ONE chain, each emitted only after the previous append settles, so mint order IS\n // the delivery order. No in-flight flag + synchronous fast path — clearing the flag in the first\n // append's `finally` while a later delta was still queued let a newer delta see \"idle\", fork a\n // parallel chain, and overtake the queued one (commit order 1, 3, 2). The chain's own `.catch`\n // contains a dropped delta (a sync throw or a rejection): a chain gap the client heals (the rev\n // already advanced); it must never reach the caller.\n this.#liveStateDeltaAppendChain = this.#liveStateDeltaAppendChain\n .then(emitDelta)\n .catch(() => {});\n }\n}\n\n// ── processor contract ── the zod CONTRACT helper (apps/os `defineProcessorContract`, focused); zod\n// rides the SDK bundle with it. A contract declares its\n// identity, reduced-state schema, the events it OWNS (`events`, keyed by the durable type string,\n// each with a zod payload schema — so the type strings and payload shapes are visible right here),\n// the events it `consumes`/`emits`, and optional `processorDeps` (other contracts whose events it may\n// consume without owning). The reduce's event union and the state type are DERIVED from the contract\n// (`ConsumedEvent` / `ProcessorState`) — no hand-kept discriminated union to drift.\n\n/** One owned event: its description and the zod schema for its payload. `ephemeral: true` marks a\n * non-durable event (delivered only when its type is named in `consumes`). */\nexport type EventDefinition = { description: string; payloadSchema: z.ZodType; ephemeral?: true };\n/** A durable event type string → its definition. */\nexport type EventCatalog = Record<string, EventDefinition>;\n\n/** A `processorDeps` entry's own event catalog. */\ntype DepCatalog<Dep> = Dep extends { events: infer Events extends EventCatalog } ? Events : never;\n/** The definition owning `Type` — local events win, then each dep. */\ntype DefinitionForType<\n Events extends EventCatalog,\n Deps extends readonly unknown[],\n Type extends string,\n> = Type extends keyof Events\n ? Events[Type]\n : Deps[number] extends infer Dep\n ? Dep extends unknown\n ? Type extends keyof DepCatalog<Dep>\n ? DepCatalog<Dep>[Type]\n : never\n : never\n : never;\n\n/** The committed event for one resolved type: `StreamEvent` narrowed to its `{ type, payload }`. */\ntype EventForType<\n Events extends EventCatalog,\n Deps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? DefinitionForType<Events, Deps, Type> extends { payloadSchema: infer Schema extends z.ZodType }\n ? StreamEvent & { type: Type; payload: z.output<Schema> }\n : never\n : never;\n\n/** The reduce union for a `consumes` tuple — `\"*\"` alone means any `StreamEvent`. */\ntype EventForTypes<\n Events extends EventCatalog,\n Deps extends readonly unknown[],\n Types extends readonly string[],\n> = \"*\" extends Types[number] ? StreamEvent : EventForType<Events, Deps, Types[number]>;\n\n/** A contract's `processorDeps` tuple, defaulting to empty. */\ntype DepsOf<Contract> = Contract extends { processorDeps: infer Deps extends readonly unknown[] }\n ? Deps\n : readonly [];\n\n/** A contract's reduced-state type, inferred from its `stateSchema`. */\nexport type ProcessorState<Contract> = Contract extends {\n stateSchema: infer Schema extends z.ZodType;\n}\n ? z.output<Schema>\n : never;\n\n/** The committed-event union a contract's `consumes` list can deliver to `reduce`/`processEvent`. */\nexport type ConsumedEvent<Contract> = Contract extends {\n events: infer Events extends EventCatalog;\n consumes: infer Consumes extends readonly string[];\n}\n ? EventForTypes<Events, DepsOf<Contract>, Consumes>\n : never;\n\n/** The input for ONE event type as a catalog spells it (`EventInput`'s row) — or, for a type no\n * catalog defines (a core control event a processor emits, `project/ingress-configured`), the plain\n * input: it widens the whole union, so a contract that emits one undefined type appends untyped\n * until that type is in a catalog it depends on. */\ntype EventInputForType<\n Events extends EventCatalog,\n Deps extends readonly unknown[],\n Type extends string,\n> = Type extends unknown\n ? [DefinitionForType<Events, Deps, Type>] extends [never]\n ? StreamEventInput\n : DefinitionForType<Events, Deps, Type> extends {\n payloadSchema: infer Schema extends z.ZodType;\n }\n ? {\n type: Type;\n payload: z.input<Schema>;\n idempotencyKey?: string;\n metadata?: Record<string, unknown>;\n } & (DefinitionForType<Events, Deps, Type> extends { ephemeral: true }\n ? { ephemeral: true }\n : { ephemeral?: never })\n : never\n : never;\n\n/** What a processor's `append`/`appendTo` take: one input per type the contract `emits` — its own\n * events and its deps' as their catalogs spell them (`z.input`), a type no catalog defines as the\n * plain input under that name. A contract whose `emits` is not a literal tuple gets every input. */\nexport type EmittedEventInput<Contract> = Contract extends {\n events: infer Events extends EventCatalog;\n emits: infer Emits extends readonly string[];\n}\n ? string[] extends Emits\n ? StreamEventInput\n : Emits extends readonly []\n ? StreamEventInput // emits nothing: no call to type, and `never` would break the host's variance\n : EventInputForType<Events, DepsOf<Contract>, Emits[number]>\n : StreamEventInput;\n\n/** What a caller APPENDS for one of a contract's OWNED events — the typed write on an entity\n * (`itx.agents.get(path).append(…)`, library.ts): the type string, the payload as its schema takes\n * it (`z.input`), a key and metadata; `ephemeral` only where the definition says so. Derived from\n * the catalog, so a payload field renamed in the contract is a type error at every call site. */\nexport type EventInput<Contract> = Contract extends { events: infer Events extends EventCatalog }\n ? {\n [Type in keyof Events & string]: {\n type: Type;\n payload: z.input<Events[Type][\"payloadSchema\"]>;\n idempotencyKey?: string;\n metadata?: Record<string, unknown>;\n } & (Events[Type] extends { ephemeral: true } ? { ephemeral: true } : { ephemeral?: never });\n }[keyof Events & string]\n : never;\n\n/** What `defineProcessorContract` returns: the base the engine reads, plus the events catalog and the\n * resolved deps. (Events are written LITERALLY at the call site — `itx.append({ type, payload })` —\n * so there is no event-builder here; the engine validates the payload against `payloadSchemaFor` at\n * reduce, and `ConsumedEvent`/`ProcessorState` give the reduce its types.) */\nexport type DefinedProcessorContract<\n StateSchema extends z.ZodType,\n Events extends EventCatalog,\n Consumes extends readonly string[],\n Deps extends readonly unknown[],\n Emits extends readonly string[] = readonly string[],\n> = ProcessorContract<z.output<StateSchema>> & {\n stateSchema: StateSchema;\n events: Events;\n // The literal consumes and emits tuples are preserved (not widened to string[]) so `ConsumedEvent`\n // and `EmittedEventInput` can map each type to its event; the base ProcessorContract only needs\n // `readonly string[]`.\n consumes: Consumes;\n emits: Emits;\n processorDeps: Deps;\n};\n\nexport function defineProcessorContract<\n const StateSchema extends z.ZodType,\n const Events extends EventCatalog = Record<string, never>,\n const Consumes extends readonly string[] = readonly string[],\n const Deps extends readonly { events: EventCatalog }[] = readonly [],\n const Emits extends readonly string[] = readonly string[],\n>(contract: {\n slug: string;\n version: string;\n description: string;\n /** Must parse `{}` — the initial state is `stateSchema.parse({})` (all fields defaulted). */\n stateSchema: StateSchema;\n /** The events this contract OWNS, keyed by durable type string. Omit for a kernel-generic\n * processor that types its own reduce through the `Event` param instead of an events catalog. */\n events?: Events;\n /** Other processors' contracts whose events this one may `consumes`/`emits` without owning. */\n processorDeps?: Deps;\n consumes: Consumes;\n emits: Emits;\n}): DefinedProcessorContract<StateSchema, Events, Consumes, Deps, Emits> {\n if (!contract.stateSchema.safeParse({}).success)\n throw new Error(`contract \"${contract.slug}\": stateSchema must parse {} (default every field)`);\n const events = (contract.events ?? {}) as Events;\n const processorDeps = (contract.processorDeps ?? []) as Deps;\n // One owner per event type: a local event may not shadow a dep's event, and two deps may not both\n // declare one. Otherwise `resolve` (and the runtime payload validation it backs) would pick just the\n // first while `ConsumedEvent`'s type union includes BOTH payload types — a second dep's events would\n // then validate against the wrong schema.\n const depEventTypes = new Set<string>();\n for (const dep of processorDeps as readonly { events: EventCatalog }[])\n for (const type of Object.keys(dep.events)) {\n if (type in events)\n throw new Error(`contract \"${contract.slug}\": event \"${type}\" is already owned by a dep`);\n if (depEventTypes.has(type))\n throw new Error(`contract \"${contract.slug}\": event \"${type}\" is declared by two deps`);\n depEventTypes.add(type);\n }\n const resolve = (type: string): EventDefinition | undefined =>\n events[type] ??\n (processorDeps as readonly { events: EventCatalog }[])\n .map((dep) => dep.events[type])\n .find(Boolean);\n return {\n slug: contract.slug,\n version: contract.version,\n description: contract.description,\n consumes: contract.consumes,\n emits: contract.emits,\n stateSchema: contract.stateSchema,\n events,\n processorDeps,\n initialState: () => contract.stateSchema.parse({}) as z.output<StateSchema>,\n payloadSchemaFor: (type: string) => resolve(type)?.payloadSchema,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run.mjs","names":[],"sources":["../../../src/next/stream/run.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/** `events.iterate.com/context/run-requested`: the whole script — the text of `async (itx) => …`.\n * The event's own OFFSET is the run's identity: the settlement names it back. */\nexport const RunRequested = z.object({ code: z.string().min(1) });\nexport type RunRequested = z.infer<typeof RunRequested>;\n/** `events.iterate.com/context/run-settled`: `requestOffset` names the request; `settlement` is\n * what the script returned (JSON — a round trip drops what JSON cannot carry) or how it failed —\n * `runtime` (the script threw, or returned what the log refuses) or `interrupted` (the context\n * restarted before it finished; it is not run again). */\nexport const RunSettled = z.object({\n requestOffset: z.number().int().positive(),\n settlement: z.discriminatedUnion(\"status\", [\n z.object({ status: z.literal(\"succeeded\"), result: z.unknown().optional() }),\n z.object({\n status: z.literal(\"failed\"),\n error: z.string(),\n failureKind: z.enum([\"runtime\", \"interrupted\"]),\n }),\n ]),\n});\nexport type RunSettled = z.infer<typeof RunSettled>;\nexport type RunSettlement = RunSettled[\"settlement\"];\n\n/** Script lifecycle events available to userspace processors. */\nexport const RunContract = {\n slug: \"context-runs\",\n version: \"1\",\n events: {\n \"events.iterate.com/context/run-requested\": {\n description:\n \"A script this context is asked to run once, against its own itx, by whoever appended it (source.principal); the event's offset is the run.\",\n payloadSchema: RunRequested,\n },\n \"events.iterate.com/context/run-settled\": {\n description:\n \"What the requested script returned, or how it failed; a run the context's restart interrupted is settled here too, never re-run.\",\n payloadSchema: RunSettled,\n },\n },\n initialState: () => ({}),\n};\n"],"mappings":";;;;AAIA,MAAa,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;;;;AAMhE,MAAa,aAAa,EAAE,OAAO;CACjC,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CACzC,YAAY,EAAE,mBAAmB,UAAU,CACzC,EAAE,OAAO;EAAE,QAAQ,EAAE,QAAQ,WAAW;EAAG,QAAQ,EAAE,QAAQ,CAAC,CAAC,SAAS;CAAE,CAAC,GAC3E,EAAE,OAAO;EACP,QAAQ,EAAE,QAAQ,QAAQ;EAC1B,OAAO,EAAE,OAAO;EAChB,aAAa,EAAE,KAAK,CAAC,WAAW,aAAa,CAAC;CAChD,CAAC,CACH,CAAC;AACH,CAAC;;AAKD,MAAa,cAAc;CACzB,MAAM;CACN,SAAS;CACT,QAAQ;EACN,4CAA4C;GAC1C,aACE;GACF,eAAe;EACjB;EACA,0CAA0C;GACxC,aACE;GACF,eAAe;EACjB;CACF;CACA,qBAAqB,CAAC;AACxB"}
|
package/dist/next-node.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type RpcStub } from "capnweb";
|
|
2
|
-
import type { IterateApi, SessionCredentials } from "./next/api.ts";
|
|
3
|
-
/** One OS Next connection. Dispose the owner to close every child capability and its socket.
|
|
4
|
-
* Operations are never retried: replaying a script could duplicate its effects. */
|
|
5
|
-
export type OsNextConnection = Disposable & {
|
|
6
|
-
session: Awaited<ReturnType<RpcStub<IterateApi>["authenticate"]>>;
|
|
7
|
-
closed: Promise<{
|
|
8
|
-
code: number;
|
|
9
|
-
reason: string;
|
|
10
|
-
}>;
|
|
11
|
-
};
|
|
12
|
-
export declare function connectOsNext(input: {
|
|
13
|
-
baseUrl: string;
|
|
14
|
-
auth: SessionCredentials;
|
|
15
|
-
}): Promise<OsNextConnection>;
|
package/dist/next-node.mjs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import WebSocket from "ws";
|
|
2
|
-
import { newWebSocketRpcSession } from "capnweb";
|
|
3
|
-
//#region src/next-node.ts
|
|
4
|
-
async function connectOsNext(input) {
|
|
5
|
-
const url = new URL("/api", input.baseUrl);
|
|
6
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error("OS Next URL must use http or https.");
|
|
7
|
-
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
8
|
-
const socket = new WebSocket(url.href, { handshakeTimeout: 15e3 });
|
|
9
|
-
const root = newWebSocketRpcSession(socket);
|
|
10
|
-
const closed = new Promise((resolve) => {
|
|
11
|
-
socket.once("close", (code, reason) => resolve({
|
|
12
|
-
code,
|
|
13
|
-
reason: reason.toString()
|
|
14
|
-
}));
|
|
15
|
-
});
|
|
16
|
-
socket.on("error", () => {});
|
|
17
|
-
let timeout;
|
|
18
|
-
try {
|
|
19
|
-
const session = await Promise.race([root.authenticate(input.auth), new Promise((_, reject) => {
|
|
20
|
-
timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("OS Next authentication timed out")), 2e4);
|
|
21
|
-
})]);
|
|
22
|
-
return {
|
|
23
|
-
session,
|
|
24
|
-
closed,
|
|
25
|
-
[Symbol.dispose]() {
|
|
26
|
-
try {
|
|
27
|
-
session[Symbol.dispose]();
|
|
28
|
-
} finally {
|
|
29
|
-
try {
|
|
30
|
-
root[Symbol.dispose]();
|
|
31
|
-
} finally {
|
|
32
|
-
socket.close();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
} catch (error) {
|
|
38
|
-
try {
|
|
39
|
-
root[Symbol.dispose]();
|
|
40
|
-
} finally {
|
|
41
|
-
socket.terminate();
|
|
42
|
-
}
|
|
43
|
-
throw error;
|
|
44
|
-
} finally {
|
|
45
|
-
clearTimeout(timeout);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//#endregion
|
|
49
|
-
export { connectOsNext };
|
|
50
|
-
|
|
51
|
-
//# sourceMappingURL=next-node.mjs.map
|
package/dist/next-node.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"next-node.mjs","names":[],"sources":["../src/next-node.ts"],"sourcesContent":["import { newWebSocketRpcSession, type RpcStub } from \"capnweb\";\nimport WebSocket from \"ws\";\nimport type { IterateApi, SessionCredentials } from \"./next/api.ts\";\n\n/** One OS Next connection. Dispose the owner to close every child capability and its socket.\n * Operations are never retried: replaying a script could duplicate its effects. */\nexport type OsNextConnection = Disposable & {\n session: Awaited<ReturnType<RpcStub<IterateApi>[\"authenticate\"]>>;\n closed: Promise<{ code: number; reason: string }>;\n};\n\n// Explicit return type keeps declaration emit from expanding capnweb's recursive mapped types.\nexport async function connectOsNext(input: {\n baseUrl: string;\n auth: SessionCredentials;\n}): Promise<OsNextConnection> {\n const url = new URL(\"/api\", input.baseUrl);\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Error(\"OS Next URL must use http or https.\");\n }\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const socket = new WebSocket(url.href, { handshakeTimeout: 15_000 });\n // ws implements the DOM event/send/close interface consumed by capnweb.\n const root = newWebSocketRpcSession<IterateApi>(socket as unknown as globalThis.WebSocket);\n const closed = new Promise<{ code: number; reason: string }>((resolve) => {\n socket.once(\"close\", (code, reason) => resolve({ code, reason: reason.toString() }));\n });\n // The transport surfaces failures through RPC and `closed`; ws also emits an EventEmitter error.\n socket.on(\"error\", () => {});\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n const session = await Promise.race([\n root.authenticate(input.auth),\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(new Error(\"OS Next authentication timed out\")), 20_000);\n }),\n ]);\n return {\n session,\n closed,\n [Symbol.dispose]() {\n try {\n session[Symbol.dispose]();\n } finally {\n try {\n root[Symbol.dispose]();\n } finally {\n socket.close();\n }\n }\n },\n };\n } catch (error) {\n try {\n root[Symbol.dispose]();\n } finally {\n socket.terminate();\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n}\n"],"mappings":";;;AAYA,eAAsB,cAAc,OAGN;CAC5B,MAAM,MAAM,IAAI,IAAI,QAAQ,MAAM,OAAO;CACzC,IAAI,IAAI,aAAa,YAAY,IAAI,aAAa,SAChD,MAAM,IAAI,MAAM,qCAAqC;CAEvD,IAAI,WAAW,IAAI,aAAa,WAAW,SAAS;CACpD,MAAM,SAAS,IAAI,UAAU,IAAI,MAAM,EAAE,kBAAkB,KAAO,CAAC;CAEnE,MAAM,OAAO,uBAAmC,MAAyC;CACzF,MAAM,SAAS,IAAI,SAA2C,YAAY;EACxE,OAAO,KAAK,UAAU,MAAM,WAAW,QAAQ;GAAE;GAAM,QAAQ,OAAO,SAAS;EAAE,CAAC,CAAC;CACrF,CAAC;CAED,OAAO,GAAG,eAAe,CAAC,CAAC;CAC3B,IAAI;CACJ,IAAI;EACF,MAAM,UAAU,MAAM,QAAQ,KAAK,CACjC,KAAK,aAAa,MAAM,IAAI,GAC5B,IAAI,SAAgB,GAAG,WAAW;GAChC,UAAU,iBAAiB,uBAAO,IAAI,MAAM,kCAAkC,CAAC,GAAG,GAAM;EAC1F,CAAC,CACH,CAAC;EACD,OAAO;GACL;GACA;GACA,CAAC,OAAO,WAAW;IACjB,IAAI;KACF,QAAQ,OAAO,QAAQ,CAAC;IAC1B,UAAU;KACR,IAAI;MACF,KAAK,OAAO,QAAQ,CAAC;KACvB,UAAU;MACR,OAAO,MAAM;KACf;IACF;GACF;EACF;CACF,SAAS,OAAO;EACd,IAAI;GACF,KAAK,OAAO,QAAQ,CAAC;EACvB,UAAU;GACR,OAAO,UAAU;EACnB;EACA,MAAM;CACR,UAAU;EACR,aAAa,OAAO;CACtB;AACF"}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
//#region src/processors/processor-host-capabilities.ts
|
|
2
|
-
/**
|
|
3
|
-
* The two live capabilities every processor host hands the stream alongside
|
|
4
|
-
* its event-batch callback, shared by the DO registry and browser runtime so they cannot
|
|
5
|
-
* drift. `getRuntimeState` assembles the published shape from its two honest
|
|
6
|
-
* sources: the SNAPSHOT from the runner (`opts.snapshot` — the cursor owner),
|
|
7
|
-
* the `runtime` bag from the processor, with the self-measured metrics merged
|
|
8
|
-
* in host-side so a subclass override cannot accidentally drop them.
|
|
9
|
-
*/
|
|
10
|
-
function hostRuntimeCapabilities(processor, opts) {
|
|
11
|
-
return {
|
|
12
|
-
getRuntimeState: async () => {
|
|
13
|
-
const contributed = await processor.getRuntimeState();
|
|
14
|
-
const metrics = processor.eventConsumptionMetrics.report();
|
|
15
|
-
return {
|
|
16
|
-
snapshot: await opts.snapshot(),
|
|
17
|
-
runtime: {
|
|
18
|
-
...contributed.runtime,
|
|
19
|
-
metrics
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
ping: (input) => {
|
|
24
|
-
const t1 = opts.now();
|
|
25
|
-
const oneWayEstimateMs = opts.oneWayEstimateMs?.();
|
|
26
|
-
if (oneWayEstimateMs !== void 0) processor.eventConsumptionMetrics.notePingObserved({
|
|
27
|
-
t0: input.t0,
|
|
28
|
-
t1,
|
|
29
|
-
oneWayEstimateMs
|
|
30
|
-
});
|
|
31
|
-
return {
|
|
32
|
-
t0: input.t0,
|
|
33
|
-
t1,
|
|
34
|
-
t2: opts.now()
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
/** Serializable processor contract carried by server and browser hosts. */
|
|
40
|
-
function announceContract(contract) {
|
|
41
|
-
return {
|
|
42
|
-
slug: contract.slug,
|
|
43
|
-
version: contract.version,
|
|
44
|
-
description: contract.description,
|
|
45
|
-
consumes: [...contract.consumes],
|
|
46
|
-
emits: [...contract.emits],
|
|
47
|
-
ownedEvents: Object.entries(contract.events).map(([type, definition]) => ({
|
|
48
|
-
type,
|
|
49
|
-
...definition.description === void 0 ? {} : { description: definition.description }
|
|
50
|
-
}))
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
export { hostRuntimeCapabilities as n, announceContract as t };
|
|
55
|
-
|
|
56
|
-
//# sourceMappingURL=processor-host-capabilities-BMFH3KTM.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"processor-host-capabilities-BMFH3KTM.mjs","names":[],"sources":["../src/processors/processor-host-capabilities.ts"],"sourcesContent":["import type { z } from \"zod\";\nimport type {\n GetProcessorRuntimeState,\n ProcessorSnapshot,\n StreamPingInput,\n StreamConnectionPing,\n} from \"./rpc-types.ts\";\nimport type { ProcessorContractAnnouncement } from \"./processor-contracts.ts\";\nimport type { EventConsumptionMetrics } from \"./event-consumption-metrics.ts\";\nimport type { ProcessorRuntimeContribution } from \"./stream-processor.ts\";\n\n/**\n * The processor surface shared by the Durable Object registry\n * (stream-processor-registry.ts) and the browser host (stream-browser-store.ts):\n * the contract description returned by `wakeStreamProcessor`, the\n * processor-contributed runtime bag, and self-measured event-consumption metrics.\n * Deliberately NOT the drive surface — cursors, snapshots, and delivery live\n * in the StreamProcessorRunner, which reaches the protected hooks through\n * `StreamProcessor.runnerHooks`.\n */\nexport type AnyHostedProcessor = {\n contract: {\n slug: string;\n version: string;\n description: string;\n stateSchema: z.ZodType;\n consumes: readonly string[];\n emits: readonly string[];\n events: Record<string, { description?: string; payloadSchema?: unknown }>;\n };\n /** The processor-contributed runtime bag; the snapshot half comes from the runner. */\n getRuntimeState(): Promise<ProcessorRuntimeContribution>;\n readonly eventConsumptionMetrics: Pick<\n EventConsumptionMetrics,\n \"report\" | \"notePingObserved\" | \"noteAppendCommitted\" | \"clearPendingAppends\"\n >;\n};\n\n/**\n * The two live capabilities every processor host hands the stream alongside\n * its event-batch callback, shared by the DO registry and browser runtime so they cannot\n * drift. `getRuntimeState` assembles the published shape from its two honest\n * sources: the SNAPSHOT from the runner (`opts.snapshot` — the cursor owner),\n * the `runtime` bag from the processor, with the self-measured metrics merged\n * in host-side so a subclass override cannot accidentally drop them.\n */\nexport function hostRuntimeCapabilities(\n processor: AnyHostedProcessor,\n opts: {\n now: () => number;\n /** The driving runner's committed snapshot (`() => runner.snapshot()`). */\n snapshot: () => Promise<ProcessorSnapshot<unknown>>;\n oneWayEstimateMs?: () => number | undefined;\n },\n): { getRuntimeState: GetProcessorRuntimeState; ping: StreamConnectionPing } {\n return {\n getRuntimeState: async () => {\n const contributed = await processor.getRuntimeState();\n const metrics = processor.eventConsumptionMetrics.report();\n return {\n snapshot: await opts.snapshot(),\n runtime: { ...contributed.runtime, metrics },\n };\n },\n ping: (input: StreamPingInput) => {\n const t1 = opts.now();\n const oneWayEstimateMs = opts.oneWayEstimateMs?.();\n if (oneWayEstimateMs !== undefined) {\n processor.eventConsumptionMetrics.notePingObserved({ t0: input.t0, t1, oneWayEstimateMs });\n }\n return { t0: input.t0, t1, t2: opts.now() };\n },\n };\n}\n\n/** Serializable processor contract carried by server and browser hosts. */\nexport function announceContract(contract: {\n slug: string;\n version: string;\n description: string;\n consumes: readonly string[];\n emits: readonly string[];\n events: Record<string, { description?: string; payloadSchema?: unknown }>;\n}): ProcessorContractAnnouncement {\n return {\n slug: contract.slug,\n version: contract.version,\n description: contract.description,\n consumes: [...contract.consumes],\n emits: [...contract.emits],\n ownedEvents: Object.entries(contract.events).map(([type, definition]) => ({\n type,\n ...(definition.description === undefined ? {} : { description: definition.description }),\n })),\n };\n}\n"],"mappings":";;;;;;;;;AA8CA,SAAgB,wBACd,WACA,MAM2E;CAC3E,OAAO;EACL,iBAAiB,YAAY;GAC3B,MAAM,cAAc,MAAM,UAAU,gBAAgB;GACpD,MAAM,UAAU,UAAU,wBAAwB,OAAO;GACzD,OAAO;IACL,UAAU,MAAM,KAAK,SAAS;IAC9B,SAAS;KAAE,GAAG,YAAY;KAAS;IAAQ;GAC7C;EACF;EACA,OAAO,UAA2B;GAChC,MAAM,KAAK,KAAK,IAAI;GACpB,MAAM,mBAAmB,KAAK,mBAAmB;GACjD,IAAI,qBAAqB,KAAA,GACvB,UAAU,wBAAwB,iBAAiB;IAAE,IAAI,MAAM;IAAI;IAAI;GAAiB,CAAC;GAE3F,OAAO;IAAE,IAAI,MAAM;IAAI;IAAI,IAAI,KAAK,IAAI;GAAE;EAC5C;CACF;AACF;;AAGA,SAAgB,iBAAiB,UAOC;CAChC,OAAO;EACL,MAAM,SAAS;EACf,SAAS,SAAS;EAClB,aAAa,SAAS;EACtB,UAAU,CAAC,GAAG,SAAS,QAAQ;EAC/B,OAAO,CAAC,GAAG,SAAS,KAAK;EACzB,aAAa,OAAO,QAAQ,SAAS,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,iBAAiB;GACxE;GACA,GAAI,WAAW,gBAAgB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,WAAW,YAAY;EACxF,EAAE;CACJ;AACF"}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { ProcessorStream } from "./stream-handle.ts";
|
|
2
|
-
import type { ProcessorProgressStore, ProcessorRecovery } from "./stream-processor-runner.ts";
|
|
3
|
-
/** The two-cursor progress record ({@link ProcessorProgress}). */
|
|
4
|
-
export declare const processorProgressKey: (name: string) => string;
|
|
5
|
-
/** The per-runner keepalive record ({@link KeepaliveRecord}). */
|
|
6
|
-
export declare const processorKeepaliveKey: (name: string) => string;
|
|
7
|
-
/**
|
|
8
|
-
* The runner's durable progress store over DO KV (`storage.kv` — synchronous,
|
|
9
|
-
* single-threaded isolate, so read-check-write is atomic without awaits).
|
|
10
|
-
*/
|
|
11
|
-
export declare function durableObjectProgressStore<State>(args: {
|
|
12
|
-
storage: DurableObjectStorage;
|
|
13
|
-
/** The registered processor name (subscription name = contract slug) —
|
|
14
|
-
* keys the progress record. */
|
|
15
|
-
name: string;
|
|
16
|
-
/** Synchronously clear related projections when the source stream is replaced. */
|
|
17
|
-
resetForStream?: () => void;
|
|
18
|
-
/**
|
|
19
|
-
* An optional bounded reduction cache. Processing cursors always remain
|
|
20
|
-
* durable; a cold runner refolds reduce-only from initialState when the
|
|
21
|
-
* predicate declines a large cache.
|
|
22
|
-
*/
|
|
23
|
-
reductionCache?: {
|
|
24
|
-
shouldCacheReduction(state: State): boolean;
|
|
25
|
-
initialState(): State;
|
|
26
|
-
};
|
|
27
|
-
}): ProcessorProgressStore<State>;
|
|
28
|
-
/**
|
|
29
|
-
* The runner's recovery adapter for a Durable Object: wraps ONE
|
|
30
|
-
* {@link ProcessorKeepalive} for THIS runner (per-runner recovery identity, so
|
|
31
|
-
* a revival names exactly which processor owed work). The keepalive machinery
|
|
32
|
-
* — mark-before-work, bounded backoff, quiet-clean reset,
|
|
33
|
-
* deploy-version reset, wedged-work detection — is reused wholesale, never
|
|
34
|
-
* reimplemented.
|
|
35
|
-
*
|
|
36
|
-
* DO-shaped seams are INJECTED, not reached for:
|
|
37
|
-
* - `armAlarm` — the hosting registry's alarm slice for this runner. A DO
|
|
38
|
-
* has ONE alarm; the registry merges every runner's desire (plus its own)
|
|
39
|
-
* and arms the earliest, exactly like the host's `setAlarmSlice`. This
|
|
40
|
-
* adapter never touches `storage.setAlarm`.
|
|
41
|
-
* - `waitUntil` — calls the hosting DO's `ctx.waitUntil`, keeping the
|
|
42
|
-
* incarnation alive while tracked work runs.
|
|
43
|
-
*
|
|
44
|
-
* Revival appends the core `stream/processor-revived` fact (ONE type for
|
|
45
|
-
* every processor — {@link STREAM_PROCESSOR_REVIVED_EVENT_TYPE}; the payload's
|
|
46
|
-
* `processorSlug` and the idempotency key carry the per-processor identity)
|
|
47
|
-
* to the stream and STOPS: the append wakes the source stream's event sender (its
|
|
48
|
-
* `woken` handlers cold-boot the stream DO if the deploy evicted it too), and
|
|
49
|
-
* wake-mode delivery reaches head and guarantees a turn: either the contract
|
|
50
|
-
* consumes the fact and receives it, or the runner supplies its eventless
|
|
51
|
-
* `processEvent(event: null, caughtUp: true)` pass. No self-driven catch-up
|
|
52
|
-
* here, unlike the host's `catchUpInternal` loop: delivery has ONE entrypoint.
|
|
53
|
-
*
|
|
54
|
-
* Construction re-issues a persisted armed desire through `armAlarm` (the
|
|
55
|
-
* host's boot-time reconcile): a platform `setAlarm` that failed after the KV
|
|
56
|
-
* record committed — or an eviction in the fire→re-arm window — would
|
|
57
|
-
* otherwise leave the only thing that revives this DO permanently lost.
|
|
58
|
-
*/
|
|
59
|
-
export declare function durableObjectRecovery(args: {
|
|
60
|
-
storage: DurableObjectStorage;
|
|
61
|
-
/** The registered processor name (subscription name = contract slug) —
|
|
62
|
-
* keys the per-runner keepalive record and the revival fact's idempotency
|
|
63
|
-
* key, and fills the revival payload's `processorSlug`. */
|
|
64
|
-
name: string;
|
|
65
|
-
/** The processor's home stream: revived facts and crash-loop evidence land here. */
|
|
66
|
-
stream: ProcessorStream;
|
|
67
|
-
/** Worker deploy version; a change resets the keepalive's crash-loop budget
|
|
68
|
-
* (the antidote deploy). Pass `workerVersion(env)`. REQUIRED for the same
|
|
69
|
-
* reason the host requires it: a silent default could never take the
|
|
70
|
-
* version-reset code path. */
|
|
71
|
-
version: string;
|
|
72
|
-
/** The registry's alarm-slice seam for this runner (null = disarm). */
|
|
73
|
-
armAlarm: (atMs: number | null) => void;
|
|
74
|
-
/** The hosting DO's `ctx.waitUntil` — keeps the incarnation alive while
|
|
75
|
-
* tracked work runs. */
|
|
76
|
-
waitUntil: (work: Promise<unknown>) => void;
|
|
77
|
-
/** Injected clock for the test harness; production uses Date.now. */
|
|
78
|
-
now?: () => number;
|
|
79
|
-
}): ProcessorRecovery;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { type LatencyStats } from "./stream-runtime-metrics.ts";
|
|
2
|
-
/** The `runtime.metrics` slice a host reports through `getRuntimeState()`. */
|
|
3
|
-
export type EventConsumptionMetricsReport = {
|
|
4
|
-
/** ISO timestamp when this host runtime started measuring (in-memory; resets on reload). */
|
|
5
|
-
measuredSince: string;
|
|
6
|
-
/**
|
|
7
|
-
* The full consume-your-own-appends loop, one clock: this host called
|
|
8
|
-
* `append()` at t0, and its OWN event connection later delivered (and the
|
|
9
|
-
* host fully ingested) THAT COMMITTED OFFSET.
|
|
10
|
-
*
|
|
11
|
-
* Samples only exist for appends that came back. An append of a type this
|
|
12
|
-
* host does not consume, or one the stream never hands back, contributes
|
|
13
|
-
* NOTHING here — it is not a slow loop, it is no loop, and the two must not
|
|
14
|
-
* be spelled the same. `null` is "no such append observed", never a
|
|
15
|
-
* fabricated number.
|
|
16
|
-
*/
|
|
17
|
-
consumeOwnAppendMs: LatencyStats | null;
|
|
18
|
-
/** `append()` call → commit acknowledged (the RPC round trip incl. commit). */
|
|
19
|
-
appendRoundTripMs: LatencyStats | null;
|
|
20
|
-
/**
|
|
21
|
-
* Age of the newest event in each batch when the host finished ingesting
|
|
22
|
-
* it, i.e. commit-to-consumed for OTHER producers' events too. Crosses
|
|
23
|
-
* clocks (event `createdAt` is stream time), corrected by the ping-derived
|
|
24
|
-
* offset estimate when one exists — an estimate, and labeled as such in UIs.
|
|
25
|
-
*/
|
|
26
|
-
deliveryAgeMs: LatencyStats | null;
|
|
27
|
-
/** Time the host spent ingesting each delivered batch (fold/SQLite write). */
|
|
28
|
-
ingestMs: LatencyStats | null;
|
|
29
|
-
batchesIngested: number;
|
|
30
|
-
eventsIngested: number;
|
|
31
|
-
/** Estimated host−stream clock skew (ms) from observed pings; `null` until pinged. */
|
|
32
|
-
clockOffsetMs: number | null;
|
|
33
|
-
};
|
|
34
|
-
export declare class EventConsumptionMetrics {
|
|
35
|
-
#private;
|
|
36
|
-
constructor(nowMs: number);
|
|
37
|
-
/**
|
|
38
|
-
* An `append()` this host issued resolved: `t0` is when the host called it,
|
|
39
|
-
* `atMs` is when the commit came back, and `maxCommittedOffset` is the
|
|
40
|
-
* highest committed offset that CAN come back to this host — the caller's
|
|
41
|
-
* judgement, because only the caller knows what it consumes. `null` means
|
|
42
|
-
* the append carried nothing this host will ever be delivered, and is timed
|
|
43
|
-
* for its round trip alone.
|
|
44
|
-
*/
|
|
45
|
-
noteAppendCommitted(args: {
|
|
46
|
-
maxCommittedOffset: number | null;
|
|
47
|
-
t0: number;
|
|
48
|
-
atMs: number;
|
|
49
|
-
}): void;
|
|
50
|
-
/** One delivered batch fully ingested (fold applied / SQLite write done). */
|
|
51
|
-
noteBatchIngested(args: {
|
|
52
|
-
/** Highest offset the host has now ingested through (its cursor, not just this batch). */
|
|
53
|
-
ingestedThroughOffset: number;
|
|
54
|
-
/**
|
|
55
|
-
* The offsets this batch actually CARRIED.
|
|
56
|
-
*
|
|
57
|
-
* The cursor above sweeps past rows this host was never handed — a
|
|
58
|
-
* filtered subscription skips them durably — so it cannot tell an own
|
|
59
|
-
* append that came back from one that never will. These can.
|
|
60
|
-
*/
|
|
61
|
-
ingestedOffsets: readonly number[];
|
|
62
|
-
/** `Date.parse(newestEvent.createdAt)` for the newest event in the batch, if any. */
|
|
63
|
-
newestEventCreatedAtMs?: number;
|
|
64
|
-
/** When the host started ingesting this batch. */
|
|
65
|
-
ingestStartedAtMs: number;
|
|
66
|
-
atMs: number;
|
|
67
|
-
}): void;
|
|
68
|
-
/**
|
|
69
|
-
* The stream pinged this host: `t0` is the stream's send time (stream
|
|
70
|
-
* clock), `t1` the host's receive time (host clock). With a one-way-delay
|
|
71
|
-
* estimate (half the host's measured transport RTT, when it has one) this
|
|
72
|
-
* yields the host−stream clock offset used to correct delivery ages.
|
|
73
|
-
*/
|
|
74
|
-
notePingObserved(args: {
|
|
75
|
-
t0: number;
|
|
76
|
-
t1: number;
|
|
77
|
-
oneWayEstimateMs?: number;
|
|
78
|
-
}): void;
|
|
79
|
-
/** The host's event connection reopened: in-flight own-append correlations are void. */
|
|
80
|
-
clearPendingAppends(): void;
|
|
81
|
-
report(): EventConsumptionMetricsReport;
|
|
82
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
type EventBody = {
|
|
2
|
-
type: string;
|
|
3
|
-
payload?: unknown;
|
|
4
|
-
metadata?: unknown;
|
|
5
|
-
ephemeral?: boolean | undefined;
|
|
6
|
-
};
|
|
7
|
-
export declare function idempotencyConflictMessage(idempotencyKey: string, existingOffset: number): string;
|
|
8
|
-
export declare function isIdempotencyConflict(error: unknown): boolean;
|
|
9
|
-
/** Whether a requested append names the SAME event an idempotency key already committed. */
|
|
10
|
-
export declare function sameIdempotentEvent(existing: EventBody, requested: EventBody): boolean;
|
|
11
|
-
/** Structural JSON equality (key-order-insensitive). */
|
|
12
|
-
export declare function jsonValuesEqual(left: unknown, right: unknown): boolean;
|
|
13
|
-
export {};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export * from "./schemas.ts";
|
|
2
|
-
export * from "./rpc-types.ts";
|
|
3
|
-
export * from "./stream-handle.ts";
|
|
4
|
-
export * from "./idempotency.ts";
|
|
5
|
-
export * from "./processor-contracts.ts";
|
|
6
|
-
export * from "./stream-processor.ts";
|
|
7
|
-
export * from "./stream-processor-runner.ts";
|
|
8
|
-
export * from "./stream-processor-keepalive.ts";
|
|
9
|
-
export * from "./processor-host-capabilities.ts";
|
|
10
|
-
export * from "./event-consumption-metrics.ts";
|
|
11
|
-
export * from "./stream-runtime-metrics.ts";
|
|
12
|
-
export * from "./prompt-sections.ts";
|