iterate 0.2.6 → 0.3.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 +86 -76
- package/THIRD_PARTY_NOTICES.md +55 -0
- package/bin/iterate.js +18 -3
- package/dist/api-url-B6404M82.mjs +17 -0
- package/dist/api-url-B6404M82.mjs.map +1 -0
- package/dist/app-ref-BipL0feU.mjs +35 -0
- package/dist/app-ref-BipL0feU.mjs.map +1 -0
- package/dist/app-ref-C1CrgXqX.mjs +7 -0
- package/dist/app-ref-C1CrgXqX.mjs.map +1 -0
- package/dist/app-ref-DYai_om1.mjs +7 -0
- package/dist/app-ref-DYai_om1.mjs.map +1 -0
- package/dist/cli-D0c-pDL_.mjs +1010 -0
- package/dist/cli-D0c-pDL_.mjs.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.mjs +4 -0
- package/dist/cloudflare-BTm90gQ4.mjs +951 -0
- package/dist/cloudflare-BTm90gQ4.mjs.map +1 -0
- package/dist/contract-s4FW4eES.mjs +309 -0
- package/dist/contract-s4FW4eES.mjs.map +1 -0
- package/dist/document-review/index.d.ts +5 -0
- package/dist/document-review/types.d.ts +107 -0
- package/dist/document-review.mjs +7015 -0
- package/dist/document-review.mjs.map +1 -0
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs +205 -0
- package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +1 -0
- package/dist/idempotency-DleloJNt.mjs +28 -0
- package/dist/idempotency-DleloJNt.mjs.map +1 -0
- package/dist/index.mjs +1 -1
- package/dist/itx/api-url.d.ts +6 -0
- package/dist/itx/itx-node-client.d.ts +65 -0
- package/dist/itx/itx-session.d.ts +215 -0
- package/dist/itx/owned-rpc-session.d.ts +14 -0
- package/dist/itx/query-client.d.ts +10 -0
- package/dist/itx-api.generated.d.ts +6195 -0
- package/dist/itx-session-sjud8GiT.mjs +534 -0
- package/dist/itx-session-sjud8GiT.mjs.map +1 -0
- package/dist/live-state-BJNqOwFw.mjs +299 -0
- package/dist/live-state-BJNqOwFw.mjs.map +1 -0
- package/dist/next/api.d.ts +479 -0
- package/dist/next/api.mjs +0 -0
- package/dist/next/app-server.d.ts +44 -0
- package/dist/next/app-server.mjs +479 -0
- package/dist/next/app-server.mjs.map +1 -0
- package/dist/next/app-session.d.ts +49 -0
- package/dist/next/app-session.mjs +238 -0
- package/dist/next/app-session.mjs.map +1 -0
- package/dist/next/app.d.ts +29 -0
- package/dist/next/app.mjs +141 -0
- package/dist/next/app.mjs.map +1 -0
- package/dist/next/client/live-state.d.ts +63 -0
- package/dist/next/client/oauth.d.ts +12 -0
- package/dist/next/client/react.d.ts +109 -0
- package/dist/next/client/socket.d.ts +6 -0
- package/dist/next/client.mjs +156 -0
- package/dist/next/client.mjs.map +1 -0
- package/dist/next/expression.d.ts +146 -0
- package/dist/next/expression.mjs +399 -0
- package/dist/next/expression.mjs.map +1 -0
- package/dist/next/lib.d.ts +56 -0
- package/dist/next/lib.mjs +199 -0
- package/dist/next/lib.mjs.map +1 -0
- package/dist/next/oauth-scopes.d.ts +32 -0
- package/dist/next/oauth-scopes.mjs +40 -0
- package/dist/next/oauth-scopes.mjs.map +1 -0
- package/dist/next/oauth.mjs +29 -0
- package/dist/next/oauth.mjs.map +1 -0
- package/dist/next/principal.d.ts +64 -0
- package/dist/next/principal.mjs +98 -0
- package/dist/next/principal.mjs.map +1 -0
- package/dist/next/project-ingress.d.ts +37 -0
- package/dist/next/project-ingress.mjs +75 -0
- package/dist/next/project-ingress.mjs.map +1 -0
- package/dist/next/react.mjs +285 -0
- package/dist/next/react.mjs.map +1 -0
- package/dist/next/sdk/auth.d.ts +5 -0
- package/dist/next/sdk/index.d.ts +112 -0
- package/dist/next/sdk.mjs +139 -0
- package/dist/next/sdk.mjs.map +1 -0
- package/dist/next/stream/processor.d.ts +378 -0
- package/dist/next/stream/processor.mjs +582 -0
- package/dist/next/stream/processor.mjs.map +1 -0
- package/dist/next/stream/run.d.ts +58 -0
- package/dist/next/stream/run.mjs +40 -0
- package/dist/next/stream/run.mjs.map +1 -0
- package/dist/next-node.d.ts +15 -0
- package/dist/next-node.mjs +51 -0
- package/dist/next-node.mjs.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.mjs +185 -0
- package/dist/node.mjs.map +1 -0
- package/dist/processor-host-capabilities-BMFH3KTM.mjs +56 -0
- package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +1 -0
- package/dist/processors/cloudflare.d.ts +3 -0
- package/dist/processors/durable-object-processor-durability.d.ts +79 -0
- package/dist/processors/event-consumption-metrics.d.ts +82 -0
- package/dist/processors/idempotency.d.ts +13 -0
- package/dist/processors/index.d.ts +12 -0
- package/dist/processors/processor-contracts.d.ts +342 -0
- package/dist/processors/processor-facet.d.ts +186 -0
- package/dist/processors/processor-host-capabilities.d.ts +60 -0
- package/dist/processors/prompt-sections.d.ts +17 -0
- package/dist/processors/rpc-types.d.ts +515 -0
- package/dist/processors/schemas.d.ts +102 -0
- package/dist/processors/stream-handle.d.ts +45 -0
- package/dist/processors/stream-processor-keepalive.d.ts +95 -0
- package/dist/processors/stream-processor-registry.d.ts +233 -0
- package/dist/processors/stream-processor-runner.d.ts +289 -0
- package/dist/processors/stream-processor.d.ts +339 -0
- package/dist/processors/stream-runtime-metrics.d.ts +107 -0
- package/dist/processors/testing.d.ts +302 -0
- package/dist/processors-BoNyeBfQ.mjs +10 -0
- package/dist/processors-BoNyeBfQ.mjs.map +1 -0
- package/dist/processors-cloudflare.mjs +3 -0
- package/dist/processors-testing.mjs +435 -0
- package/dist/processors-testing.mjs.map +1 -0
- package/dist/processors.mjs +52 -0
- package/dist/processors.mjs.map +1 -0
- package/dist/protocol-DnK_f2m6.mjs +251 -0
- package/dist/protocol-DnK_f2m6.mjs.map +1 -0
- package/dist/sdk/capnweb/index.d.ts +2 -0
- package/dist/sdk/capnweb/live-state/compact.d.ts +5 -0
- package/dist/sdk/capnweb/live-state/diff.d.ts +41 -0
- package/dist/sdk/capnweb/live-state/engine.d.ts +44 -0
- package/dist/sdk/capnweb/live-state/index.d.ts +41 -0
- package/dist/sdk/capnweb/live-state/protocol.d.ts +87 -0
- package/dist/sdk/capnweb/live-state/retain.d.ts +23 -0
- package/dist/sdk/capnweb/live-state/store.d.ts +20 -0
- package/dist/sdk/capnweb/live-state/types.d.ts +11 -0
- package/dist/sdk/capnweb/react.d.ts +45 -0
- package/dist/sdk/capnweb/react.mjs +316 -0
- package/dist/sdk/capnweb/react.mjs.map +1 -0
- package/dist/sdk/capnweb.mjs +4 -0
- package/dist/sdk/itx/react.d.ts +191 -0
- package/dist/sdk/itx/react.mjs +383 -0
- package/dist/sdk/itx/react.mjs.map +1 -0
- package/dist/sdk-DMB-IM11.mjs +933 -0
- package/dist/sdk-DMB-IM11.mjs.map +1 -0
- package/dist/sdk.d.ts +339 -0
- package/dist/sdk.mjs +2 -0
- package/dist/serve-itx.d.ts +46 -0
- package/dist/starter-apps/flake-dashboard/app-ref.d.ts +31 -0
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs +1055 -0
- package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/flake-dashboard/contract.d.ts +4839 -0
- package/dist/starter-apps/flake-dashboard/contract.mjs +2 -0
- package/dist/starter-apps/flake-dashboard/index.d.ts +17 -0
- package/dist/starter-apps/flake-dashboard/index.mjs +56 -0
- package/dist/starter-apps/flake-dashboard/index.mjs.map +1 -0
- package/dist/starter-apps/flake-dashboard/worker.d.ts +4607 -0
- package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +8914 -0
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs +17987 -0
- package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/github-ai-linter/contract.d.ts +9193 -0
- package/dist/starter-apps/github-ai-linter/index.d.ts +10 -0
- package/dist/starter-apps/github-ai-linter/index.mjs +36 -0
- package/dist/starter-apps/github-ai-linter/index.mjs.map +1 -0
- package/dist/starter-apps/github-ai-linter/prompt.d.ts +13 -0
- package/dist/starter-apps/github-ai-linter/review-bot.d.ts +808 -0
- package/dist/starter-apps/github-ai-linter/rules.d.ts +34 -0
- package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +19 -0
- package/dist/starter-apps/github-ai-linter/worker.d.ts +19 -0
- package/dist/starter-apps/github-ai-linter/worker.mjs +947 -0
- package/dist/starter-apps/github-ai-linter/worker.mjs.map +1 -0
- package/dist/starter-apps/guestbook/app-ref.d.ts +27 -0
- package/dist/starter-apps/guestbook/client.d.ts +7 -0
- package/dist/starter-apps/guestbook/client.mjs +59 -0
- package/dist/starter-apps/guestbook/configured-worker.mjs +205 -0
- package/dist/starter-apps/guestbook/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/guestbook/index.d.ts +9 -0
- package/dist/starter-apps/guestbook/index.mjs +31 -0
- package/dist/starter-apps/guestbook/index.mjs.map +1 -0
- package/dist/starter-apps/guestbook/processor.d.ts +2267 -0
- package/dist/starter-apps/guestbook/worker.d.ts +26 -0
- package/dist/starter-apps/guestbook/worker.mjs +191 -0
- package/dist/starter-apps/guestbook/worker.mjs.map +1 -0
- package/dist/starter-apps/media/configured-worker.mjs +577 -0
- package/dist/starter-apps/media/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/media/index.mjs +36 -0
- package/dist/starter-apps/media/index.mjs.map +1 -0
- package/dist/starter-apps/media/ref.mjs +20 -0
- package/dist/starter-apps/media/ref.mjs.map +1 -0
- package/dist/starter-apps/media/worker.mjs +579 -0
- package/dist/starter-apps/media/worker.mjs.map +1 -0
- package/dist/starter-apps/notes/configured-worker.mjs +6134 -0
- package/dist/starter-apps/notes/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/notes/index.mjs +23 -0
- package/dist/starter-apps/notes/index.mjs.map +1 -0
- package/dist/starter-apps/notes/ref.mjs +21 -0
- package/dist/starter-apps/notes/ref.mjs.map +1 -0
- package/dist/starter-apps/notes/worker.mjs +427 -0
- package/dist/starter-apps/notes/worker.mjs.map +1 -0
- package/dist/starter-apps/todo/client.mjs +59 -0
- package/dist/starter-apps/todo/configured-worker.mjs +2864 -0
- package/dist/starter-apps/todo/configured-worker.mjs.map +1 -0
- package/dist/starter-apps/todo/index.d.ts +8 -0
- package/dist/starter-apps/todo/index.mjs +29 -0
- package/dist/starter-apps/todo/index.mjs.map +1 -0
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs +2082 -0
- package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +1 -0
- package/dist/usingCtx-inzbY1Qz.mjs +57 -0
- package/dist/usingCtx-mZx5nsAW.mjs +11800 -0
- package/dist/usingCtx-mZx5nsAW.mjs.map +1 -0
- package/dist/worker-ref-DZxPDmb_.mjs +390 -0
- package/dist/worker-ref-DZxPDmb_.mjs.map +1 -0
- package/menubar/Iterate.entitlements +12 -0
- package/menubar/Iterate.swift +914 -0
- package/menubar/IterateIcon.swift +145 -0
- package/menubar/README.md +28 -0
- package/menubar/build-menubar-app.sh +59 -0
- package/package.json +235 -18
- package/dist/cli-DMS4kJph.mjs +0 -868
- package/dist/cli-DMS4kJph.mjs.map +0 -1
- package/dist/config-DtnR7Lv7.mjs +0 -170
- package/dist/config-DtnR7Lv7.mjs.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/stream-tui/agent-chat-terminal.d.mts +0 -1
- package/dist/stream-tui/agent-chat-terminal.mjs +0 -933
- package/dist/stream-tui/agent-chat-terminal.mjs.map +0 -1
|
@@ -0,0 +1,951 @@
|
|
|
1
|
+
import { o as StreamProcessorRunner } from "./stream-processor-keepalive-DAQTP6m3.mjs";
|
|
2
|
+
import { n as durableObjectRecovery, t as durableObjectProgressStore } from "./durable-object-processor-durability-CNsTjAJS.mjs";
|
|
3
|
+
import { n as hostRuntimeCapabilities, t as announceContract } from "./processor-host-capabilities-BMFH3KTM.mjs";
|
|
4
|
+
import * as cloudflareWorkers from "cloudflare:workers";
|
|
5
|
+
import { DurableObject } from "cloudflare:workers";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
//#region src/sdk/capnweb/live-state/retain.ts
|
|
8
|
+
/**
|
|
9
|
+
* Retain a single-argument RPC callback so it survives past the call that
|
|
10
|
+
* carried it. `dup()` duplicates the underlying remote stub (that dup is what
|
|
11
|
+
* `[Symbol.dispose]` releases); `onRpcBroken` is forwarded defensively — Cap'n
|
|
12
|
+
* Web stubs expose no own property descriptors and a Workers RPC property access
|
|
13
|
+
* can fabricate a pipelined method that rejects at call time, so we wire whatever
|
|
14
|
+
* the stub claims and swallow registration failures. `onRpcBroken` is only a
|
|
15
|
+
* prompt hint; callers that need a terminal verdict must model one explicitly
|
|
16
|
+
* (for example, the durable wake lane's independent settlement capability).
|
|
17
|
+
*/
|
|
18
|
+
function retainCallback(callback) {
|
|
19
|
+
const retainable = callback;
|
|
20
|
+
const retained = retainable.dup?.() ?? retainable;
|
|
21
|
+
const dispose = retained[Symbol.dispose]?.bind(retained);
|
|
22
|
+
let disposed = false;
|
|
23
|
+
const wrapped = Object.assign((arg) => retained(arg), { [Symbol.dispose]() {
|
|
24
|
+
if (disposed) return;
|
|
25
|
+
disposed = true;
|
|
26
|
+
dispose?.();
|
|
27
|
+
} });
|
|
28
|
+
const onRpcBroken = retained.onRpcBroken;
|
|
29
|
+
if (typeof onRpcBroken === "function") wrapped.onRpcBroken = (handler) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = onRpcBroken.call(retained, handler);
|
|
32
|
+
if (isThenable(result)) Promise.resolve(result).catch(() => {});
|
|
33
|
+
} catch {}
|
|
34
|
+
};
|
|
35
|
+
return wrapped;
|
|
36
|
+
}
|
|
37
|
+
/** Thenable probe: RPC stubs and their call results are thenable-shaped. */
|
|
38
|
+
function isThenable(value) {
|
|
39
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Dispose the result of an RPC call whose value the caller ignores. Reading a
|
|
43
|
+
* Cap'n Web / Workers RPC method yields a disposable stub even when unused, and
|
|
44
|
+
* dropping it without disposal leaks the remote reference.
|
|
45
|
+
*/
|
|
46
|
+
function disposeIgnoredRpcResult(result) {
|
|
47
|
+
if (result !== null && (typeof result === "object" || typeof result === "function") && Symbol.dispose in result) result[Symbol.dispose]();
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/sdk/capnweb/live-state/diff.ts
|
|
51
|
+
/**
|
|
52
|
+
* Structural diff between two JSON values, producing the minimal `LiveStatePatch`
|
|
53
|
+
* — or `undefined` when nothing changed.
|
|
54
|
+
*
|
|
55
|
+
* The diff is REFERENCE-FIRST: identical references short-circuit immediately
|
|
56
|
+
* (`Object.is`), so the cost is O(changed), not O(size) — *as long as callers
|
|
57
|
+
* update state immutably* (unchanged sub-objects keep their identity). That one
|
|
58
|
+
* discipline is the whole performance story: a single touched row in a
|
|
59
|
+
* thousand-entry index yields one tiny patch instead of a full rescan.
|
|
60
|
+
*
|
|
61
|
+
* PLAIN objects (prototype `Object.prototype` or `null`) are treated as keyed
|
|
62
|
+
* maps and diffed per key. Dense arrays are diffed by position. Everything
|
|
63
|
+
* else — primitives, `null`, sparse arrays, and
|
|
64
|
+
* non-plain instances like `Date`/`Map`/`Set` — is a leaf, replaced wholesale.
|
|
65
|
+
* Descending into an instance would diff its own enumerable keys, which for a
|
|
66
|
+
* `Date` is NONE — two different Dates would read as "unchanged" and the
|
|
67
|
+
* subscriber would stay stale forever. Keyed objects remain preferable for
|
|
68
|
+
* collections whose entries frequently move; positional patches do not infer moves.
|
|
69
|
+
*/
|
|
70
|
+
function diff(prev, next, options = {}) {
|
|
71
|
+
if (Object.is(prev, next)) return void 0;
|
|
72
|
+
if (Array.isArray(prev) && Array.isArray(next) && options.arrays !== false) {
|
|
73
|
+
const items = [];
|
|
74
|
+
for (let index = 0; index < next.length; index++) {
|
|
75
|
+
if (!Object.hasOwn(next, index) || index < prev.length && !Object.hasOwn(prev, index)) return { set: next };
|
|
76
|
+
const patch = index < prev.length ? diff(prev[index], next[index], options) : { set: next[index] };
|
|
77
|
+
if (patch) items.push([index, patch]);
|
|
78
|
+
}
|
|
79
|
+
return items.length > 0 || prev.length !== next.length ? { array: {
|
|
80
|
+
length: next.length,
|
|
81
|
+
items
|
|
82
|
+
} } : void 0;
|
|
83
|
+
}
|
|
84
|
+
if (!isPlainObject(prev) || !isPlainObject(next)) return { set: next };
|
|
85
|
+
const fields = [];
|
|
86
|
+
const drop = [];
|
|
87
|
+
for (const key of Object.keys(next)) {
|
|
88
|
+
if (next[key] === void 0) {
|
|
89
|
+
if (Object.hasOwn(prev, key)) drop.push(key);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const childPatch = diff(Object.hasOwn(prev, key) ? prev[key] : void 0, next[key], options);
|
|
93
|
+
if (childPatch !== void 0) fields.push([key, childPatch]);
|
|
94
|
+
}
|
|
95
|
+
for (const key of Object.keys(prev)) if (!Object.hasOwn(next, key)) drop.push(key);
|
|
96
|
+
if (fields.length === 0 && drop.length === 0) return void 0;
|
|
97
|
+
const patch = {};
|
|
98
|
+
if (fields.length > 0) patch.fields = Object.fromEntries(fields);
|
|
99
|
+
if (drop.length > 0) patch.drop = drop;
|
|
100
|
+
return patch;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Apply a `LiveStatePatch` to a previous value, returning the next value.
|
|
104
|
+
*
|
|
105
|
+
* Like `diff`, this preserves structural sharing: a fresh object is built only
|
|
106
|
+
* along changed paths, so untouched branches keep their previous reference.
|
|
107
|
+
* That's what lets a client selector (`s => s.streamsIndex`) skip re-rendering
|
|
108
|
+
* when an unrelated slice changed — the slice it reads stays `Object.is`-equal
|
|
109
|
+
* across applies.
|
|
110
|
+
*/
|
|
111
|
+
function applyPatch(prev, patch) {
|
|
112
|
+
if ("set" in patch) return patch.set;
|
|
113
|
+
if ("array" in patch) {
|
|
114
|
+
if (!Array.isArray(prev)) throw new Error("Live-state array patch requires an array baseline");
|
|
115
|
+
const next = prev.slice(0, patch.array.length);
|
|
116
|
+
next.length = patch.array.length;
|
|
117
|
+
for (const [index, child] of patch.array.items) {
|
|
118
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= next.length) throw new Error("Live-state array patch index is outside its resulting length");
|
|
119
|
+
next[index] = applyPatch(prev[index], child);
|
|
120
|
+
}
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
const base = isPlainObject(prev) ? prev : {};
|
|
124
|
+
const next = { ...base };
|
|
125
|
+
if (patch.fields) for (const [key, childPatch] of Object.entries(patch.fields)) Object.defineProperty(next, key, {
|
|
126
|
+
value: applyPatch(Object.hasOwn(base, key) ? base[key] : void 0, childPatch),
|
|
127
|
+
enumerable: true,
|
|
128
|
+
writable: true,
|
|
129
|
+
configurable: true
|
|
130
|
+
});
|
|
131
|
+
if (patch.drop) for (const key of patch.drop) delete next[key];
|
|
132
|
+
return next;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A PLAIN object — prototype `Object.prototype` or `null` — the only thing
|
|
136
|
+
* `diff` descends into and `applyPatch` merges over. Arrays and class instances
|
|
137
|
+
* (`Date`, `Map`, `Set`, …) fail this on purpose: they carry state outside
|
|
138
|
+
* their own enumerable keys, so per-key diffing would misread them (see the
|
|
139
|
+
* `diff` docstring) — they are leaves, replaced wholesale.
|
|
140
|
+
*/
|
|
141
|
+
function isPlainObject(value) {
|
|
142
|
+
if (typeof value !== "object" || value === null) return false;
|
|
143
|
+
const proto = Object.getPrototypeOf(value);
|
|
144
|
+
return proto === Object.prototype || proto === null;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/sdk/capnweb/live-state/compact.ts
|
|
148
|
+
const fieldAddresses = /* @__PURE__ */ new WeakMap();
|
|
149
|
+
function addresses(value) {
|
|
150
|
+
let cached = fieldAddresses.get(value);
|
|
151
|
+
if (!cached) {
|
|
152
|
+
const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
|
|
153
|
+
cached = {
|
|
154
|
+
keys,
|
|
155
|
+
indices: new Map(keys.map((key, index) => [key, index]))
|
|
156
|
+
};
|
|
157
|
+
fieldAddresses.set(value, cached);
|
|
158
|
+
}
|
|
159
|
+
return cached;
|
|
160
|
+
}
|
|
161
|
+
/** Encode only the changed paths of an existing structural diff. */
|
|
162
|
+
function compactPatch(previous, patch) {
|
|
163
|
+
if ("set" in patch) {
|
|
164
|
+
const next = patch.set;
|
|
165
|
+
if (typeof previous === "string" && typeof next === "string" && previous.length >= 16 && previous.length <= 4096 && next.length > previous.length && next.startsWith(previous)) return [previous.length, next.slice(previous.length)];
|
|
166
|
+
if (next === null || typeof next === "string" || typeof next === "number" || typeof next === "boolean") return next;
|
|
167
|
+
return [next];
|
|
168
|
+
}
|
|
169
|
+
if ("array" in patch) {
|
|
170
|
+
if (!Array.isArray(previous)) throw new Error("Live-state array patch requires an array baseline");
|
|
171
|
+
const result = {};
|
|
172
|
+
if (patch.array.length !== previous.length) result["#"] = patch.array.length;
|
|
173
|
+
for (const [index, child] of patch.array.items) result[index] = compactPatch(previous[index], child);
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
if (!isPlainObject(previous)) throw new Error("Live-state object patch requires an object baseline");
|
|
177
|
+
const { indices } = addresses(previous);
|
|
178
|
+
const fields = [];
|
|
179
|
+
for (const [key, child] of Object.entries(patch.fields || {})) {
|
|
180
|
+
const index = indices.get(key);
|
|
181
|
+
fields.push([index === void 0 ? `+${key}` : String(index), compactPatch(index === void 0 ? void 0 : previous[key], child)]);
|
|
182
|
+
}
|
|
183
|
+
for (const key of patch.drop || []) {
|
|
184
|
+
const index = indices.get(key);
|
|
185
|
+
if (index !== void 0) fields.push([String(index), []]);
|
|
186
|
+
}
|
|
187
|
+
return Object.fromEntries(fields);
|
|
188
|
+
}
|
|
189
|
+
/** Apply a version 3 patch, retaining every untouched object's identity. */
|
|
190
|
+
function applyCompactPatch(previous, patch) {
|
|
191
|
+
return applyCompactValue(previous, patch);
|
|
192
|
+
}
|
|
193
|
+
function applyCompactValue(previous, patch) {
|
|
194
|
+
if (patch === null || typeof patch !== "object") return patch;
|
|
195
|
+
if (Array.isArray(patch)) {
|
|
196
|
+
if (patch.length === 1) return patch[0];
|
|
197
|
+
if (patch.length !== 2 || typeof previous !== "string" || previous.length !== patch[0]) throw new Error("Live-state append does not match its string baseline");
|
|
198
|
+
return previous + patch[1];
|
|
199
|
+
}
|
|
200
|
+
if (Array.isArray(previous)) {
|
|
201
|
+
const length = Object.hasOwn(patch, "#") ? patch["#"] : previous.length;
|
|
202
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > 4294967295) throw new Error("Live-state array patch has an invalid length");
|
|
203
|
+
const next = previous.slice(0, length);
|
|
204
|
+
next.length = length;
|
|
205
|
+
for (const [address, child] of Object.entries(patch)) {
|
|
206
|
+
if (address === "#") continue;
|
|
207
|
+
const index = position(address, length);
|
|
208
|
+
next[index] = applyCompactValue(previous[index], child);
|
|
209
|
+
}
|
|
210
|
+
return next;
|
|
211
|
+
}
|
|
212
|
+
if (!isPlainObject(previous)) throw new Error("Live-state object patch requires an object baseline");
|
|
213
|
+
const { keys } = addresses(previous);
|
|
214
|
+
const next = { ...previous };
|
|
215
|
+
for (const [address, child] of Object.entries(patch)) {
|
|
216
|
+
const added = address.startsWith("+");
|
|
217
|
+
const key = added ? address.slice(1) : keys[position(address, keys.length)];
|
|
218
|
+
if (added && Object.hasOwn(previous, key) && previous[key] !== void 0) throw new Error("Live-state new field already exists in its baseline");
|
|
219
|
+
if (Array.isArray(child) && child.length === 0) {
|
|
220
|
+
if (added) throw new Error("Live-state cannot delete a new field");
|
|
221
|
+
delete next[key];
|
|
222
|
+
} else Object.defineProperty(next, key, {
|
|
223
|
+
value: applyCompactValue(added ? void 0 : previous[key], child),
|
|
224
|
+
enumerable: true,
|
|
225
|
+
writable: true,
|
|
226
|
+
configurable: true
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return next;
|
|
230
|
+
}
|
|
231
|
+
function position(address, length) {
|
|
232
|
+
const index = Number(address);
|
|
233
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= length || String(index) !== address) throw new Error("Live-state field address is outside its baseline");
|
|
234
|
+
return index;
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/sdk/capnweb/live-state/engine.ts
|
|
238
|
+
const DEFAULT_DEBOUNCE_MS = 100;
|
|
239
|
+
const ACK_TIMEOUT_MS = 1e4;
|
|
240
|
+
/**
|
|
241
|
+
* A source-agnostic live store: hold a state value, and when it changes push the
|
|
242
|
+
* minimal diff to every subscriber. It does not know or care where the state
|
|
243
|
+
* comes from — a Durable Object folds events into it, a stateless RpcTarget polls
|
|
244
|
+
* a third-party API into it — so ANY RpcTarget can expose live state by holding
|
|
245
|
+
* one and returning a read-only wrapper from a `.live` getter.
|
|
246
|
+
*
|
|
247
|
+
* Two rules make it cheap and correct:
|
|
248
|
+
* - Updates are IMMUTABLE (`setState`/`assign` build a new value, never mutate),
|
|
249
|
+
* so the diff short-circuits unchanged branches by identity — O(changed), not
|
|
250
|
+
* O(size). See `diff.ts`.
|
|
251
|
+
* - Push work runs only while a subscriber exists. Transient readers call
|
|
252
|
+
* `readSince` explicitly; a dormant engine schedules nothing.
|
|
253
|
+
*
|
|
254
|
+
* All subscribers share one revision line. Fast subscribers share a patch;
|
|
255
|
+
* slow subscribers receive a coalesced patch from their acknowledged state.
|
|
256
|
+
* A revision mismatch makes the client resync instead of applying stale data.
|
|
257
|
+
*/
|
|
258
|
+
var LiveState = class {
|
|
259
|
+
#current;
|
|
260
|
+
/** Latest flushed state — the shared diff baseline. */
|
|
261
|
+
#broadcast;
|
|
262
|
+
#revision = 0;
|
|
263
|
+
#epoch = crypto.randomUUID();
|
|
264
|
+
#lastUpdate;
|
|
265
|
+
#lastCompactUpdate;
|
|
266
|
+
#subscribers = /* @__PURE__ */ new Map();
|
|
267
|
+
#debounceMs;
|
|
268
|
+
#flushTimer;
|
|
269
|
+
constructor(initial, options = {}) {
|
|
270
|
+
this.#current = initial;
|
|
271
|
+
this.#broadcast = initial;
|
|
272
|
+
this.#debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
273
|
+
}
|
|
274
|
+
/** The current state — reflects every `setState`/`assign`, even while dormant. */
|
|
275
|
+
getState() {
|
|
276
|
+
return this.#current;
|
|
277
|
+
}
|
|
278
|
+
/** One retained delta bounds history while a single parent can pull incrementally.
|
|
279
|
+
* A late reader or a new incarnation receives an explicit snapshot instead. */
|
|
280
|
+
readSince(cursor, options = {}) {
|
|
281
|
+
if (this.#flushTimer !== void 0) clearTimeout(this.#flushTimer);
|
|
282
|
+
this.#flushTimer = void 0;
|
|
283
|
+
this.#flush();
|
|
284
|
+
if (cursor?.epoch === this.#epoch) {
|
|
285
|
+
if (cursor.revision === this.#revision) return {
|
|
286
|
+
epoch: this.#epoch,
|
|
287
|
+
update: null
|
|
288
|
+
};
|
|
289
|
+
if (cursor.revision === this.#lastUpdate?.from) return {
|
|
290
|
+
epoch: this.#epoch,
|
|
291
|
+
update: options.patchVersion === 3 ? this.#lastCompactUpdate : this.#lastUpdate
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
epoch: this.#epoch,
|
|
296
|
+
update: options.patchVersion === 3 ? { s: [this.#revision, this.#broadcast] } : {
|
|
297
|
+
type: "snapshot",
|
|
298
|
+
revision: this.#revision,
|
|
299
|
+
state: this.#broadcast
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/** True while at least one live subscriber makes projection work observable. */
|
|
304
|
+
get observed() {
|
|
305
|
+
return this.#subscribers.size > 0;
|
|
306
|
+
}
|
|
307
|
+
/** Replace the state. Build a NEW value (don't mutate) so the diff stays cheap. */
|
|
308
|
+
setState(next) {
|
|
309
|
+
this.#current = typeof next === "function" ? next(this.#current) : next;
|
|
310
|
+
if (this.#subscribers.size > 0) this.#scheduleFlush();
|
|
311
|
+
}
|
|
312
|
+
/** Shallow-merge a partial into the state — sugar for `setState(p => ({ ...p, ...partial }))`. */
|
|
313
|
+
assign(partial) {
|
|
314
|
+
this.setState((prev) => ({
|
|
315
|
+
...prev,
|
|
316
|
+
...partial
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
subscribe(sink, options = {}) {
|
|
320
|
+
if (this.#subscribers.size === 0) this.#flush();
|
|
321
|
+
const retained = retainCallback(sink);
|
|
322
|
+
this.#subscribers.set(retained, {
|
|
323
|
+
version: options.patchVersion ?? 1,
|
|
324
|
+
state: this.#broadcast,
|
|
325
|
+
revision: this.#revision,
|
|
326
|
+
busy: false
|
|
327
|
+
});
|
|
328
|
+
retained.onRpcBroken?.(() => this.#drop(retained));
|
|
329
|
+
this.#deliver(retained, options.patchVersion === 3 ? { s: [this.#revision, this.#broadcast] } : {
|
|
330
|
+
type: "snapshot",
|
|
331
|
+
revision: this.#revision,
|
|
332
|
+
state: this.#broadcast
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
ping: () => this.#subscribers.has(retained),
|
|
336
|
+
unsubscribe: () => this.#drop(retained),
|
|
337
|
+
[Symbol.dispose]: () => this.#drop(retained)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
#scheduleFlush() {
|
|
341
|
+
this.#flushTimer ??= setTimeout(() => {
|
|
342
|
+
this.#flushTimer = void 0;
|
|
343
|
+
this.#flush();
|
|
344
|
+
}, this.#debounceMs);
|
|
345
|
+
}
|
|
346
|
+
#flush() {
|
|
347
|
+
const previous = this.#broadcast;
|
|
348
|
+
const patch = diff(previous, this.#current);
|
|
349
|
+
if (patch === void 0) return;
|
|
350
|
+
const from = this.#revision;
|
|
351
|
+
const to = this.#revision += 1;
|
|
352
|
+
this.#broadcast = this.#current;
|
|
353
|
+
const update = {
|
|
354
|
+
type: "patch",
|
|
355
|
+
from,
|
|
356
|
+
to,
|
|
357
|
+
patch
|
|
358
|
+
};
|
|
359
|
+
this.#lastUpdate = update;
|
|
360
|
+
this.#lastCompactUpdate = { p: [
|
|
361
|
+
from,
|
|
362
|
+
to,
|
|
363
|
+
compactPatch(previous, patch)
|
|
364
|
+
] };
|
|
365
|
+
for (const subscriber of this.#subscribers.keys()) this.#sendLatest(subscriber);
|
|
366
|
+
}
|
|
367
|
+
/** Slow subscribers hold one call and one baseline, never a queue of token updates. */
|
|
368
|
+
#sendLatest(subscriber) {
|
|
369
|
+
const held = this.#subscribers.get(subscriber);
|
|
370
|
+
if (!held || held.busy || held.revision === this.#revision) return;
|
|
371
|
+
if (held.version === 3) {
|
|
372
|
+
if (held.revision === this.#lastUpdate?.from) this.#deliver(subscriber, this.#lastCompactUpdate);
|
|
373
|
+
else {
|
|
374
|
+
const patch = diff(held.state, this.#broadcast);
|
|
375
|
+
this.#deliver(subscriber, { p: [
|
|
376
|
+
held.revision,
|
|
377
|
+
this.#revision,
|
|
378
|
+
patch ? compactPatch(held.state, patch) : {}
|
|
379
|
+
] });
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const patch = held.version === 2 && held.revision === this.#lastUpdate?.from ? this.#lastUpdate.patch : diff(held.state, this.#broadcast, { arrays: held.version === 2 });
|
|
384
|
+
this.#deliver(subscriber, {
|
|
385
|
+
type: "patch",
|
|
386
|
+
from: held.revision,
|
|
387
|
+
to: this.#revision,
|
|
388
|
+
patch: patch ?? {}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/** Only acknowledgement releases the next coalesced update; a dead sink is dropped. */
|
|
392
|
+
#deliver(subscriber, update) {
|
|
393
|
+
const held = this.#subscribers.get(subscriber);
|
|
394
|
+
if (!held) return;
|
|
395
|
+
held.state = this.#broadcast;
|
|
396
|
+
held.revision = this.#revision;
|
|
397
|
+
held.busy = true;
|
|
398
|
+
let result;
|
|
399
|
+
try {
|
|
400
|
+
result = subscriber(update);
|
|
401
|
+
} catch {
|
|
402
|
+
this.#drop(subscriber);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (isThenable(result)) {
|
|
406
|
+
held.timeout = setTimeout(() => {
|
|
407
|
+
console.warn("Live-state subscriber dropped: acknowledgement timed out", {
|
|
408
|
+
revision: held.revision,
|
|
409
|
+
timeoutMs: ACK_TIMEOUT_MS
|
|
410
|
+
});
|
|
411
|
+
this.#drop(subscriber);
|
|
412
|
+
}, ACK_TIMEOUT_MS);
|
|
413
|
+
Promise.resolve(result).then(() => {
|
|
414
|
+
if (held.timeout !== void 0) clearTimeout(held.timeout);
|
|
415
|
+
held.busy = false;
|
|
416
|
+
this.#sendLatest(subscriber);
|
|
417
|
+
}, () => this.#drop(subscriber)).finally(() => disposeIgnoredRpcResult(result));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
held.busy = false;
|
|
421
|
+
disposeIgnoredRpcResult(result);
|
|
422
|
+
}
|
|
423
|
+
#drop(subscriber) {
|
|
424
|
+
const timeout = this.#subscribers.get(subscriber)?.timeout;
|
|
425
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
426
|
+
if (!this.#subscribers.delete(subscriber)) return;
|
|
427
|
+
subscriber[Symbol.dispose]();
|
|
428
|
+
if (this.#subscribers.size === 0 && this.#flushTimer !== void 0) {
|
|
429
|
+
clearTimeout(this.#flushTimer);
|
|
430
|
+
this.#flushTimer = void 0;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/processors/stream-processor-registry.ts
|
|
436
|
+
const tracing = cloudflareWorkers.tracing ?? { enterSpan: (_name, fn) => fn({ setAttribute() {} }) };
|
|
437
|
+
const WakeDeliveryThrowableFields = z.object({
|
|
438
|
+
durableObjectReset: z.unknown().optional(),
|
|
439
|
+
itxCallId: z.unknown().optional(),
|
|
440
|
+
message: z.unknown().optional(),
|
|
441
|
+
name: z.unknown().optional(),
|
|
442
|
+
overloaded: z.unknown().optional(),
|
|
443
|
+
retryable: z.unknown().optional()
|
|
444
|
+
});
|
|
445
|
+
function serializeWakeDeliveryError(error) {
|
|
446
|
+
const parsed = WakeDeliveryThrowableFields.safeParse(error);
|
|
447
|
+
const candidate = parsed.success ? parsed.data : void 0;
|
|
448
|
+
return {
|
|
449
|
+
name: typeof candidate?.name === "string" ? candidate.name : "Error",
|
|
450
|
+
message: typeof candidate?.message === "string" ? candidate.message : String(error) || "unknown delivery failure",
|
|
451
|
+
...typeof candidate?.itxCallId === "string" && candidate.itxCallId.length > 0 && candidate.itxCallId.length <= 200 && { itxCallId: candidate.itxCallId },
|
|
452
|
+
...candidate?.durableObjectReset === true && { durableObjectReset: true },
|
|
453
|
+
...candidate?.overloaded === true && { overloaded: true },
|
|
454
|
+
...candidate?.retryable === true && { retryable: true }
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Report one final wake-delivery result without awaiting the report call's
|
|
459
|
+
* return value. Calling the callback sends the result; awaiting it would keep
|
|
460
|
+
* the original cross-Durable-Object call chain open.
|
|
461
|
+
*/
|
|
462
|
+
function reportWakeDeliveryResult(reportDeliveryResult, deliveryResult) {
|
|
463
|
+
let result;
|
|
464
|
+
try {
|
|
465
|
+
result = reportDeliveryResult(deliveryResult);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
console.warn("stream wake delivery result could not be reported", { error });
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
disposeIgnoredRpcResult(result);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
console.warn("stream wake delivery report result disposal failed", { error });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function createStreamProcessorRegistry(ctx, options) {
|
|
477
|
+
const entries = /* @__PURE__ */ new Map();
|
|
478
|
+
const now = options.now ?? (() => Date.now());
|
|
479
|
+
const alarmSlices = /* @__PURE__ */ new Map();
|
|
480
|
+
/**
|
|
481
|
+
* A durable alarm armed by a PREVIOUS incarnation whose owning slice we
|
|
482
|
+
* cannot know (in-memory desires died with it). The first reconcile adopts
|
|
483
|
+
* it as a desire of its own so this incarnation's arming/disarming cannot
|
|
484
|
+
* clobber it; the next actual fire drops it — every subsystem re-derives
|
|
485
|
+
* its desire in that turn.
|
|
486
|
+
*/
|
|
487
|
+
const INHERITED_ALARM_SLICE = "@inherited";
|
|
488
|
+
/** What the platform alarm holds; null = cleared, undefined = unknown. */
|
|
489
|
+
let platformAlarmAtMs;
|
|
490
|
+
/** Serializes alarm storage ops so read-adopt and writes cannot interleave. */
|
|
491
|
+
let alarmChain = Promise.resolve();
|
|
492
|
+
/**
|
|
493
|
+
* Returns the settled write for callers whose correctness depends on the
|
|
494
|
+
* alarm being DURABLY armed (the alarm handler, the scheduler's error-path
|
|
495
|
+
* fallback) — a rejected step means the platform alarm may not exist, and
|
|
496
|
+
* such callers must rethrow so the platform retries them. Fire-and-forget
|
|
497
|
+
* callers may ignore the result: the chain observes the rejection either
|
|
498
|
+
* way, and the next reconcile re-reads and re-issues.
|
|
499
|
+
*/
|
|
500
|
+
function reconcileAlarm() {
|
|
501
|
+
const step = alarmChain.then(async () => {
|
|
502
|
+
if (platformAlarmAtMs === void 0) {
|
|
503
|
+
const existing = await ctx.storage.getAlarm();
|
|
504
|
+
platformAlarmAtMs = existing;
|
|
505
|
+
if (existing !== null) alarmSlices.set(INHERITED_ALARM_SLICE, existing);
|
|
506
|
+
}
|
|
507
|
+
let earliest = null;
|
|
508
|
+
for (const atMs of alarmSlices.values()) if (earliest === null || atMs < earliest) earliest = atMs;
|
|
509
|
+
if (earliest === platformAlarmAtMs) return;
|
|
510
|
+
platformAlarmAtMs = earliest;
|
|
511
|
+
if (earliest === null) await ctx.storage.deleteAlarm();
|
|
512
|
+
else await ctx.storage.setAlarm(earliest);
|
|
513
|
+
});
|
|
514
|
+
alarmChain = step.catch((error) => {
|
|
515
|
+
platformAlarmAtMs = void 0;
|
|
516
|
+
console.error("stream processor registry alarm arming failed", error);
|
|
517
|
+
});
|
|
518
|
+
ctx.waitUntil(alarmChain);
|
|
519
|
+
return step;
|
|
520
|
+
}
|
|
521
|
+
function setAlarmSlice(name, atMs) {
|
|
522
|
+
if (atMs === null) alarmSlices.delete(name);
|
|
523
|
+
else alarmSlices.set(name, atMs);
|
|
524
|
+
return reconcileAlarm();
|
|
525
|
+
}
|
|
526
|
+
function requireEntry(name) {
|
|
527
|
+
const entry = entries.get(name);
|
|
528
|
+
if (entry === void 0) throw new Error(`Unknown stream processor name "${name}" on this registry (registered: ${[...entries.keys()].join(", ") || "none"})`);
|
|
529
|
+
return entry;
|
|
530
|
+
}
|
|
531
|
+
function requireEntryForInstance(processor, verb) {
|
|
532
|
+
for (const entry of entries.values()) if (entry.processor === processor) return entry;
|
|
533
|
+
throw new Error(`stream processor "${processor.contract.slug}" instance passed to ${verb}() is not registered on this registry — pass the exact instance register() returned (registered names: ${[...entries.keys()].join(", ") || "none"})`);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Route a wake to a registered entry. The subscription NAME is the key —
|
|
537
|
+
* and equals the contract slug (enforced at configure time; under facet
|
|
538
|
+
* placement it is also the facet name). An unknown name is a loud error.
|
|
539
|
+
*/
|
|
540
|
+
function resolveProcessorName(args) {
|
|
541
|
+
if (entries.get(args.name) === void 0) throw new Error(`wakeStreamProcessor for unknown name "${args.name}" — subscribe by registered name (registered: ${[...entries.keys()].join(", ") || "none"})`);
|
|
542
|
+
return args.name;
|
|
543
|
+
}
|
|
544
|
+
const live = new LiveState({});
|
|
545
|
+
function assembleLive() {
|
|
546
|
+
if ([...entries.values()].some((entry) => !entry.runner.isLoaded)) {
|
|
547
|
+
options.onLiveAssembled?.({ skippedUnloadedRunners: true });
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const primary = [...entries.values()][0]?.runner.currentState;
|
|
551
|
+
live.setState(options.getLiveState?.() ?? primary ?? {});
|
|
552
|
+
options.onLiveAssembled?.({ skippedUnloadedRunners: false });
|
|
553
|
+
}
|
|
554
|
+
async function loadThenAssemble() {
|
|
555
|
+
await Promise.all([...entries.values()].map((entry) => entry.runner.snapshot()));
|
|
556
|
+
assembleLive();
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
stream: options.stream,
|
|
560
|
+
live,
|
|
561
|
+
refreshLive: assembleLive,
|
|
562
|
+
loadAndRefreshLive: loadThenAssemble,
|
|
563
|
+
get names() {
|
|
564
|
+
return [...entries.keys()];
|
|
565
|
+
},
|
|
566
|
+
register(processor, opts) {
|
|
567
|
+
const name = opts?.name ?? processor.contract.slug;
|
|
568
|
+
if (entries.has(name)) throw new Error(`Stream processor name "${name}" is already registered on this registry`);
|
|
569
|
+
for (const [registeredName, entry] of entries) if (entry.processor === processor) throw new Error(`this exact processor instance is already registered as "${registeredName}" — construct one instance per registration`);
|
|
570
|
+
const recovery = opts?.recovery ? durableObjectRecovery({
|
|
571
|
+
storage: ctx.storage,
|
|
572
|
+
name,
|
|
573
|
+
stream: options.stream,
|
|
574
|
+
version: options.version,
|
|
575
|
+
armAlarm: (atMs) => void setAlarmSlice(`keepalive:${name}`, atMs),
|
|
576
|
+
waitUntil: (work) => ctx.waitUntil(work),
|
|
577
|
+
now
|
|
578
|
+
}) : void 0;
|
|
579
|
+
const runner = new StreamProcessorRunner({
|
|
580
|
+
processor,
|
|
581
|
+
stream: options.stream,
|
|
582
|
+
durability: {
|
|
583
|
+
progress: durableObjectProgressStore({
|
|
584
|
+
storage: ctx.storage,
|
|
585
|
+
name,
|
|
586
|
+
resetForStream: opts?.resetForStream,
|
|
587
|
+
reductionCache: opts?.reductionCache
|
|
588
|
+
}),
|
|
589
|
+
...recovery === void 0 ? {} : { recovery }
|
|
590
|
+
},
|
|
591
|
+
keepAlive: (work) => ctx.waitUntil(work()),
|
|
592
|
+
now
|
|
593
|
+
});
|
|
594
|
+
entries.set(name, {
|
|
595
|
+
processor,
|
|
596
|
+
runner,
|
|
597
|
+
...recovery === void 0 ? {} : { recovery }
|
|
598
|
+
});
|
|
599
|
+
runner.observeStateChanges(() => assembleLive());
|
|
600
|
+
return processor;
|
|
601
|
+
},
|
|
602
|
+
reads(processorOrName) {
|
|
603
|
+
const entry = typeof processorOrName === "string" ? requireEntry(processorOrName) : requireEntryForInstance(processorOrName, "reads");
|
|
604
|
+
const { runner } = entry;
|
|
605
|
+
return {
|
|
606
|
+
snapshot: () => runner.snapshot(),
|
|
607
|
+
getRuntimeState: async () => {
|
|
608
|
+
return {
|
|
609
|
+
...await entry.processor.getRuntimeState(),
|
|
610
|
+
snapshot: await runner.snapshot()
|
|
611
|
+
};
|
|
612
|
+
},
|
|
613
|
+
waitUntilEvent: (input) => "offset" in input ? runner.waitUntilEvent(input) : runner.waitUntilEvent(input),
|
|
614
|
+
catchUp: () => runner.catchUp(),
|
|
615
|
+
get currentAcknowledgedThroughOffset() {
|
|
616
|
+
return runner.currentAcknowledgedThroughOffset;
|
|
617
|
+
},
|
|
618
|
+
get currentStreamId() {
|
|
619
|
+
return runner.currentStreamId;
|
|
620
|
+
},
|
|
621
|
+
get currentState() {
|
|
622
|
+
return runner.currentState;
|
|
623
|
+
},
|
|
624
|
+
get isLoaded() {
|
|
625
|
+
return runner.isLoaded;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
},
|
|
629
|
+
observeStateChanges(processor, observer) {
|
|
630
|
+
return requireEntryForInstance(processor, "observeStateChanges").runner.observeStateChanges(observer);
|
|
631
|
+
},
|
|
632
|
+
async catchUp(name) {
|
|
633
|
+
await requireEntry(name).runner.catchUp();
|
|
634
|
+
},
|
|
635
|
+
resetRecoveryBackoff(name) {
|
|
636
|
+
requireEntry(name).recovery?.resetBackoff?.();
|
|
637
|
+
},
|
|
638
|
+
handleAlarm(alarmInfo) {
|
|
639
|
+
return tracing.enterSpan("alarm processor keepalive", async (span) => {
|
|
640
|
+
platformAlarmAtMs = null;
|
|
641
|
+
const firedAt = now();
|
|
642
|
+
for (const [name, atMs] of alarmSlices) if (atMs <= firedAt) alarmSlices.delete(name);
|
|
643
|
+
span.setAttribute("iterate.alarm.kind", "processor_keepalive");
|
|
644
|
+
if (alarmInfo !== void 0) {
|
|
645
|
+
span.setAttribute("iterate.alarm.is_retry", alarmInfo.isRetry);
|
|
646
|
+
span.setAttribute("iterate.alarm.retry_count", alarmInfo.retryCount);
|
|
647
|
+
}
|
|
648
|
+
try {
|
|
649
|
+
const failures = [];
|
|
650
|
+
for (const [name, entry] of entries) try {
|
|
651
|
+
await entry.runner.handleAlarm(alarmInfo);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
console.error(`stream processor "${name}" alarm handling failed`, error);
|
|
654
|
+
failures.push(error);
|
|
655
|
+
}
|
|
656
|
+
if (failures.length > 0) throw failures[0];
|
|
657
|
+
} finally {
|
|
658
|
+
await reconcileAlarm();
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
},
|
|
662
|
+
setAlarmSlice,
|
|
663
|
+
getAlarmSlice: (name) => alarmSlices.get(name) ?? null,
|
|
664
|
+
async wakeStreamProcessor(args) {
|
|
665
|
+
if (args.stream.projectId !== options.projectId || args.stream.path !== options.path) throw new Error(`wakeStreamProcessor coordinate mismatch: wake for ${args.stream.projectId ?? "null"}:${args.stream.path} does not match registry ${options.projectId ?? "null"}:${options.path}`);
|
|
666
|
+
const name = resolveProcessorName(args);
|
|
667
|
+
const entry = requireEntry(name);
|
|
668
|
+
if (!z.uuid().safeParse(args.stream.streamId).success) throw new Error(`wakeStreamProcessor streamId must be a UUID`);
|
|
669
|
+
const opened = await entry.runner.openHostedEventBatchCallback(args.stream.streamId);
|
|
670
|
+
const processEventBatch = (batch) => {
|
|
671
|
+
const { reportDeliveryResult, ...frame } = batch;
|
|
672
|
+
if (frame.projectId !== options.projectId || frame.path !== options.path || frame.streamId !== args.stream.streamId) throw new Error(`hosted processor "${name}" received batch for ${frame.projectId ?? "null"}:${frame.path}:${frame.streamId}; expected ${options.projectId ?? "null"}:${options.path}:${args.stream.streamId}`);
|
|
673
|
+
let retainedResultCallback;
|
|
674
|
+
try {
|
|
675
|
+
retainedResultCallback = retainCallback(reportDeliveryResult);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.error("stream wake delivery result callback could not be retained", { error });
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
let attempt;
|
|
681
|
+
try {
|
|
682
|
+
attempt = opened.processEventBatch(frame);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
attempt = Promise.reject(error);
|
|
685
|
+
}
|
|
686
|
+
const report = attempt.then(() => reportWakeDeliveryResult(retainedResultCallback, { outcome: "ok" }), (error) => reportWakeDeliveryResult(retainedResultCallback, {
|
|
687
|
+
outcome: "error",
|
|
688
|
+
error: serializeWakeDeliveryError(error)
|
|
689
|
+
}));
|
|
690
|
+
ctx.waitUntil(report.finally(() => {
|
|
691
|
+
try {
|
|
692
|
+
retainedResultCallback[Symbol.dispose]();
|
|
693
|
+
} catch (error) {
|
|
694
|
+
console.warn("stream wake delivery result callback disposal failed", { error });
|
|
695
|
+
}
|
|
696
|
+
}));
|
|
697
|
+
};
|
|
698
|
+
const capabilities = hostRuntimeCapabilities(entry.processor, {
|
|
699
|
+
now,
|
|
700
|
+
snapshot: () => entry.runner.snapshot()
|
|
701
|
+
});
|
|
702
|
+
return {
|
|
703
|
+
streamId: args.stream.streamId,
|
|
704
|
+
checkpointOffset: opened.checkpointOffset,
|
|
705
|
+
processEventBatch,
|
|
706
|
+
openedBy: { processor: { announcement: announceContract(entry.processor.contract) } },
|
|
707
|
+
...capabilities
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/processors/processor-facet.ts
|
|
714
|
+
/**
|
|
715
|
+
* Where the configured identity lives in the facet's own kv. Exported so a
|
|
716
|
+
* subclass's own doors can self-heal a fresh incarnation (re-run the
|
|
717
|
+
* idempotent `configure` from the stash) before the base's boot microtask has
|
|
718
|
+
* run — see apps/os `ProcessorFacet.#requireRegistry`.
|
|
719
|
+
*/
|
|
720
|
+
const FACET_IDENTITY_KEY = "iterate:processor-facet:identity";
|
|
721
|
+
/**
|
|
722
|
+
* A view of `target` with `overrides` in front and everything else passed
|
|
723
|
+
* through. Proxies (not spreads or Object.create) because DurableObjectState
|
|
724
|
+
* and DurableObjectStorage are host objects: their methods must be invoked
|
|
725
|
+
* with the REAL receiver or workerd throws "Illegal invocation". (The same
|
|
726
|
+
* overlay as sdk.ts's `selfAlarmState` facade — duplicated here because the
|
|
727
|
+
* sdk entry drags its whole dependency graph along.)
|
|
728
|
+
*/
|
|
729
|
+
function overlay(target, overrides) {
|
|
730
|
+
return new Proxy(target, { get(object, prop) {
|
|
731
|
+
if (Object.hasOwn(overrides, prop)) return overrides[prop];
|
|
732
|
+
const value = Reflect.get(object, prop, object);
|
|
733
|
+
return typeof value === "function" ? value.bind(object) : value;
|
|
734
|
+
} });
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* A facet's `DurableObjectState` facade for `createStreamProcessorRegistry`:
|
|
738
|
+
* ONLY `storage.setAlarm` / `storage.deleteAlarm` / `storage.getAlarm` are
|
|
739
|
+
* overridden to dial the parent's {@link ProcessorFacetAlarmProxy}; kv,
|
|
740
|
+
* waitUntil, and everything else pass through untouched. `parentAlarms` is
|
|
741
|
+
* invoked PER CALL — the parent stub must be resolved lazily from the stashed
|
|
742
|
+
* identity, because the recovery adapter's boot reconcile issues alarm calls
|
|
743
|
+
* at construction and a stub captured once would outlive its RPC session.
|
|
744
|
+
*/
|
|
745
|
+
function facetProcessorDurableObjectState(ctx, parentAlarms) {
|
|
746
|
+
return overlay(ctx, { storage: overlay(ctx.storage, {
|
|
747
|
+
setAlarm: async (scheduledTime) => {
|
|
748
|
+
disposeIgnoredRpcResult(await parentAlarms().proxySetAlarm(typeof scheduledTime === "number" ? scheduledTime : scheduledTime.getTime()));
|
|
749
|
+
},
|
|
750
|
+
deleteAlarm: async () => {
|
|
751
|
+
disposeIgnoredRpcResult(await parentAlarms().proxyDeleteAlarm());
|
|
752
|
+
},
|
|
753
|
+
getAlarm: async () => await parentAlarms().proxyGetAlarm()
|
|
754
|
+
}) });
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Plain-copy an `AlarmInvocationInfo` before a facet hop. The platform hands
|
|
758
|
+
* the parent's `alarm()` a host object that does not serialize across RPC;
|
|
759
|
+
* a parent replaying a fire into {@link ProcessorFacet.handleAlarm} must send
|
|
760
|
+
* this copy instead.
|
|
761
|
+
*/
|
|
762
|
+
function plainAlarmInvocationInfo(info) {
|
|
763
|
+
if (info === void 0) return void 0;
|
|
764
|
+
const { scheduledTime } = info;
|
|
765
|
+
return {
|
|
766
|
+
...scheduledTime === void 0 ? {} : { scheduledTime },
|
|
767
|
+
isRetry: info.isRetry,
|
|
768
|
+
retryCount: info.retryCount
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Generic base for hosting stream processors in a Durable Object facet.
|
|
773
|
+
* Subclasses are the per-host composition (the facets task's target shape) and
|
|
774
|
+
* supply exactly two things: how to dial the parent's alarm proxy and what
|
|
775
|
+
* this host runs —
|
|
776
|
+
*
|
|
777
|
+
* ```ts
|
|
778
|
+
* export class AgentProcessors extends ProcessorFacet<Env> {
|
|
779
|
+
* protected parentAlarms({ parentName }: ProcessorFacetIdentity) {
|
|
780
|
+
* return this.env.STREAM.getByName(parentName);
|
|
781
|
+
* }
|
|
782
|
+
* protected createHost({ path, projectId }: ProcessorFacetIdentity): ProcessorFacetHost {
|
|
783
|
+
* const stream = ...; // dial the parent stream
|
|
784
|
+
* return {
|
|
785
|
+
* stream,
|
|
786
|
+
* version: workerVersion(this.env),
|
|
787
|
+
* registerProcessors: (registry) => {
|
|
788
|
+
* registry.register(
|
|
789
|
+
* new AgentProcessor({ stream, path, projectId, ... }),
|
|
790
|
+
* { recovery: true, name: "agent" },
|
|
791
|
+
* );
|
|
792
|
+
* },
|
|
793
|
+
* };
|
|
794
|
+
* }
|
|
795
|
+
* }
|
|
796
|
+
* ```
|
|
797
|
+
*
|
|
798
|
+
* The parent creates it with `ctx.facets.get(name, () => ({ class:
|
|
799
|
+
* ctx.exports.AgentProcessors }))`, calls `configure()` once (idempotent), and
|
|
800
|
+
* routes wakes/alarms/doors to the RPC surface below.
|
|
801
|
+
*
|
|
802
|
+
* THIS CLASS MUST NEVER DEFINE `alarm()` — and neither may a subclass. The
|
|
803
|
+
* fire arrives as `handleAlarm` (an ordinary RPC verb) instead: workerd
|
|
804
|
+
* reserves `alarm` for its native handler, and with one present a failed
|
|
805
|
+
* native `setAlarm` poisons the facet's output gate (workerd#6810). The
|
|
806
|
+
* constructor enforces this.
|
|
807
|
+
*/
|
|
808
|
+
var ProcessorFacet = class extends DurableObject {
|
|
809
|
+
#host;
|
|
810
|
+
constructor(ctx, env) {
|
|
811
|
+
super(ctx, env);
|
|
812
|
+
if ("alarm" in this && typeof this.alarm === "function") throw new Error(`${this.constructor.name} must not define alarm(): facets have no native alarms (workerd#6810 — a failed setAlarm would poison this facet's output gate). The parent's fire arrives as handleAlarm() instead.`);
|
|
813
|
+
Promise.resolve().then(() => {
|
|
814
|
+
try {
|
|
815
|
+
if (this.#readIdentity() !== void 0) this.#requireHost();
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.error("ProcessorFacet boot host construction failed", error);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* First-contact identity stash, idempotent: re-configuring with the same
|
|
823
|
+
* identity is a no-op (and ensures the host is built); a DIFFERENT identity
|
|
824
|
+
* throws — a facet's coordinate never changes, so a mismatch is a miswire,
|
|
825
|
+
* not a migration.
|
|
826
|
+
*/
|
|
827
|
+
configure(identity) {
|
|
828
|
+
const stashed = this.#readIdentity();
|
|
829
|
+
if (stashed !== void 0) {
|
|
830
|
+
if (stashed.parentName !== identity.parentName || stashed.projectId !== identity.projectId || stashed.path !== identity.path) throw new Error(`ProcessorFacet is configured for ${stashed.projectId ?? "null"}:${stashed.path} (parent "${stashed.parentName}") and cannot be re-configured for ${identity.projectId ?? "null"}:${identity.path} (parent "${identity.parentName}")`);
|
|
831
|
+
this.#requireHost();
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
this.ctx.storage.kv.put(FACET_IDENTITY_KEY, {
|
|
835
|
+
parentName: identity.parentName,
|
|
836
|
+
projectId: identity.projectId,
|
|
837
|
+
path: identity.path
|
|
838
|
+
});
|
|
839
|
+
this.#requireHost();
|
|
840
|
+
}
|
|
841
|
+
/** The wake door — the parent's colocated delivery dial. Routes to the
|
|
842
|
+
* registry: name-first resolution, coordinate fence, per-batch settlement
|
|
843
|
+
* through each batch's independent `reportDeliveryResult`. */
|
|
844
|
+
wakeStreamProcessor(request) {
|
|
845
|
+
return this.#requireHost().registry.wakeStreamProcessor(request);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* The parent's alarm replay door — deliberately NOT named `alarm` (see the
|
|
849
|
+
* class doc). The parent plain-copies its `AlarmInvocationInfo`
|
|
850
|
+
* ({@link plainAlarmInvocationInfo}) and awaits this; a rejection propagates
|
|
851
|
+
* so the parent can rethrow and get the platform's alarm retry.
|
|
852
|
+
*/
|
|
853
|
+
async handleAlarm(info) {
|
|
854
|
+
if (this.#readIdentity() === void 0) {
|
|
855
|
+
console.warn("ProcessorFacet.handleAlarm before configure(); nothing to revive");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
await this.#requireHost().registry.handleAlarm(plainAlarmInvocationInfo(info));
|
|
859
|
+
}
|
|
860
|
+
/** One consistent read of a hosted processor's committed fold. `name` may be
|
|
861
|
+
* omitted on a single-processor host. */
|
|
862
|
+
async snapshot(args) {
|
|
863
|
+
return await this.#reads(args?.name).snapshot();
|
|
864
|
+
}
|
|
865
|
+
/** The hosted processor's runtime state (runner snapshot + contributed
|
|
866
|
+
* runtime bag). `name` may be omitted on a single-processor host. */
|
|
867
|
+
async getRuntimeState(args) {
|
|
868
|
+
return await this.#reads(args?.name).getRuntimeState();
|
|
869
|
+
}
|
|
870
|
+
/** Resolve once the named processor's acknowledged cursor reaches `offset` —
|
|
871
|
+
* the read-your-writes barrier. The subscription catalog's uniform
|
|
872
|
+
* `waitUntilProcessed` delegates its facet-placed processor rows here. */
|
|
873
|
+
async waitUntilProcessed(args) {
|
|
874
|
+
const { name, ...input } = args;
|
|
875
|
+
await this.#reads(name).waitUntilEvent(input);
|
|
876
|
+
}
|
|
877
|
+
/** Pull the named processor through the durable stream tail — the parent
|
|
878
|
+
* facade's catch-up-before-snapshot leg, dialed before every facet read.
|
|
879
|
+
* `name` may be omitted on a single-processor host. */
|
|
880
|
+
async catchUp(args) {
|
|
881
|
+
await this.#reads(args?.name).catchUp();
|
|
882
|
+
}
|
|
883
|
+
/** Read a transient snapshot or delta for the parent's current stream lifetime.
|
|
884
|
+
* Cold or replaced processors hydrate from durable progress. Warm reads use
|
|
885
|
+
* committed local state: polling must not call back into the parent stream
|
|
886
|
+
* for every processor while it is delivering events to this facet. */
|
|
887
|
+
async readLiveState(args) {
|
|
888
|
+
const { registry } = this.#requireHost();
|
|
889
|
+
if (registry.names.some((name) => {
|
|
890
|
+
const reads = registry.reads(name);
|
|
891
|
+
return !reads.isLoaded || reads.currentStreamId !== args.streamId;
|
|
892
|
+
})) await registry.loadAndRefreshLive();
|
|
893
|
+
else registry.refreshLive();
|
|
894
|
+
return registry.live.readSince(args.cursor, { patchVersion: args.patchVersion });
|
|
895
|
+
}
|
|
896
|
+
/** Direct subscriptions retain their callbacks across the Workers RPC hop. */
|
|
897
|
+
liveState() {
|
|
898
|
+
const { registry } = this.#requireHost();
|
|
899
|
+
return {
|
|
900
|
+
get: async () => {
|
|
901
|
+
await registry.loadAndRefreshLive();
|
|
902
|
+
return registry.live.getState();
|
|
903
|
+
},
|
|
904
|
+
subscribe: async (onUpdate, options) => {
|
|
905
|
+
await registry.loadAndRefreshLive();
|
|
906
|
+
const subscription = registry.live.subscribe(onUpdate, options);
|
|
907
|
+
return {
|
|
908
|
+
ping: () => subscription.ping(),
|
|
909
|
+
unsubscribe: () => subscription.unsubscribe(),
|
|
910
|
+
[Symbol.dispose]: () => subscription.unsubscribe()
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
#readIdentity() {
|
|
916
|
+
return this.ctx.storage.kv.get(FACET_IDENTITY_KEY);
|
|
917
|
+
}
|
|
918
|
+
#requireIdentity() {
|
|
919
|
+
const identity = this.#readIdentity();
|
|
920
|
+
if (identity === void 0) throw new Error("ProcessorFacet has no stashed identity yet — call configure() first");
|
|
921
|
+
return identity;
|
|
922
|
+
}
|
|
923
|
+
#requireHost() {
|
|
924
|
+
if (this.#host === void 0) {
|
|
925
|
+
const identity = this.#requireIdentity();
|
|
926
|
+
const host = this.createHost(identity);
|
|
927
|
+
const registry = createStreamProcessorRegistry(facetProcessorDurableObjectState(this.ctx, () => this.parentAlarms(this.#requireIdentity())), {
|
|
928
|
+
stream: host.stream,
|
|
929
|
+
path: identity.path,
|
|
930
|
+
projectId: identity.projectId,
|
|
931
|
+
version: host.version,
|
|
932
|
+
...host.now === void 0 ? {} : { now: host.now },
|
|
933
|
+
...host.getLiveState === void 0 ? {} : { getLiveState: host.getLiveState }
|
|
934
|
+
});
|
|
935
|
+
host.registerProcessors(registry);
|
|
936
|
+
this.#host = { registry };
|
|
937
|
+
}
|
|
938
|
+
return this.#host;
|
|
939
|
+
}
|
|
940
|
+
#reads(name) {
|
|
941
|
+
const { registry } = this.#requireHost();
|
|
942
|
+
if (name !== void 0) return registry.reads(name);
|
|
943
|
+
const names = registry.names;
|
|
944
|
+
if (names.length === 1) return registry.reads(names[0]);
|
|
945
|
+
throw new Error(`this facet hosts ${names.length} processors (${names.join(", ") || "none"}); pass a name`);
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
//#endregion
|
|
949
|
+
export { createStreamProcessorRegistry as a, compactPatch as c, disposeIgnoredRpcResult as d, isThenable as f, plainAlarmInvocationInfo as i, applyPatch as l, ProcessorFacet as n, LiveState as o, retainCallback as p, facetProcessorDurableObjectState as r, applyCompactPatch as s, FACET_IDENTITY_KEY as t, diff as u };
|
|
950
|
+
|
|
951
|
+
//# sourceMappingURL=cloudflare-BTm90gQ4.mjs.map
|