@ewhauser/world-celld 0.3.0 → 0.3.2
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/CHANGELOG.md +23 -0
- package/README.md +49 -19
- package/celld-worker/README.md +5 -3
- package/celld-worker/worker.ts +9 -1
- package/celld-worker/wrangler.jsonc +13 -4
- package/dist/index.d.ts +168 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +432 -124
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +47 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1988 -558
- package/dist/testing.js.map +1 -1
- package/dist/util-ya3V_FTA.js +558 -0
- package/dist/util-ya3V_FTA.js.map +1 -0
- package/dist/worker.d.ts +238 -108
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +2082 -520
- package/dist/worker.js.map +1 -1
- package/package.json +4 -2
- package/dist/retention-bLY1vcOu.js +0 -164
- package/dist/retention-bLY1vcOu.js.map +0 -1
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
//#region src/stream-protocol.ts
|
|
2
|
+
/**
|
|
3
|
+
* Hard-cutover stream protocol shared by the client, Worker router, and
|
|
4
|
+
* StreamDO. Chunk payloads use a compact binary envelope on the public HTTP
|
|
5
|
+
* boundary and Uint8Array values on the DO/storage boundary.
|
|
6
|
+
*/
|
|
7
|
+
const STREAM_BATCH_CONTENT_TYPE = "application/vnd.world-celld.stream-batch.v1";
|
|
8
|
+
/** Metadata plus payload and size-index entries remain below the 128-key platform limit. */
|
|
9
|
+
const MAX_STREAM_WRITE_CHUNKS = 32;
|
|
10
|
+
/** A chunk remains below the 2 MiB SQLite-backed DO key/value limit. */
|
|
11
|
+
const MAX_STREAM_CHUNK_BYTES = 1048576;
|
|
12
|
+
/** Keeps serialized RPC bodies well below the 32 MiB Workers RPC limit. */
|
|
13
|
+
const MAX_STREAM_BATCH_BYTES = 8388608;
|
|
14
|
+
const MAX_STREAM_READ_CHUNKS = 32;
|
|
15
|
+
const MAX_STREAM_READ_BYTES = 8388608;
|
|
16
|
+
const MAX_STREAM_ERROR_BYTES = 16384;
|
|
17
|
+
/** Must remain below the default 30 second fleet request deadline. */
|
|
18
|
+
const MAX_STREAM_LONG_POLL_MS = 2e4;
|
|
19
|
+
const WRITE_MAGIC = 1464030039;
|
|
20
|
+
const WRITE_ACK_MAGIC = 1464030017;
|
|
21
|
+
const READ_MAGIC = 1464030034;
|
|
22
|
+
const PROTOCOL_VERSION = 1;
|
|
23
|
+
const WRITE_HEADER_BYTES = 8;
|
|
24
|
+
const WRITE_ACK_BYTES = 16;
|
|
25
|
+
const READ_HEADER_BYTES = 28;
|
|
26
|
+
const STATE_TO_WIRE = {
|
|
27
|
+
open: 0,
|
|
28
|
+
closed: 1,
|
|
29
|
+
errored: 2,
|
|
30
|
+
expired: 3
|
|
31
|
+
};
|
|
32
|
+
const WIRE_TO_STATE = [
|
|
33
|
+
"open",
|
|
34
|
+
"closed",
|
|
35
|
+
"errored",
|
|
36
|
+
"expired"
|
|
37
|
+
];
|
|
38
|
+
function protocolError(message) {
|
|
39
|
+
const error = /* @__PURE__ */ new Error(`world-celld: invalid stream protocol: ${message}`);
|
|
40
|
+
error.name = "StreamProtocolError";
|
|
41
|
+
return error;
|
|
42
|
+
}
|
|
43
|
+
function requireSafeIndex(value, field) {
|
|
44
|
+
if (!Number.isSafeInteger(value) || value < -1 || value > 2147483647) throw protocolError(`${field} is out of range`);
|
|
45
|
+
}
|
|
46
|
+
function validateStreamWriteChunks(chunks) {
|
|
47
|
+
if (chunks.length === 0) throw protocolError("batch must contain at least one chunk");
|
|
48
|
+
if (chunks.length > 32) throw protocolError(`batch exceeds 32 chunks`);
|
|
49
|
+
let totalBytes = 0;
|
|
50
|
+
for (const chunk of chunks) {
|
|
51
|
+
if (!(chunk instanceof Uint8Array)) throw protocolError("chunk is not binary data");
|
|
52
|
+
if (chunk.byteLength > 1048576) throw protocolError(`chunk exceeds ${MAX_STREAM_CHUNK_BYTES} bytes`);
|
|
53
|
+
totalBytes += chunk.byteLength;
|
|
54
|
+
if (totalBytes > 8388608) throw protocolError(`batch exceeds ${MAX_STREAM_BATCH_BYTES} bytes`);
|
|
55
|
+
}
|
|
56
|
+
return totalBytes;
|
|
57
|
+
}
|
|
58
|
+
function validateStreamReadRequest(request) {
|
|
59
|
+
if (!request.runId) throw protocolError("runId is required");
|
|
60
|
+
if (!Number.isSafeInteger(request.startIndex) || request.startIndex < 0) throw protocolError("startIndex must be a non-negative safe integer");
|
|
61
|
+
if (!Number.isSafeInteger(request.maxChunks) || request.maxChunks < 0 || request.maxChunks > 32) throw protocolError(`maxChunks must be between 0 and 32`);
|
|
62
|
+
if (!Number.isSafeInteger(request.maxBytes) || request.maxBytes < 1048576 || request.maxBytes > 8388608) throw protocolError(`maxBytes must be between ${MAX_STREAM_CHUNK_BYTES} and ${MAX_STREAM_READ_BYTES}`);
|
|
63
|
+
if (!Number.isSafeInteger(request.waitMs) || request.waitMs < 0 || request.waitMs > 2e4) throw protocolError(`waitMs must be between 0 and ${MAX_STREAM_LONG_POLL_MS}`);
|
|
64
|
+
}
|
|
65
|
+
function encodeStreamWriteBatch(chunks) {
|
|
66
|
+
const totalBytes = validateStreamWriteChunks(chunks);
|
|
67
|
+
const encoded = new Uint8Array(WRITE_HEADER_BYTES + chunks.length * 4 + totalBytes);
|
|
68
|
+
const view = new DataView(encoded.buffer);
|
|
69
|
+
view.setUint32(0, WRITE_MAGIC);
|
|
70
|
+
view.setUint16(4, PROTOCOL_VERSION);
|
|
71
|
+
view.setUint16(6, chunks.length);
|
|
72
|
+
let offset = WRITE_HEADER_BYTES;
|
|
73
|
+
for (const chunk of chunks) {
|
|
74
|
+
view.setUint32(offset, chunk.byteLength);
|
|
75
|
+
offset += 4;
|
|
76
|
+
encoded.set(chunk, offset);
|
|
77
|
+
offset += chunk.byteLength;
|
|
78
|
+
}
|
|
79
|
+
return encoded;
|
|
80
|
+
}
|
|
81
|
+
function decodeStreamWriteBatch(encoded) {
|
|
82
|
+
if (encoded.byteLength < WRITE_HEADER_BYTES) throw protocolError("truncated write header");
|
|
83
|
+
const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
|
|
84
|
+
if (view.getUint32(0) !== WRITE_MAGIC) throw protocolError("bad write magic");
|
|
85
|
+
if (view.getUint16(4) !== PROTOCOL_VERSION) throw protocolError("unsupported write version");
|
|
86
|
+
const count = view.getUint16(6);
|
|
87
|
+
const chunks = [];
|
|
88
|
+
let offset = WRITE_HEADER_BYTES;
|
|
89
|
+
for (let index = 0; index < count; index++) {
|
|
90
|
+
if (offset + 4 > encoded.byteLength) throw protocolError("truncated chunk length");
|
|
91
|
+
const length = view.getUint32(offset);
|
|
92
|
+
offset += 4;
|
|
93
|
+
if (offset + length > encoded.byteLength) throw protocolError("truncated chunk data");
|
|
94
|
+
chunks.push(new Uint8Array(encoded.subarray(offset, offset + length)));
|
|
95
|
+
offset += length;
|
|
96
|
+
}
|
|
97
|
+
if (offset !== encoded.byteLength) throw protocolError("trailing write data");
|
|
98
|
+
validateStreamWriteChunks(chunks);
|
|
99
|
+
return chunks;
|
|
100
|
+
}
|
|
101
|
+
function encodeStreamWriteResult(result) {
|
|
102
|
+
requireSafeIndex(result.startIndex, "startIndex");
|
|
103
|
+
requireSafeIndex(result.tailIndex, "tailIndex");
|
|
104
|
+
if (!Number.isSafeInteger(result.count) || result.count < 1 || result.count > 32) throw protocolError("write count is out of range");
|
|
105
|
+
const encoded = new Uint8Array(WRITE_ACK_BYTES);
|
|
106
|
+
const view = new DataView(encoded.buffer);
|
|
107
|
+
view.setUint32(0, WRITE_ACK_MAGIC);
|
|
108
|
+
view.setUint16(4, PROTOCOL_VERSION);
|
|
109
|
+
view.setUint16(6, result.count);
|
|
110
|
+
view.setInt32(8, result.startIndex);
|
|
111
|
+
view.setInt32(12, result.tailIndex);
|
|
112
|
+
return encoded;
|
|
113
|
+
}
|
|
114
|
+
function decodeStreamWriteResult(encoded) {
|
|
115
|
+
if (encoded.byteLength !== WRITE_ACK_BYTES) throw protocolError("invalid write response size");
|
|
116
|
+
const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
|
|
117
|
+
if (view.getUint32(0) !== WRITE_ACK_MAGIC) throw protocolError("bad write response magic");
|
|
118
|
+
if (view.getUint16(4) !== PROTOCOL_VERSION) throw protocolError("unsupported write response version");
|
|
119
|
+
const count = view.getUint16(6);
|
|
120
|
+
if (count < 1 || count > 32) throw protocolError("write response count is out of range");
|
|
121
|
+
const result = {
|
|
122
|
+
count,
|
|
123
|
+
startIndex: view.getInt32(8),
|
|
124
|
+
tailIndex: view.getInt32(12)
|
|
125
|
+
};
|
|
126
|
+
requireSafeIndex(result.startIndex, "startIndex");
|
|
127
|
+
requireSafeIndex(result.tailIndex, "tailIndex");
|
|
128
|
+
if (result.tailIndex !== result.startIndex + result.count - 1) throw protocolError("write response offsets are inconsistent");
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function encodeStreamReadResult(result) {
|
|
132
|
+
requireSafeIndex(result.startIndex, "startIndex");
|
|
133
|
+
requireSafeIndex(result.tailIndex, "tailIndex");
|
|
134
|
+
let chunkBytes = 0;
|
|
135
|
+
for (const chunk of result.chunks) chunkBytes += 4 + chunk.byteLength;
|
|
136
|
+
if (result.chunks.length > 32 || chunkBytes > 8388736) throw protocolError("read result exceeds configured bounds");
|
|
137
|
+
const encoder = new TextEncoder();
|
|
138
|
+
const errorName = result.error ? encoder.encode(result.error.name) : /* @__PURE__ */ new Uint8Array();
|
|
139
|
+
const errorMessage = result.error ? encoder.encode(result.error.message) : /* @__PURE__ */ new Uint8Array();
|
|
140
|
+
if (errorName.byteLength + errorMessage.byteLength > 16384) throw protocolError(`stream error exceeds ${MAX_STREAM_ERROR_BYTES} bytes`);
|
|
141
|
+
const encoded = new Uint8Array(READ_HEADER_BYTES + chunkBytes + errorName.byteLength + errorMessage.byteLength);
|
|
142
|
+
const view = new DataView(encoded.buffer);
|
|
143
|
+
view.setUint32(0, READ_MAGIC);
|
|
144
|
+
view.setUint8(4, PROTOCOL_VERSION);
|
|
145
|
+
const state = STATE_TO_WIRE[result.state];
|
|
146
|
+
if (state === void 0) throw protocolError("unknown stream state");
|
|
147
|
+
view.setUint8(5, state);
|
|
148
|
+
view.setUint8(6, result.timedOut ? 1 : 0);
|
|
149
|
+
view.setUint8(7, 0);
|
|
150
|
+
view.setInt32(8, result.startIndex);
|
|
151
|
+
view.setInt32(12, result.tailIndex);
|
|
152
|
+
view.setUint32(16, result.chunks.length);
|
|
153
|
+
view.setUint32(20, errorName.byteLength);
|
|
154
|
+
view.setUint32(24, errorMessage.byteLength);
|
|
155
|
+
let offset = READ_HEADER_BYTES;
|
|
156
|
+
for (const chunk of result.chunks) {
|
|
157
|
+
view.setUint32(offset, chunk.byteLength);
|
|
158
|
+
offset += 4;
|
|
159
|
+
encoded.set(chunk, offset);
|
|
160
|
+
offset += chunk.byteLength;
|
|
161
|
+
}
|
|
162
|
+
encoded.set(errorName, offset);
|
|
163
|
+
offset += errorName.byteLength;
|
|
164
|
+
encoded.set(errorMessage, offset);
|
|
165
|
+
return encoded;
|
|
166
|
+
}
|
|
167
|
+
function decodeStreamReadResult(encoded) {
|
|
168
|
+
if (encoded.byteLength < READ_HEADER_BYTES) throw protocolError("truncated read header");
|
|
169
|
+
const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);
|
|
170
|
+
if (view.getUint32(0) !== READ_MAGIC) throw protocolError("bad read magic");
|
|
171
|
+
if (view.getUint8(4) !== PROTOCOL_VERSION) throw protocolError("unsupported read version");
|
|
172
|
+
const state = WIRE_TO_STATE[view.getUint8(5)];
|
|
173
|
+
if (!state) throw protocolError("unknown stream state");
|
|
174
|
+
const startIndex = view.getInt32(8);
|
|
175
|
+
const tailIndex = view.getInt32(12);
|
|
176
|
+
requireSafeIndex(startIndex, "startIndex");
|
|
177
|
+
requireSafeIndex(tailIndex, "tailIndex");
|
|
178
|
+
const count = view.getUint32(16);
|
|
179
|
+
const errorNameLength = view.getUint32(20);
|
|
180
|
+
const errorMessageLength = view.getUint32(24);
|
|
181
|
+
if (count > 32) throw protocolError("read chunk count exceeds limit");
|
|
182
|
+
if (errorNameLength + errorMessageLength > 16384) throw protocolError("read error exceeds limit");
|
|
183
|
+
const chunks = [];
|
|
184
|
+
let offset = READ_HEADER_BYTES;
|
|
185
|
+
let totalBytes = 0;
|
|
186
|
+
for (let index = 0; index < count; index++) {
|
|
187
|
+
if (offset + 4 > encoded.byteLength) throw protocolError("truncated read chunk length");
|
|
188
|
+
const length = view.getUint32(offset);
|
|
189
|
+
offset += 4;
|
|
190
|
+
if (offset + length > encoded.byteLength) throw protocolError("truncated read chunk data");
|
|
191
|
+
totalBytes += length;
|
|
192
|
+
if (totalBytes > 8388608) throw protocolError("read bytes exceed limit");
|
|
193
|
+
chunks.push(encoded.subarray(offset, offset + length));
|
|
194
|
+
offset += length;
|
|
195
|
+
}
|
|
196
|
+
if (offset + errorNameLength + errorMessageLength !== encoded.byteLength) throw protocolError("invalid read error payload");
|
|
197
|
+
const decoder = new TextDecoder();
|
|
198
|
+
const error = errorNameLength > 0 || errorMessageLength > 0 ? {
|
|
199
|
+
name: decoder.decode(encoded.subarray(offset, offset + errorNameLength)),
|
|
200
|
+
message: decoder.decode(encoded.subarray(offset + errorNameLength))
|
|
201
|
+
} : void 0;
|
|
202
|
+
return {
|
|
203
|
+
startIndex,
|
|
204
|
+
tailIndex,
|
|
205
|
+
chunks,
|
|
206
|
+
state,
|
|
207
|
+
timedOut: (view.getUint8(6) & 1) !== 0,
|
|
208
|
+
error
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function normalizeStreamError(error) {
|
|
212
|
+
let normalized;
|
|
213
|
+
if (error instanceof Error) normalized = {
|
|
214
|
+
name: error.name || "Error",
|
|
215
|
+
message: error.message
|
|
216
|
+
};
|
|
217
|
+
else if (typeof error === "string") normalized = {
|
|
218
|
+
name: "Error",
|
|
219
|
+
message: error
|
|
220
|
+
};
|
|
221
|
+
else if (error && typeof error === "object" && typeof error.name === "string" && typeof error.message === "string") normalized = {
|
|
222
|
+
name: error.name,
|
|
223
|
+
message: error.message
|
|
224
|
+
};
|
|
225
|
+
else normalized = {
|
|
226
|
+
name: "Error",
|
|
227
|
+
message: String(error)
|
|
228
|
+
};
|
|
229
|
+
normalized.name = normalized.name.slice(0, 1024);
|
|
230
|
+
normalized.message = normalized.message.slice(0, 4096);
|
|
231
|
+
while (new TextEncoder().encode(normalized.name).byteLength + new TextEncoder().encode(normalized.message).byteLength > MAX_STREAM_ERROR_BYTES) {
|
|
232
|
+
normalized.message = normalized.message.slice(0, Math.max(0, normalized.message.length - 256));
|
|
233
|
+
if (normalized.message.length === 0) normalized.name = normalized.name.slice(0, Math.max(0, normalized.name.length - 256));
|
|
234
|
+
}
|
|
235
|
+
return normalized;
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/lifecycle.ts
|
|
239
|
+
/**
|
|
240
|
+
* Hard protocol bounds used by lifecycle leases and derivative-fence cleanup.
|
|
241
|
+
*
|
|
242
|
+
* These are not retention TTLs. They bound how long a request which was
|
|
243
|
+
* admitted before expiry may still mutate a derivative Durable Object:
|
|
244
|
+
*
|
|
245
|
+
* - fleet calls have at most three 5-minute attempts plus bounded retry delay;
|
|
246
|
+
* - one queue delivery owns its lease for at most 5 minutes plus 30 seconds;
|
|
247
|
+
* - hook admission spans reserve retries, one authoritative apply, and
|
|
248
|
+
* finalize retries before the exact claim must be reacquired.
|
|
249
|
+
*/
|
|
250
|
+
const MAX_FLEET_RPC_TIMEOUT_MS = 3e5;
|
|
251
|
+
const MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS = 1200900;
|
|
252
|
+
const MAX_QUEUE_DELIVERY_TIMEOUT_MS = 3e5;
|
|
253
|
+
const QUEUE_INFLIGHT_GRACE_MS = 3e4;
|
|
254
|
+
const MAX_QUEUE_INFLIGHT_LEASE_MS = 33e4;
|
|
255
|
+
const CATALOG_FENCE_GRACE_MS = MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS;
|
|
256
|
+
const QUEUE_FENCE_GRACE_MS = Math.max(MAX_FLEET_RPC_TIMEOUT_MS, MAX_QUEUE_INFLIGHT_LEASE_MS);
|
|
257
|
+
const HOOK_CLAIM_LEASE_MS = 2101800;
|
|
258
|
+
const LIFECYCLE_COMPACTION_RETRY_MS = 1e3;
|
|
259
|
+
const INDEX_PROTOCOL_VERSION = "v1";
|
|
260
|
+
const HOOK_RELEASE_BATCH = 64;
|
|
261
|
+
/** Stable FNV-1a routing hash. Collisions share a shard but never a storage key. */
|
|
262
|
+
function stableIndexHash(value) {
|
|
263
|
+
let hash = 2166136261;
|
|
264
|
+
for (const byte of new TextEncoder().encode(value)) {
|
|
265
|
+
hash ^= byte;
|
|
266
|
+
hash = Math.imul(hash, 16777619);
|
|
267
|
+
}
|
|
268
|
+
return hash >>> 0;
|
|
269
|
+
}
|
|
270
|
+
function shardName(domain, value, cardinality) {
|
|
271
|
+
const width = Math.max(2, (cardinality - 1).toString(16).length);
|
|
272
|
+
const shard = stableIndexHash(value) % cardinality;
|
|
273
|
+
return `${domain}:${INDEX_PROTOCOL_VERSION}:${shard.toString(16).padStart(width, "0")}`;
|
|
274
|
+
}
|
|
275
|
+
function runCatalogShardName(runId) {
|
|
276
|
+
return shardName("run-catalog", runId, 16);
|
|
277
|
+
}
|
|
278
|
+
function hookTokenShardName(token) {
|
|
279
|
+
return shardName("hook-token", token, 32);
|
|
280
|
+
}
|
|
281
|
+
function hookIdShardName(hookId) {
|
|
282
|
+
return shardName("hook-id", hookId, 32);
|
|
283
|
+
}
|
|
284
|
+
function allRunCatalogShardNames() {
|
|
285
|
+
return Array.from({ length: 16 }, (_, shard) => `run-catalog:${INDEX_PROTOCOL_VERSION}:${shard.toString(16).padStart(2, "0")}`);
|
|
286
|
+
}
|
|
287
|
+
function stub(namespace, name) {
|
|
288
|
+
return namespace.get(namespace.idFromName(name));
|
|
289
|
+
}
|
|
290
|
+
function runKeys(run) {
|
|
291
|
+
const timestamp = run.createdAt.getTime().toString().padStart(13, "0");
|
|
292
|
+
return [`run:${run.workflowName}:${timestamp}:${run.runId}`, `runall:${timestamp}:${run.runId}`];
|
|
293
|
+
}
|
|
294
|
+
function groupByShard(values, route) {
|
|
295
|
+
const groups = /* @__PURE__ */ new Map();
|
|
296
|
+
for (const value of values) {
|
|
297
|
+
const name = route(value);
|
|
298
|
+
const group = groups.get(name);
|
|
299
|
+
if (group) group.push(value);
|
|
300
|
+
else groups.set(name, [value]);
|
|
301
|
+
}
|
|
302
|
+
return groups;
|
|
303
|
+
}
|
|
304
|
+
function sameOwner(left, right) {
|
|
305
|
+
return left.runId === right.runId && left.hookId === right.hookId;
|
|
306
|
+
}
|
|
307
|
+
function compareBytes(left, right) {
|
|
308
|
+
const length = Math.min(left.byteLength, right.byteLength);
|
|
309
|
+
for (let index = 0; index < length; index++) {
|
|
310
|
+
const difference = left[index] - right[index];
|
|
311
|
+
if (difference !== 0) return difference;
|
|
312
|
+
}
|
|
313
|
+
return left.byteLength - right.byteLength;
|
|
314
|
+
}
|
|
315
|
+
async function inBatches(values, operation) {
|
|
316
|
+
let deleted = 0;
|
|
317
|
+
for (let offset = 0; offset < values.length; offset += HOOK_RELEASE_BATCH) deleted += (await operation(values.slice(offset, offset + HOOK_RELEASE_BATCH))).deleted;
|
|
318
|
+
return deleted;
|
|
319
|
+
}
|
|
320
|
+
function createWorkflowIndex(bindings) {
|
|
321
|
+
const releaseHooks = async (request) => {
|
|
322
|
+
const tokenGroups = groupByShard(request.hooks, (hook) => hookTokenShardName(hook.token));
|
|
323
|
+
const idGroups = groupByShard(request.hooks, (hook) => hookIdShardName(hook.hookId));
|
|
324
|
+
const releases = [];
|
|
325
|
+
for (const [name, hooks] of tokenGroups) {
|
|
326
|
+
const target = stub(bindings.hookTokens, name);
|
|
327
|
+
releases.push(inBatches(hooks.map((hook) => ({
|
|
328
|
+
token: hook.token,
|
|
329
|
+
owner: {
|
|
330
|
+
runId: request.runId,
|
|
331
|
+
hookId: hook.hookId
|
|
332
|
+
}
|
|
333
|
+
})), (batch) => target.releaseBatch(batch)));
|
|
334
|
+
}
|
|
335
|
+
for (const [name, hooks] of idGroups) {
|
|
336
|
+
const target = stub(bindings.hookIds, name);
|
|
337
|
+
releases.push(inBatches(hooks.map((hook) => ({
|
|
338
|
+
hookId: hook.hookId,
|
|
339
|
+
owner: {
|
|
340
|
+
runId: request.runId,
|
|
341
|
+
hookId: hook.hookId
|
|
342
|
+
}
|
|
343
|
+
})), (batch) => target.releaseBatch(batch)));
|
|
344
|
+
}
|
|
345
|
+
return (await Promise.all(releases)).reduce((total, count) => total + count, 0);
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
async commitRun(run, serializedMetadata, publicationExpiresAt) {
|
|
349
|
+
return await stub(bindings.runCatalog, runCatalogShardName(run.runId)).upsertRun(run.runId, runKeys(run), serializedMetadata, publicationExpiresAt);
|
|
350
|
+
},
|
|
351
|
+
async listRuns(options) {
|
|
352
|
+
const suppliedLimit = options?.limit;
|
|
353
|
+
const limit = Math.min(1e3, Math.max(1, typeof suppliedLimit === "number" && Number.isFinite(suppliedLimit) ? Math.floor(suppliedLimit) : 1e3));
|
|
354
|
+
const pages = await Promise.all(allRunCatalogShardNames().map((name) => stub(bindings.runCatalog, name).list({
|
|
355
|
+
...options,
|
|
356
|
+
limit: limit + 1
|
|
357
|
+
})));
|
|
358
|
+
const encoder = new TextEncoder();
|
|
359
|
+
const candidates = pages.flatMap((shardPage) => shardPage.keys.map((entry) => ({
|
|
360
|
+
entry,
|
|
361
|
+
encodedName: encoder.encode(entry.name)
|
|
362
|
+
}))).toSorted((left, right) => {
|
|
363
|
+
const compared = compareBytes(left.encodedName, right.encodedName);
|
|
364
|
+
return options?.reverse ? -compared : compared;
|
|
365
|
+
});
|
|
366
|
+
const page = candidates.slice(0, limit).map(({ entry }) => entry);
|
|
367
|
+
const listComplete = candidates.length <= limit && pages.every((entry) => entry.list_complete);
|
|
368
|
+
return {
|
|
369
|
+
keys: page,
|
|
370
|
+
list_complete: listComplete,
|
|
371
|
+
cursor: listComplete ? void 0 : page.at(-1)?.name
|
|
372
|
+
};
|
|
373
|
+
},
|
|
374
|
+
getHookByToken(token) {
|
|
375
|
+
return stub(bindings.hookTokens, hookTokenShardName(token)).get(token);
|
|
376
|
+
},
|
|
377
|
+
getHookById(hookId) {
|
|
378
|
+
return stub(bindings.hookIds, hookIdShardName(hookId)).get(hookId);
|
|
379
|
+
},
|
|
380
|
+
async reserveHook(token, owner) {
|
|
381
|
+
const proposedClaimId = crypto.randomUUID();
|
|
382
|
+
const tokenTarget = stub(bindings.hookTokens, hookTokenShardName(token));
|
|
383
|
+
const tokenClaim = await tokenTarget.reserve(token, owner, proposedClaimId);
|
|
384
|
+
if (!tokenClaim.claimed && !sameOwner(tokenClaim.holder, owner)) return {
|
|
385
|
+
admitted: false,
|
|
386
|
+
holder: tokenClaim.holder
|
|
387
|
+
};
|
|
388
|
+
const tokenClaimId = tokenClaim.claimed ? tokenClaim.claimId : void 0;
|
|
389
|
+
const hookIdTarget = stub(bindings.hookIds, hookIdShardName(owner.hookId));
|
|
390
|
+
const sharedClaimId = tokenClaimId ?? proposedClaimId;
|
|
391
|
+
let hookIdClaim;
|
|
392
|
+
try {
|
|
393
|
+
hookIdClaim = await hookIdTarget.reserve(owner.hookId, owner, sharedClaimId);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
await Promise.allSettled([...tokenClaimId ? [tokenTarget.releaseClaim(token, owner, tokenClaimId)] : [], hookIdTarget.releaseClaim(owner.hookId, owner, sharedClaimId)]);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
if (!hookIdClaim.claimed) {
|
|
399
|
+
if (tokenClaimId) await tokenTarget.releaseClaim(token, owner, tokenClaimId);
|
|
400
|
+
return {
|
|
401
|
+
admitted: false,
|
|
402
|
+
holder: hookIdClaim.holder
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
admitted: true,
|
|
407
|
+
reservation: {
|
|
408
|
+
claimId: `${tokenClaimId ?? "-"}:${hookIdClaim.claimId}`,
|
|
409
|
+
tokenClaimId,
|
|
410
|
+
hookIdClaimId: hookIdClaim.claimId
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
async finalizeHookIndexes(token, hookId, serializedHook, owner, reservation) {
|
|
415
|
+
if (!(await stub(bindings.hookTokens, hookTokenShardName(token)).finalize(token, serializedHook, owner, reservation?.tokenClaimId)).stored) return;
|
|
416
|
+
await stub(bindings.hookIds, hookIdShardName(hookId)).publish(hookId, serializedHook, owner, reservation?.hookIdClaimId);
|
|
417
|
+
},
|
|
418
|
+
async releaseHookReservation(token, owner, reservation) {
|
|
419
|
+
const releases = [];
|
|
420
|
+
if (reservation.tokenClaimId) releases.push(stub(bindings.hookTokens, hookTokenShardName(token)).releaseClaim(token, owner, reservation.tokenClaimId));
|
|
421
|
+
if (reservation.hookIdClaimId) releases.push(stub(bindings.hookIds, hookIdShardName(owner.hookId)).releaseClaim(owner.hookId, owner, reservation.hookIdClaimId));
|
|
422
|
+
await Promise.all(releases);
|
|
423
|
+
},
|
|
424
|
+
async releaseHookIndexes(request) {
|
|
425
|
+
return { deleted: await releaseHooks(request) };
|
|
426
|
+
},
|
|
427
|
+
async expireRun(request) {
|
|
428
|
+
const [catalog, hookDeletes] = await Promise.all([stub(bindings.runCatalog, runCatalogShardName(request.runId)).expireRun(request.runId, request.keys, request.expiredAt), releaseHooks({
|
|
429
|
+
runId: request.runId,
|
|
430
|
+
hooks: request.hooks
|
|
431
|
+
})]);
|
|
432
|
+
return { deleted: catalog.deleted + hookDeletes };
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/vendor/shared/serialization.ts
|
|
438
|
+
/** Standard Date fields used across all worlds */
|
|
439
|
+
const DATE_FIELDS = /* @__PURE__ */ new Set([
|
|
440
|
+
"createdAt",
|
|
441
|
+
"updatedAt",
|
|
442
|
+
"startedAt",
|
|
443
|
+
"completedAt",
|
|
444
|
+
"retryAfter"
|
|
445
|
+
]);
|
|
446
|
+
/**
|
|
447
|
+
* JSON reviver that converts ISO date strings to Date objects.
|
|
448
|
+
* Use with JSON.parse(json, dateReviver).
|
|
449
|
+
*/
|
|
450
|
+
function dateReviver(key, value) {
|
|
451
|
+
if (DATE_FIELDS.has(key) && typeof value === "string") {
|
|
452
|
+
const date = new Date(value);
|
|
453
|
+
return Number.isNaN(date.getTime()) ? value : date;
|
|
454
|
+
}
|
|
455
|
+
return value;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Base64 helpers. Modified vs upstream: implemented over atob/btoa instead of
|
|
459
|
+
* Buffer so this module is safe inside the celld worker bundle (workerd has
|
|
460
|
+
* no Buffer global); atob/btoa are global in both workerd and Node >= 16.
|
|
461
|
+
*/
|
|
462
|
+
function b64encode(bytes) {
|
|
463
|
+
let bin = "";
|
|
464
|
+
const CHUNK = 32768;
|
|
465
|
+
for (let i = 0; i < bytes.length; i += CHUNK) bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
466
|
+
return btoa(bin);
|
|
467
|
+
}
|
|
468
|
+
function b64decode(text) {
|
|
469
|
+
const bin = atob(text);
|
|
470
|
+
const out = new Uint8Array(bin.length);
|
|
471
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
472
|
+
return out;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* JSON replacer that encodes Uint8Array as a tagged object with base64 data.
|
|
476
|
+
*/
|
|
477
|
+
function uint8ArrayReplacer(_key, value) {
|
|
478
|
+
if (value instanceof Uint8Array) return {
|
|
479
|
+
__type: "Uint8Array",
|
|
480
|
+
data: b64encode(value)
|
|
481
|
+
};
|
|
482
|
+
return value;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* JSON reviver that decodes tagged Uint8Array objects and ISO date strings.
|
|
486
|
+
* Supports both:
|
|
487
|
+
* - New format: { __type: 'Uint8Array', data: '<base64>' }
|
|
488
|
+
* - Legacy format: { __uint8array: true, data: [1, 2, 3] }
|
|
489
|
+
*/
|
|
490
|
+
function uint8ArrayReviver(key, value) {
|
|
491
|
+
if (value && typeof value === "object") {
|
|
492
|
+
const obj = value;
|
|
493
|
+
if (obj.__type === "Uint8Array" && typeof obj.data === "string") return b64decode(obj.data);
|
|
494
|
+
if (obj.__uint8array === true && Array.isArray(obj.data)) return new Uint8Array(obj.data);
|
|
495
|
+
}
|
|
496
|
+
return dateReviver(key, value);
|
|
497
|
+
}
|
|
498
|
+
/** Stringify with Uint8Array support. */
|
|
499
|
+
function stringify(value) {
|
|
500
|
+
return JSON.stringify(value, uint8ArrayReplacer);
|
|
501
|
+
}
|
|
502
|
+
/** Parse with Uint8Array and Date support. */
|
|
503
|
+
function parse(text) {
|
|
504
|
+
return JSON.parse(text, uint8ArrayReviver);
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/codec.ts
|
|
508
|
+
/**
|
|
509
|
+
* RPC wire codec for the Node client <-> worker router HTTP boundary.
|
|
510
|
+
*
|
|
511
|
+
* Extends the shared tagged-JSON format (`{__type:'Uint8Array', data}` +
|
|
512
|
+
* DATE_FIELDS revival) with explicitly tagged Dates: on Cloudflare the DO RPC
|
|
513
|
+
* boundary is structured clone, which preserves EVERY Date — including ones
|
|
514
|
+
* outside the well-known DATE_FIELDS (e.g. inside applyEvent outcomes and
|
|
515
|
+
* user payloads). The HTTP hop must preserve them too, so Dates travel as
|
|
516
|
+
* `{__type:'Date', iso}`.
|
|
517
|
+
*
|
|
518
|
+
* Runs on both sides of the wire, including inside the celld worker — so no
|
|
519
|
+
* Node built-ins (Buffer): base64 goes through atob/btoa.
|
|
520
|
+
*/
|
|
521
|
+
function rpcReplacer(key, value) {
|
|
522
|
+
const original = this && typeof this === "object" ? this[key] : void 0;
|
|
523
|
+
if (original instanceof Date) return {
|
|
524
|
+
__type: "Date",
|
|
525
|
+
iso: original.toISOString()
|
|
526
|
+
};
|
|
527
|
+
if (value instanceof Uint8Array) return {
|
|
528
|
+
__type: "Uint8Array",
|
|
529
|
+
data: b64encode(value)
|
|
530
|
+
};
|
|
531
|
+
return value;
|
|
532
|
+
}
|
|
533
|
+
function rpcReviver(key, value) {
|
|
534
|
+
if (value && typeof value === "object") {
|
|
535
|
+
const obj = value;
|
|
536
|
+
if (obj.__type === "Date" && typeof obj.iso === "string") return new Date(obj.iso);
|
|
537
|
+
if (obj.__type === "Uint8Array" && typeof obj.data === "string") return b64decode(obj.data);
|
|
538
|
+
}
|
|
539
|
+
return dateReviver(key, value);
|
|
540
|
+
}
|
|
541
|
+
function rpcStringify(value) {
|
|
542
|
+
return JSON.stringify(value, rpcReplacer);
|
|
543
|
+
}
|
|
544
|
+
function rpcParse(text) {
|
|
545
|
+
return JSON.parse(text, rpcReviver);
|
|
546
|
+
}
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/vendor/shared/util.ts
|
|
549
|
+
function compact(obj) {
|
|
550
|
+
const value = {};
|
|
551
|
+
for (const key in obj) if (obj[key] !== null) value[key] = obj[key];
|
|
552
|
+
else value[key] = void 0;
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
export { validateStreamReadRequest as A, decodeStreamReadResult as C, encodeStreamWriteBatch as D, encodeStreamReadResult as E, encodeStreamWriteResult as O, STREAM_BATCH_CONTENT_TYPE as S, decodeStreamWriteResult as T, MAX_STREAM_ERROR_BYTES as _, stringify as a, MAX_STREAM_READ_CHUNKS as b, HOOK_CLAIM_LEASE_MS as c, MAX_QUEUE_DELIVERY_TIMEOUT_MS as d, MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS as f, MAX_STREAM_CHUNK_BYTES as g, MAX_STREAM_BATCH_BYTES as h, parse as i, validateStreamWriteChunks as j, normalizeStreamError as k, LIFECYCLE_COMPACTION_RETRY_MS as l, QUEUE_INFLIGHT_GRACE_MS as m, rpcParse as n, createWorkflowIndex as o, QUEUE_FENCE_GRACE_MS as p, rpcStringify as r, CATALOG_FENCE_GRACE_MS as s, compact as t, MAX_FLEET_RPC_TIMEOUT_MS as u, MAX_STREAM_LONG_POLL_MS as v, decodeStreamWriteBatch as w, MAX_STREAM_WRITE_CHUNKS as x, MAX_STREAM_READ_BYTES as y };
|
|
557
|
+
|
|
558
|
+
//# sourceMappingURL=util-ya3V_FTA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util-ya3V_FTA.js","names":[],"sources":["../src/stream-protocol.ts","../src/lifecycle.ts","../src/indexes.ts","../src/vendor/shared/serialization.ts","../src/codec.ts","../src/vendor/shared/util.ts"],"sourcesContent":["/**\n * Hard-cutover stream protocol shared by the client, Worker router, and\n * StreamDO. Chunk payloads use a compact binary envelope on the public HTTP\n * boundary and Uint8Array values on the DO/storage boundary.\n */\n\nexport const STREAM_BATCH_CONTENT_TYPE = 'application/vnd.world-celld.stream-batch.v1';\n\n/** Metadata plus payload and size-index entries remain below the 128-key platform limit. */\nexport const MAX_STREAM_WRITE_CHUNKS = 32;\n/** A chunk remains below the 2 MiB SQLite-backed DO key/value limit. */\nexport const MAX_STREAM_CHUNK_BYTES = 1024 * 1024;\n/** Keeps serialized RPC bodies well below the 32 MiB Workers RPC limit. */\nexport const MAX_STREAM_BATCH_BYTES = 8 * 1024 * 1024;\nexport const MAX_STREAM_READ_CHUNKS = 32;\nexport const MAX_STREAM_READ_BYTES = 8 * 1024 * 1024;\nexport const MAX_STREAM_ERROR_BYTES = 16 * 1024;\n/** Must remain below the default 30 second fleet request deadline. */\nexport const MAX_STREAM_LONG_POLL_MS = 20_000;\n\nexport type StreamTerminalState = 'open' | 'closed' | 'errored' | 'expired';\n\nexport interface StreamErrorData {\n name: string;\n message: string;\n}\n\nexport interface StreamWriteResult {\n startIndex: number;\n count: number;\n tailIndex: number;\n}\n\nexport interface StreamReadRequest {\n runId: string;\n startIndex: number;\n maxChunks: number;\n maxBytes: number;\n waitMs: number;\n}\n\nexport interface StreamReadResult {\n startIndex: number;\n tailIndex: number;\n chunks: Uint8Array[];\n state: StreamTerminalState;\n timedOut: boolean;\n error?: StreamErrorData;\n}\n\nconst WRITE_MAGIC = 0x57435357; // WCSW\nconst WRITE_ACK_MAGIC = 0x57435341; // WCSA\nconst READ_MAGIC = 0x57435352; // WCSR\nconst PROTOCOL_VERSION = 1;\nconst WRITE_HEADER_BYTES = 8;\nconst WRITE_ACK_BYTES = 16;\nconst READ_HEADER_BYTES = 28;\n\nconst STATE_TO_WIRE: Record<StreamTerminalState, number> = {\n open: 0,\n closed: 1,\n errored: 2,\n expired: 3,\n};\n\nconst WIRE_TO_STATE: readonly StreamTerminalState[] = ['open', 'closed', 'errored', 'expired'];\n\nfunction protocolError(message: string): Error {\n const error = new Error(`world-celld: invalid stream protocol: ${message}`);\n error.name = 'StreamProtocolError';\n return error;\n}\n\nfunction requireSafeIndex(value: number, field: string): void {\n if (!Number.isSafeInteger(value) || value < -1 || value > 0x7fffffff) {\n throw protocolError(`${field} is out of range`);\n }\n}\n\nexport function validateStreamWriteChunks(chunks: readonly Uint8Array[]): number {\n if (chunks.length === 0) throw protocolError('batch must contain at least one chunk');\n if (chunks.length > MAX_STREAM_WRITE_CHUNKS) {\n throw protocolError(`batch exceeds ${MAX_STREAM_WRITE_CHUNKS} chunks`);\n }\n\n let totalBytes = 0;\n for (const chunk of chunks) {\n if (!(chunk instanceof Uint8Array)) throw protocolError('chunk is not binary data');\n if (chunk.byteLength > MAX_STREAM_CHUNK_BYTES) {\n throw protocolError(`chunk exceeds ${MAX_STREAM_CHUNK_BYTES} bytes`);\n }\n totalBytes += chunk.byteLength;\n if (totalBytes > MAX_STREAM_BATCH_BYTES) {\n throw protocolError(`batch exceeds ${MAX_STREAM_BATCH_BYTES} bytes`);\n }\n }\n return totalBytes;\n}\n\nexport function validateStreamReadRequest(request: StreamReadRequest): void {\n if (!request.runId) throw protocolError('runId is required');\n if (!Number.isSafeInteger(request.startIndex) || request.startIndex < 0) {\n throw protocolError('startIndex must be a non-negative safe integer');\n }\n if (\n !Number.isSafeInteger(request.maxChunks) ||\n request.maxChunks < 0 ||\n request.maxChunks > MAX_STREAM_READ_CHUNKS\n ) {\n throw protocolError(`maxChunks must be between 0 and ${MAX_STREAM_READ_CHUNKS}`);\n }\n if (\n !Number.isSafeInteger(request.maxBytes) ||\n request.maxBytes < MAX_STREAM_CHUNK_BYTES ||\n request.maxBytes > MAX_STREAM_READ_BYTES\n ) {\n throw protocolError(\n `maxBytes must be between ${MAX_STREAM_CHUNK_BYTES} and ${MAX_STREAM_READ_BYTES}`,\n );\n }\n if (\n !Number.isSafeInteger(request.waitMs) ||\n request.waitMs < 0 ||\n request.waitMs > MAX_STREAM_LONG_POLL_MS\n ) {\n throw protocolError(`waitMs must be between 0 and ${MAX_STREAM_LONG_POLL_MS}`);\n }\n}\n\nexport function encodeStreamWriteBatch(chunks: readonly Uint8Array[]): Uint8Array {\n const totalBytes = validateStreamWriteChunks(chunks);\n const encoded = new Uint8Array(WRITE_HEADER_BYTES + chunks.length * 4 + totalBytes);\n const view = new DataView(encoded.buffer);\n view.setUint32(0, WRITE_MAGIC);\n view.setUint16(4, PROTOCOL_VERSION);\n view.setUint16(6, chunks.length);\n\n let offset = WRITE_HEADER_BYTES;\n for (const chunk of chunks) {\n view.setUint32(offset, chunk.byteLength);\n offset += 4;\n encoded.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return encoded;\n}\n\nexport function decodeStreamWriteBatch(encoded: Uint8Array): Uint8Array[] {\n if (encoded.byteLength < WRITE_HEADER_BYTES) throw protocolError('truncated write header');\n const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n if (view.getUint32(0) !== WRITE_MAGIC) throw protocolError('bad write magic');\n if (view.getUint16(4) !== PROTOCOL_VERSION) throw protocolError('unsupported write version');\n const count = view.getUint16(6);\n const chunks: Uint8Array[] = [];\n let offset = WRITE_HEADER_BYTES;\n for (let index = 0; index < count; index++) {\n if (offset + 4 > encoded.byteLength) throw protocolError('truncated chunk length');\n const length = view.getUint32(offset);\n offset += 4;\n if (offset + length > encoded.byteLength) throw protocolError('truncated chunk data');\n // Persisted DO values are structured-cloned. A view would retain the\n // complete batch backing buffer and could exceed the per-value storage\n // limit even when this individual chunk is valid.\n chunks.push(new Uint8Array(encoded.subarray(offset, offset + length)));\n offset += length;\n }\n if (offset !== encoded.byteLength) throw protocolError('trailing write data');\n validateStreamWriteChunks(chunks);\n return chunks;\n}\n\nexport function encodeStreamWriteResult(result: StreamWriteResult): Uint8Array {\n requireSafeIndex(result.startIndex, 'startIndex');\n requireSafeIndex(result.tailIndex, 'tailIndex');\n if (\n !Number.isSafeInteger(result.count) ||\n result.count < 1 ||\n result.count > MAX_STREAM_WRITE_CHUNKS\n ) {\n throw protocolError('write count is out of range');\n }\n const encoded = new Uint8Array(WRITE_ACK_BYTES);\n const view = new DataView(encoded.buffer);\n view.setUint32(0, WRITE_ACK_MAGIC);\n view.setUint16(4, PROTOCOL_VERSION);\n view.setUint16(6, result.count);\n view.setInt32(8, result.startIndex);\n view.setInt32(12, result.tailIndex);\n return encoded;\n}\n\nexport function decodeStreamWriteResult(encoded: Uint8Array): StreamWriteResult {\n if (encoded.byteLength !== WRITE_ACK_BYTES) throw protocolError('invalid write response size');\n const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n if (view.getUint32(0) !== WRITE_ACK_MAGIC) throw protocolError('bad write response magic');\n if (view.getUint16(4) !== PROTOCOL_VERSION) {\n throw protocolError('unsupported write response version');\n }\n const count = view.getUint16(6);\n if (count < 1 || count > MAX_STREAM_WRITE_CHUNKS) {\n throw protocolError('write response count is out of range');\n }\n const result = {\n count,\n startIndex: view.getInt32(8),\n tailIndex: view.getInt32(12),\n };\n requireSafeIndex(result.startIndex, 'startIndex');\n requireSafeIndex(result.tailIndex, 'tailIndex');\n if (result.tailIndex !== result.startIndex + result.count - 1) {\n throw protocolError('write response offsets are inconsistent');\n }\n return result;\n}\n\nexport function encodeStreamReadResult(result: StreamReadResult): Uint8Array {\n requireSafeIndex(result.startIndex, 'startIndex');\n requireSafeIndex(result.tailIndex, 'tailIndex');\n let chunkBytes = 0;\n for (const chunk of result.chunks) chunkBytes += 4 + chunk.byteLength;\n if (result.chunks.length > MAX_STREAM_READ_CHUNKS || chunkBytes > MAX_STREAM_READ_BYTES + 128) {\n throw protocolError('read result exceeds configured bounds');\n }\n\n const encoder = new TextEncoder();\n const errorName = result.error ? encoder.encode(result.error.name) : new Uint8Array();\n const errorMessage = result.error ? encoder.encode(result.error.message) : new Uint8Array();\n if (errorName.byteLength + errorMessage.byteLength > MAX_STREAM_ERROR_BYTES) {\n throw protocolError(`stream error exceeds ${MAX_STREAM_ERROR_BYTES} bytes`);\n }\n const encoded = new Uint8Array(\n READ_HEADER_BYTES + chunkBytes + errorName.byteLength + errorMessage.byteLength,\n );\n const view = new DataView(encoded.buffer);\n view.setUint32(0, READ_MAGIC);\n view.setUint8(4, PROTOCOL_VERSION);\n const state = STATE_TO_WIRE[result.state];\n if (state === undefined) throw protocolError('unknown stream state');\n view.setUint8(5, state);\n view.setUint8(6, result.timedOut ? 1 : 0);\n view.setUint8(7, 0);\n view.setInt32(8, result.startIndex);\n view.setInt32(12, result.tailIndex);\n view.setUint32(16, result.chunks.length);\n view.setUint32(20, errorName.byteLength);\n view.setUint32(24, errorMessage.byteLength);\n\n let offset = READ_HEADER_BYTES;\n for (const chunk of result.chunks) {\n view.setUint32(offset, chunk.byteLength);\n offset += 4;\n encoded.set(chunk, offset);\n offset += chunk.byteLength;\n }\n encoded.set(errorName, offset);\n offset += errorName.byteLength;\n encoded.set(errorMessage, offset);\n return encoded;\n}\n\nexport function decodeStreamReadResult(encoded: Uint8Array): StreamReadResult {\n if (encoded.byteLength < READ_HEADER_BYTES) throw protocolError('truncated read header');\n const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n if (view.getUint32(0) !== READ_MAGIC) throw protocolError('bad read magic');\n if (view.getUint8(4) !== PROTOCOL_VERSION) throw protocolError('unsupported read version');\n const state = WIRE_TO_STATE[view.getUint8(5)];\n if (!state) throw protocolError('unknown stream state');\n const startIndex = view.getInt32(8);\n const tailIndex = view.getInt32(12);\n requireSafeIndex(startIndex, 'startIndex');\n requireSafeIndex(tailIndex, 'tailIndex');\n const count = view.getUint32(16);\n const errorNameLength = view.getUint32(20);\n const errorMessageLength = view.getUint32(24);\n if (count > MAX_STREAM_READ_CHUNKS) throw protocolError('read chunk count exceeds limit');\n if (errorNameLength + errorMessageLength > MAX_STREAM_ERROR_BYTES) {\n throw protocolError('read error exceeds limit');\n }\n\n const chunks: Uint8Array[] = [];\n let offset = READ_HEADER_BYTES;\n let totalBytes = 0;\n for (let index = 0; index < count; index++) {\n if (offset + 4 > encoded.byteLength) throw protocolError('truncated read chunk length');\n const length = view.getUint32(offset);\n offset += 4;\n if (offset + length > encoded.byteLength) throw protocolError('truncated read chunk data');\n totalBytes += length;\n if (totalBytes > MAX_STREAM_READ_BYTES) throw protocolError('read bytes exceed limit');\n chunks.push(encoded.subarray(offset, offset + length));\n offset += length;\n }\n\n if (offset + errorNameLength + errorMessageLength !== encoded.byteLength) {\n throw protocolError('invalid read error payload');\n }\n const decoder = new TextDecoder();\n const error =\n errorNameLength > 0 || errorMessageLength > 0\n ? {\n name: decoder.decode(encoded.subarray(offset, offset + errorNameLength)),\n message: decoder.decode(encoded.subarray(offset + errorNameLength)),\n }\n : undefined;\n\n return {\n startIndex,\n tailIndex,\n chunks,\n state,\n timedOut: (view.getUint8(6) & 1) !== 0,\n error,\n };\n}\n\nexport function normalizeStreamError(error: unknown): StreamErrorData {\n let normalized: StreamErrorData;\n if (error instanceof Error) {\n normalized = { name: error.name || 'Error', message: error.message };\n } else if (typeof error === 'string') {\n normalized = { name: 'Error', message: error };\n } else if (\n error &&\n typeof error === 'object' &&\n typeof (error as { name?: unknown }).name === 'string' &&\n typeof (error as { message?: unknown }).message === 'string'\n ) {\n normalized = {\n name: (error as StreamErrorData).name,\n message: (error as StreamErrorData).message,\n };\n } else {\n normalized = { name: 'Error', message: String(error) };\n }\n\n // Bound persisted terminal metadata as well as its binary response frame.\n normalized.name = normalized.name.slice(0, 1024);\n normalized.message = normalized.message.slice(0, 4096);\n while (\n new TextEncoder().encode(normalized.name).byteLength +\n new TextEncoder().encode(normalized.message).byteLength >\n MAX_STREAM_ERROR_BYTES\n ) {\n normalized.message = normalized.message.slice(0, Math.max(0, normalized.message.length - 256));\n if (normalized.message.length === 0) {\n normalized.name = normalized.name.slice(0, Math.max(0, normalized.name.length - 256));\n }\n }\n return normalized;\n}\n","/**\n * Hard protocol bounds used by lifecycle leases and derivative-fence cleanup.\n *\n * These are not retention TTLs. They bound how long a request which was\n * admitted before expiry may still mutate a derivative Durable Object:\n *\n * - fleet calls have at most three 5-minute attempts plus bounded retry delay;\n * - one queue delivery owns its lease for at most 5 minutes plus 30 seconds;\n * - hook admission spans reserve retries, one authoritative apply, and\n * finalize retries before the exact claim must be reacquired.\n */\nexport const MAX_FLEET_RPC_TIMEOUT_MS = 5 * 60 * 1000;\nexport const FLEET_IDEMPOTENT_ATTEMPTS = 3;\nexport const FLEET_RETRY_BACKOFF_MAX_MS = 900;\n\nexport const MAX_IDEMPOTENT_RPC_LIFETIME_MS =\n FLEET_IDEMPOTENT_ATTEMPTS * MAX_FLEET_RPC_TIMEOUT_MS + FLEET_RETRY_BACKOFF_MAX_MS;\nexport const MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS =\n MAX_FLEET_RPC_TIMEOUT_MS + MAX_IDEMPOTENT_RPC_LIFETIME_MS;\n\nexport const MAX_QUEUE_DELIVERY_TIMEOUT_MS = 5 * 60 * 1000;\nexport const QUEUE_INFLIGHT_GRACE_MS = 30 * 1000;\nexport const MAX_QUEUE_INFLIGHT_LEASE_MS = MAX_QUEUE_DELIVERY_TIMEOUT_MS + QUEUE_INFLIGHT_GRACE_MS;\n\nexport const CATALOG_FENCE_GRACE_MS = MAX_RUN_INDEX_PUBLICATION_LIFETIME_MS;\nexport const QUEUE_FENCE_GRACE_MS = Math.max(MAX_FLEET_RPC_TIMEOUT_MS, MAX_QUEUE_INFLIGHT_LEASE_MS);\n\nexport const HOOK_CLAIM_LEASE_MS =\n (FLEET_IDEMPOTENT_ATTEMPTS * 2 + 1) * MAX_FLEET_RPC_TIMEOUT_MS + FLEET_RETRY_BACKOFF_MAX_MS * 2;\n\nexport const LIFECYCLE_COMPACTION_BATCH = 128;\nexport const LIFECYCLE_COMPACTION_RETRY_MS = 1000;\n","import type { WorkflowRun } from '@workflow/world';\nimport type { HookTokenOwner } from './config.js';\nimport type {\n ExpireRunIndexesRequest,\n ExpireRunIndexesResult,\n ReleaseHookIndexesRequest,\n ReleaseHookIndexesResult,\n} from './retention.js';\n\nexport const RUN_CATALOG_SHARDS = 16;\nexport const HOOK_TOKEN_SHARDS = 32;\nexport const HOOK_ID_SHARDS = 32;\n\nconst INDEX_PROTOCOL_VERSION = 'v1';\nconst HOOK_RELEASE_BATCH = 64;\n\nexport interface IndexListOptions {\n prefix?: string;\n cursor?: string;\n limit?: number;\n reverse?: boolean;\n}\n\nexport interface IndexListPage {\n keys: Array<{ name: string; value: string }>;\n list_complete: boolean;\n cursor?: string;\n}\n\nexport interface RunCatalogShardStub {\n upsertRun(\n runId: string,\n keys: string[],\n serializedMetadata: string,\n publicationExpiresAt: number,\n ): Promise<{ stored: boolean }>;\n list(options?: IndexListOptions): Promise<IndexListPage>;\n expireRun(runId: string, keys: string[], expiredAt: number): Promise<ExpireRunIndexesResult>;\n}\n\nexport type HookClaimResult =\n | { claimed: true; claimId: string }\n | { claimed: false; holder: HookTokenOwner };\n\nexport interface HookReservation {\n claimId: string;\n tokenClaimId?: string;\n hookIdClaimId?: string;\n}\n\nexport type HookReservationResult =\n | { admitted: true; reservation: HookReservation }\n | { admitted: false; holder: HookTokenOwner };\n\nexport interface HookTokenShardStub {\n get(token: string): Promise<string | null>;\n reserve(token: string, owner: HookTokenOwner, proposedClaimId: string): Promise<HookClaimResult>;\n finalize(\n token: string,\n serializedHook: string,\n owner: HookTokenOwner,\n claimId?: string,\n ): Promise<{ stored: boolean }>;\n releaseClaim(token: string, owner: HookTokenOwner, claimId: string): Promise<void>;\n releaseBatch(\n hooks: Array<{ token: string; owner: HookTokenOwner }>,\n ): Promise<{ deleted: number }>;\n}\n\nexport interface HookIdShardStub {\n get(hookId: string): Promise<string | null>;\n reserve(hookId: string, owner: HookTokenOwner, proposedClaimId: string): Promise<HookClaimResult>;\n publish(\n hookId: string,\n serializedHook: string,\n owner: HookTokenOwner,\n claimId?: string,\n ): Promise<{ stored: boolean }>;\n releaseClaim(hookId: string, owner: HookTokenOwner, claimId: string): Promise<void>;\n releaseBatch(\n hooks: Array<{ hookId: string; owner: HookTokenOwner }>,\n ): Promise<{ deleted: number }>;\n}\n\ninterface CellIdLike {\n toString(): string;\n}\n\nexport interface CellNamespaceLike<T> {\n idFromName(name: string): CellIdLike;\n get(id: CellIdLike): T;\n}\n\nexport interface WorkflowIndexBindings {\n runCatalog: CellNamespaceLike<RunCatalogShardStub>;\n hookTokens: CellNamespaceLike<HookTokenShardStub>;\n hookIds: CellNamespaceLike<HookIdShardStub>;\n}\n\n/** High-level client used by the World storage layer and retention coordinator. */\nexport interface WorkflowIndex {\n commitRun(\n run: WorkflowRun,\n serializedMetadata: string,\n publicationExpiresAt: number,\n ): Promise<{ stored: boolean }>;\n listRuns(options?: IndexListOptions): Promise<IndexListPage>;\n getHookByToken(token: string): Promise<string | null>;\n getHookById(hookId: string): Promise<string | null>;\n reserveHook(token: string, owner: HookTokenOwner): Promise<HookReservationResult>;\n finalizeHookIndexes(\n token: string,\n hookId: string,\n serializedHook: string,\n owner: HookTokenOwner,\n reservation?: HookReservation,\n ): Promise<void>;\n releaseHookReservation(\n token: string,\n owner: HookTokenOwner,\n reservation: HookReservation,\n ): Promise<void>;\n releaseHookIndexes(request: ReleaseHookIndexesRequest): Promise<ReleaseHookIndexesResult>;\n expireRun(request: ExpireRunIndexesRequest): Promise<ExpireRunIndexesResult>;\n}\n\n/** Stable FNV-1a routing hash. Collisions share a shard but never a storage key. */\nexport function stableIndexHash(value: string): number {\n let hash = 0x811c9dc5;\n for (const byte of new TextEncoder().encode(value)) {\n hash ^= byte;\n hash = Math.imul(hash, 0x01000193);\n }\n return hash >>> 0;\n}\n\nfunction shardName(domain: string, value: string, cardinality: number): string {\n const width = Math.max(2, (cardinality - 1).toString(16).length);\n const shard = stableIndexHash(value) % cardinality;\n return `${domain}:${INDEX_PROTOCOL_VERSION}:${shard.toString(16).padStart(width, '0')}`;\n}\n\nexport function runCatalogShardName(runId: string): string {\n return shardName('run-catalog', runId, RUN_CATALOG_SHARDS);\n}\n\nexport function hookTokenShardName(token: string): string {\n return shardName('hook-token', token, HOOK_TOKEN_SHARDS);\n}\n\nexport function hookIdShardName(hookId: string): string {\n return shardName('hook-id', hookId, HOOK_ID_SHARDS);\n}\n\nexport function allRunCatalogShardNames(): string[] {\n return Array.from(\n { length: RUN_CATALOG_SHARDS },\n (_, shard) => `run-catalog:${INDEX_PROTOCOL_VERSION}:${shard.toString(16).padStart(2, '0')}`,\n );\n}\n\nfunction stub<T>(namespace: CellNamespaceLike<T>, name: string): T {\n return namespace.get(namespace.idFromName(name));\n}\n\nfunction runKeys(run: Pick<WorkflowRun, 'runId' | 'workflowName' | 'createdAt'>): string[] {\n const timestamp = run.createdAt.getTime().toString().padStart(13, '0');\n return [`run:${run.workflowName}:${timestamp}:${run.runId}`, `runall:${timestamp}:${run.runId}`];\n}\n\nfunction groupByShard<T>(values: T[], route: (value: T) => string): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n for (const value of values) {\n const name = route(value);\n const group = groups.get(name);\n if (group) group.push(value);\n else groups.set(name, [value]);\n }\n return groups;\n}\n\nfunction sameOwner(left: HookTokenOwner, right: HookTokenOwner): boolean {\n return left.runId === right.runId && left.hookId === right.hookId;\n}\n\nfunction compareBytes(left: Uint8Array, right: Uint8Array): number {\n const length = Math.min(left.byteLength, right.byteLength);\n for (let index = 0; index < length; index++) {\n const difference = left[index] - right[index];\n if (difference !== 0) return difference;\n }\n return left.byteLength - right.byteLength;\n}\n\nasync function inBatches<T>(\n values: T[],\n operation: (batch: T[]) => Promise<{ deleted: number }>,\n): Promise<number> {\n let deleted = 0;\n for (let offset = 0; offset < values.length; offset += HOOK_RELEASE_BATCH) {\n deleted += (await operation(values.slice(offset, offset + HOOK_RELEASE_BATCH))).deleted;\n }\n return deleted;\n}\n\nexport function createWorkflowIndex(bindings: WorkflowIndexBindings): WorkflowIndex {\n const releaseHooks = async (request: ReleaseHookIndexesRequest): Promise<number> => {\n const tokenGroups = groupByShard(request.hooks, (hook) => hookTokenShardName(hook.token));\n const idGroups = groupByShard(request.hooks, (hook) => hookIdShardName(hook.hookId));\n const releases: Array<Promise<number>> = [];\n for (const [name, hooks] of tokenGroups) {\n const target = stub(bindings.hookTokens, name);\n releases.push(\n inBatches(\n hooks.map((hook) => ({\n token: hook.token,\n owner: { runId: request.runId, hookId: hook.hookId },\n })),\n (batch) => target.releaseBatch(batch),\n ),\n );\n }\n for (const [name, hooks] of idGroups) {\n const target = stub(bindings.hookIds, name);\n releases.push(\n inBatches(\n hooks.map((hook) => ({\n hookId: hook.hookId,\n owner: { runId: request.runId, hookId: hook.hookId },\n })),\n (batch) => target.releaseBatch(batch),\n ),\n );\n }\n return (await Promise.all(releases)).reduce((total, count) => total + count, 0);\n };\n\n return {\n async commitRun(run, serializedMetadata, publicationExpiresAt) {\n return await stub(bindings.runCatalog, runCatalogShardName(run.runId)).upsertRun(\n run.runId,\n runKeys(run),\n serializedMetadata,\n publicationExpiresAt,\n );\n },\n\n async listRuns(options) {\n const suppliedLimit = options?.limit;\n const requestedLimit =\n typeof suppliedLimit === 'number' && Number.isFinite(suppliedLimit)\n ? Math.floor(suppliedLimit)\n : 1000;\n const limit = Math.min(1000, Math.max(1, requestedLimit));\n const pages = await Promise.all(\n allRunCatalogShardNames().map((name) =>\n stub(bindings.runCatalog, name).list({ ...options, limit: limit + 1 }),\n ),\n );\n const encoder = new TextEncoder();\n const candidates = pages\n .flatMap((shardPage) =>\n shardPage.keys.map((entry) => ({ entry, encodedName: encoder.encode(entry.name) })),\n )\n .toSorted((left, right) => {\n const compared = compareBytes(left.encodedName, right.encodedName);\n return options?.reverse ? -compared : compared;\n });\n const page = candidates.slice(0, limit).map(({ entry }) => entry);\n const listComplete =\n candidates.length <= limit && pages.every((entry) => entry.list_complete);\n return {\n keys: page,\n list_complete: listComplete,\n cursor: listComplete ? undefined : page.at(-1)?.name,\n };\n },\n\n getHookByToken(token) {\n return stub(bindings.hookTokens, hookTokenShardName(token)).get(token);\n },\n\n getHookById(hookId) {\n return stub(bindings.hookIds, hookIdShardName(hookId)).get(hookId);\n },\n\n async reserveHook(token, owner) {\n const proposedClaimId = crypto.randomUUID();\n const tokenTarget = stub(bindings.hookTokens, hookTokenShardName(token));\n const tokenClaim = await tokenTarget.reserve(token, owner, proposedClaimId);\n if (!tokenClaim.claimed && !sameOwner(tokenClaim.holder, owner)) {\n return { admitted: false, holder: tokenClaim.holder };\n }\n\n const tokenClaimId = tokenClaim.claimed ? tokenClaim.claimId : undefined;\n const hookIdTarget = stub(bindings.hookIds, hookIdShardName(owner.hookId));\n const sharedClaimId = tokenClaimId ?? proposedClaimId;\n let hookIdClaim: HookClaimResult;\n try {\n hookIdClaim = await hookIdTarget.reserve(owner.hookId, owner, sharedClaimId);\n } catch (error) {\n await Promise.allSettled([\n ...(tokenClaimId ? [tokenTarget.releaseClaim(token, owner, tokenClaimId)] : []),\n hookIdTarget.releaseClaim(owner.hookId, owner, sharedClaimId),\n ]);\n throw error;\n }\n if (!hookIdClaim.claimed) {\n if (tokenClaimId) await tokenTarget.releaseClaim(token, owner, tokenClaimId);\n return { admitted: false, holder: hookIdClaim.holder };\n }\n\n const reservation: HookReservation = {\n claimId: `${tokenClaimId ?? '-'}:${hookIdClaim.claimId}`,\n tokenClaimId,\n hookIdClaimId: hookIdClaim.claimId,\n };\n return { admitted: true, reservation };\n },\n\n async finalizeHookIndexes(token, hookId, serializedHook, owner, reservation) {\n const tokenResult = await stub(bindings.hookTokens, hookTokenShardName(token)).finalize(\n token,\n serializedHook,\n owner,\n reservation?.tokenClaimId,\n );\n if (!tokenResult.stored) return;\n await stub(bindings.hookIds, hookIdShardName(hookId)).publish(\n hookId,\n serializedHook,\n owner,\n reservation?.hookIdClaimId,\n );\n },\n\n async releaseHookReservation(token, owner, reservation) {\n const releases: Array<Promise<void>> = [];\n if (reservation.tokenClaimId) {\n releases.push(\n stub(bindings.hookTokens, hookTokenShardName(token)).releaseClaim(\n token,\n owner,\n reservation.tokenClaimId,\n ),\n );\n }\n if (reservation.hookIdClaimId) {\n releases.push(\n stub(bindings.hookIds, hookIdShardName(owner.hookId)).releaseClaim(\n owner.hookId,\n owner,\n reservation.hookIdClaimId,\n ),\n );\n }\n await Promise.all(releases);\n },\n\n async releaseHookIndexes(request) {\n return { deleted: await releaseHooks(request) };\n },\n\n async expireRun(request) {\n const [catalog, hookDeletes] = await Promise.all([\n stub(bindings.runCatalog, runCatalogShardName(request.runId)).expireRun(\n request.runId,\n request.keys,\n request.expiredAt,\n ),\n releaseHooks({ runId: request.runId, hooks: request.hooks }),\n ]);\n return { deleted: catalog.deleted + hookDeletes };\n },\n };\n}\n","/** Standard Date fields used across all worlds */\nexport const DATE_FIELDS = new Set([\n 'createdAt',\n 'updatedAt',\n 'startedAt',\n 'completedAt',\n 'retryAfter',\n] as const);\n\n/**\n * JSON reviver that converts ISO date strings to Date objects.\n * Use with JSON.parse(json, dateReviver).\n */\nexport function dateReviver(key: string, value: unknown): unknown {\n if (\n DATE_FIELDS.has(key as typeof DATE_FIELDS extends Set<infer T> ? T : never) &&\n typeof value === 'string'\n ) {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? value : date;\n }\n return value;\n}\n\n/**\n * Base64 helpers. Modified vs upstream: implemented over atob/btoa instead of\n * Buffer so this module is safe inside the celld worker bundle (workerd has\n * no Buffer global); atob/btoa are global in both workerd and Node >= 16.\n */\nexport function b64encode(bytes: Uint8Array): string {\n let bin = '';\n const CHUNK = 0x8000;\n for (let i = 0; i < bytes.length; i += CHUNK) {\n bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(bin);\n}\n\nexport function b64decode(text: string): Uint8Array {\n const bin = atob(text);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) {\n out[i] = bin.charCodeAt(i);\n }\n return out;\n}\n\n/**\n * JSON replacer that encodes Uint8Array as a tagged object with base64 data.\n */\nexport function uint8ArrayReplacer(_key: string, value: unknown): unknown {\n if (value instanceof Uint8Array) {\n return {\n __type: 'Uint8Array',\n data: b64encode(value),\n };\n }\n return value;\n}\n\n/**\n * JSON reviver that decodes tagged Uint8Array objects and ISO date strings.\n * Supports both:\n * - New format: { __type: 'Uint8Array', data: '<base64>' }\n * - Legacy format: { __uint8array: true, data: [1, 2, 3] }\n */\nexport function uint8ArrayReviver(key: string, value: unknown): unknown {\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n // New format: base64-encoded\n if (obj.__type === 'Uint8Array' && typeof obj.data === 'string') {\n return b64decode(obj.data);\n }\n // Legacy format: number array (backwards compat with NATS JetStream data)\n if (obj.__uint8array === true && Array.isArray(obj.data)) {\n return new Uint8Array(obj.data as number[]);\n }\n }\n return dateReviver(key, value);\n}\n\n/** Stringify with Uint8Array support. */\nexport function stringify(value: unknown): string {\n return JSON.stringify(value, uint8ArrayReplacer);\n}\n\n/** Parse with Uint8Array and Date support. */\nexport function parse<T>(text: string): T {\n return JSON.parse(text, uint8ArrayReviver) as T;\n}\n\n/** Deep clone using structuredClone */\nexport function deepClone<T>(value: T): T {\n return structuredClone(value);\n}\n","/**\n * RPC wire codec for the Node client <-> worker router HTTP boundary.\n *\n * Extends the shared tagged-JSON format (`{__type:'Uint8Array', data}` +\n * DATE_FIELDS revival) with explicitly tagged Dates: on Cloudflare the DO RPC\n * boundary is structured clone, which preserves EVERY Date — including ones\n * outside the well-known DATE_FIELDS (e.g. inside applyEvent outcomes and\n * user payloads). The HTTP hop must preserve them too, so Dates travel as\n * `{__type:'Date', iso}`.\n *\n * Runs on both sides of the wire, including inside the celld worker — so no\n * Node built-ins (Buffer): base64 goes through atob/btoa.\n */\nimport { b64decode, b64encode, dateReviver } from './vendor/shared/serialization.js';\n\nfunction rpcReplacer(this: unknown, key: string, value: unknown): unknown {\n // JSON.stringify invokes Date.toJSON before the replacer sees the value;\n // recover the original from the holder object.\n const original =\n this && typeof this === 'object' ? (this as Record<string, unknown>)[key] : undefined;\n if (original instanceof Date) {\n return { __type: 'Date', iso: original.toISOString() };\n }\n if (value instanceof Uint8Array) {\n return { __type: 'Uint8Array', data: b64encode(value) };\n }\n return value;\n}\n\nfunction rpcReviver(key: string, value: unknown): unknown {\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n if (obj.__type === 'Date' && typeof obj.iso === 'string') {\n return new Date(obj.iso);\n }\n if (obj.__type === 'Uint8Array' && typeof obj.data === 'string') {\n return b64decode(obj.data);\n }\n }\n // Fallback shared-codec semantics for untagged ISO strings in DATE_FIELDS.\n return dateReviver(key, value);\n}\n\nexport function rpcStringify(value: unknown): string {\n return JSON.stringify(value, rpcReplacer);\n}\n\nexport function rpcParse<T>(text: string): T {\n return JSON.parse(text, rpcReviver) as T;\n}\n","export class Mutex {\n promise: Promise<unknown> = Promise.resolve();\n andThen<T>(fn: () => Promise<T> | T): Promise<T> {\n this.promise = this.promise.then(\n () => fn(),\n () => fn(),\n );\n return this.promise as Promise<T>;\n }\n}\n\nexport function compact<T extends object>(obj: T) {\n const value = {} as {\n [key in keyof T]: null extends T[key] ? undefined | NonNullable<T[key]> : T[key];\n };\n for (const key in obj) {\n if (obj[key] !== null) {\n value[key] = obj[key] as any;\n } else {\n value[key] = undefined as any;\n }\n }\n return value;\n}\n\n/**\n * Reference counting helper for managing resource lifecycle\n */\nexport class Rc {\n private count = 0;\n private disposeFn?: () => void | Promise<void>;\n\n constructor(disposeFn?: () => void | Promise<void>) {\n this.disposeFn = disposeFn;\n }\n\n inc(): void {\n this.count++;\n }\n\n async dec(): Promise<void> {\n this.count--;\n if (this.count === 0 && this.disposeFn) {\n await this.disposeFn();\n }\n }\n\n getCount(): number {\n return this.count;\n }\n}\n"],"mappings":";;;;;;AAMA,MAAa,4BAA4B;;AAGzC,MAAa,0BAA0B;;AAEvC,MAAa,yBAAyB;;AAEtC,MAAa,yBAAyB;AACtC,MAAa,yBAAyB;AACtC,MAAa,wBAAwB;AACrC,MAAa,yBAAyB;;AAEtC,MAAa,0BAA0B;AAgCvC,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,aAAa;AACnB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AACxB,MAAM,oBAAoB;AAE1B,MAAM,gBAAqD;CACzD,MAAM;CACN,QAAQ;CACR,SAAS;CACT,SAAS;AACX;AAEA,MAAM,gBAAgD;CAAC;CAAQ;CAAU;CAAW;AAAS;AAE7F,SAAS,cAAc,SAAwB;CAC7C,MAAM,wBAAQ,IAAI,MAAM,yCAAyC,SAAS;CAC1E,MAAM,OAAO;CACb,OAAO;AACT;AAEA,SAAS,iBAAiB,OAAe,OAAqB;CAC5D,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,MAAM,QAAQ,YACxD,MAAM,cAAc,GAAG,MAAM,iBAAiB;AAElD;AAEA,SAAgB,0BAA0B,QAAuC;CAC/E,IAAI,OAAO,WAAW,GAAG,MAAM,cAAc,uCAAuC;CACpF,IAAI,OAAO,SAAA,IACT,MAAM,cAAc,yBAAiD;CAGvE,IAAI,aAAa;CACjB,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,EAAE,iBAAiB,aAAa,MAAM,cAAc,0BAA0B;EAClF,IAAI,MAAM,aAAA,SACR,MAAM,cAAc,iBAAiB,uBAAuB,OAAO;EAErE,cAAc,MAAM;EACpB,IAAI,aAAA,SACF,MAAM,cAAc,iBAAiB,uBAAuB,OAAO;CAEvE;CACA,OAAO;AACT;AAEA,SAAgB,0BAA0B,SAAkC;CAC1E,IAAI,CAAC,QAAQ,OAAO,MAAM,cAAc,mBAAmB;CAC3D,IAAI,CAAC,OAAO,cAAc,QAAQ,UAAU,KAAK,QAAQ,aAAa,GACpE,MAAM,cAAc,gDAAgD;CAEtE,IACE,CAAC,OAAO,cAAc,QAAQ,SAAS,KACvC,QAAQ,YAAY,KACpB,QAAQ,YAAA,IAER,MAAM,cAAc,oCAA2D;CAEjF,IACE,CAAC,OAAO,cAAc,QAAQ,QAAQ,KACtC,QAAQ,WAAA,WACR,QAAQ,WAAA,SAER,MAAM,cACJ,4BAA4B,uBAAuB,OAAO,uBAC5D;CAEF,IACE,CAAC,OAAO,cAAc,QAAQ,MAAM,KACpC,QAAQ,SAAS,KACjB,QAAQ,SAAA,KAER,MAAM,cAAc,gCAAgC,yBAAyB;AAEjF;AAEA,SAAgB,uBAAuB,QAA2C;CAChF,MAAM,aAAa,0BAA0B,MAAM;CACnD,MAAM,UAAU,IAAI,WAAW,qBAAqB,OAAO,SAAS,IAAI,UAAU;CAClF,MAAM,OAAO,IAAI,SAAS,QAAQ,MAAM;CACxC,KAAK,UAAU,GAAG,WAAW;CAC7B,KAAK,UAAU,GAAG,gBAAgB;CAClC,KAAK,UAAU,GAAG,OAAO,MAAM;CAE/B,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,KAAK,UAAU,QAAQ,MAAM,UAAU;EACvC,UAAU;EACV,QAAQ,IAAI,OAAO,MAAM;EACzB,UAAU,MAAM;CAClB;CACA,OAAO;AACT;AAEA,SAAgB,uBAAuB,SAAmC;CACxE,IAAI,QAAQ,aAAa,oBAAoB,MAAM,cAAc,wBAAwB;CACzF,MAAM,OAAO,IAAI,SAAS,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,UAAU;CAChF,IAAI,KAAK,UAAU,CAAC,MAAM,aAAa,MAAM,cAAc,iBAAiB;CAC5E,IAAI,KAAK,UAAU,CAAC,MAAM,kBAAkB,MAAM,cAAc,2BAA2B;CAC3F,MAAM,QAAQ,KAAK,UAAU,CAAC;CAC9B,MAAM,SAAuB,CAAC;CAC9B,IAAI,SAAS;CACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,SAAS;EAC1C,IAAI,SAAS,IAAI,QAAQ,YAAY,MAAM,cAAc,wBAAwB;EACjF,MAAM,SAAS,KAAK,UAAU,MAAM;EACpC,UAAU;EACV,IAAI,SAAS,SAAS,QAAQ,YAAY,MAAM,cAAc,sBAAsB;EAIpF,OAAO,KAAK,IAAI,WAAW,QAAQ,SAAS,QAAQ,SAAS,MAAM,CAAC,CAAC;EACrE,UAAU;CACZ;CACA,IAAI,WAAW,QAAQ,YAAY,MAAM,cAAc,qBAAqB;CAC5E,0BAA0B,MAAM;CAChC,OAAO;AACT;AAEA,SAAgB,wBAAwB,QAAuC;CAC7E,iBAAiB,OAAO,YAAY,YAAY;CAChD,iBAAiB,OAAO,WAAW,WAAW;CAC9C,IACE,CAAC,OAAO,cAAc,OAAO,KAAK,KAClC,OAAO,QAAQ,KACf,OAAO,QAAA,IAEP,MAAM,cAAc,6BAA6B;CAEnD,MAAM,UAAU,IAAI,WAAW,eAAe;CAC9C,MAAM,OAAO,IAAI,SAAS,QAAQ,MAAM;CACxC,KAAK,UAAU,GAAG,eAAe;CACjC,KAAK,UAAU,GAAG,gBAAgB;CAClC,KAAK,UAAU,GAAG,OAAO,KAAK;CAC9B,KAAK,SAAS,GAAG,OAAO,UAAU;CAClC,KAAK,SAAS,IAAI,OAAO,SAAS;CAClC,OAAO;AACT;AAEA,SAAgB,wBAAwB,SAAwC;CAC9E,IAAI,QAAQ,eAAe,iBAAiB,MAAM,cAAc,6BAA6B;CAC7F,MAAM,OAAO,IAAI,SAAS,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,UAAU;CAChF,IAAI,KAAK,UAAU,CAAC,MAAM,iBAAiB,MAAM,cAAc,0BAA0B;CACzF,IAAI,KAAK,UAAU,CAAC,MAAM,kBACxB,MAAM,cAAc,oCAAoC;CAE1D,MAAM,QAAQ,KAAK,UAAU,CAAC;CAC9B,IAAI,QAAQ,KAAK,QAAA,IACf,MAAM,cAAc,sCAAsC;CAE5D,MAAM,SAAS;EACb;EACA,YAAY,KAAK,SAAS,CAAC;EAC3B,WAAW,KAAK,SAAS,EAAE;CAC7B;CACA,iBAAiB,OAAO,YAAY,YAAY;CAChD,iBAAiB,OAAO,WAAW,WAAW;CAC9C,IAAI,OAAO,cAAc,OAAO,aAAa,OAAO,QAAQ,GAC1D,MAAM,cAAc,yCAAyC;CAE/D,OAAO;AACT;AAEA,SAAgB,uBAAuB,QAAsC;CAC3E,iBAAiB,OAAO,YAAY,YAAY;CAChD,iBAAiB,OAAO,WAAW,WAAW;CAC9C,IAAI,aAAa;CACjB,KAAK,MAAM,SAAS,OAAO,QAAQ,cAAc,IAAI,MAAM;CAC3D,IAAI,OAAO,OAAO,SAAA,MAAmC,aAAa,SAChE,MAAM,cAAc,uCAAuC;CAG7D,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,YAAY,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,IAAI,oBAAI,IAAI,WAAW;CACpF,MAAM,eAAe,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,OAAO,oBAAI,IAAI,WAAW;CAC1F,IAAI,UAAU,aAAa,aAAa,aAAA,OACtC,MAAM,cAAc,wBAAwB,uBAAuB,OAAO;CAE5E,MAAM,UAAU,IAAI,WAClB,oBAAoB,aAAa,UAAU,aAAa,aAAa,UACvE;CACA,MAAM,OAAO,IAAI,SAAS,QAAQ,MAAM;CACxC,KAAK,UAAU,GAAG,UAAU;CAC5B,KAAK,SAAS,GAAG,gBAAgB;CACjC,MAAM,QAAQ,cAAc,OAAO;CACnC,IAAI,UAAU,KAAA,GAAW,MAAM,cAAc,sBAAsB;CACnE,KAAK,SAAS,GAAG,KAAK;CACtB,KAAK,SAAS,GAAG,OAAO,WAAW,IAAI,CAAC;CACxC,KAAK,SAAS,GAAG,CAAC;CAClB,KAAK,SAAS,GAAG,OAAO,UAAU;CAClC,KAAK,SAAS,IAAI,OAAO,SAAS;CAClC,KAAK,UAAU,IAAI,OAAO,OAAO,MAAM;CACvC,KAAK,UAAU,IAAI,UAAU,UAAU;CACvC,KAAK,UAAU,IAAI,aAAa,UAAU;CAE1C,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,OAAO,QAAQ;EACjC,KAAK,UAAU,QAAQ,MAAM,UAAU;EACvC,UAAU;EACV,QAAQ,IAAI,OAAO,MAAM;EACzB,UAAU,MAAM;CAClB;CACA,QAAQ,IAAI,WAAW,MAAM;CAC7B,UAAU,UAAU;CACpB,QAAQ,IAAI,cAAc,MAAM;CAChC,OAAO;AACT;AAEA,SAAgB,uBAAuB,SAAuC;CAC5E,IAAI,QAAQ,aAAa,mBAAmB,MAAM,cAAc,uBAAuB;CACvF,MAAM,OAAO,IAAI,SAAS,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,UAAU;CAChF,IAAI,KAAK,UAAU,CAAC,MAAM,YAAY,MAAM,cAAc,gBAAgB;CAC1E,IAAI,KAAK,SAAS,CAAC,MAAM,kBAAkB,MAAM,cAAc,0BAA0B;CACzF,MAAM,QAAQ,cAAc,KAAK,SAAS,CAAC;CAC3C,IAAI,CAAC,OAAO,MAAM,cAAc,sBAAsB;CACtD,MAAM,aAAa,KAAK,SAAS,CAAC;CAClC,MAAM,YAAY,KAAK,SAAS,EAAE;CAClC,iBAAiB,YAAY,YAAY;CACzC,iBAAiB,WAAW,WAAW;CACvC,MAAM,QAAQ,KAAK,UAAU,EAAE;CAC/B,MAAM,kBAAkB,KAAK,UAAU,EAAE;CACzC,MAAM,qBAAqB,KAAK,UAAU,EAAE;CAC5C,IAAI,QAAA,IAAgC,MAAM,cAAc,gCAAgC;CACxF,IAAI,kBAAkB,qBAAA,OACpB,MAAM,cAAc,0BAA0B;CAGhD,MAAM,SAAuB,CAAC;CAC9B,IAAI,SAAS;CACb,IAAI,aAAa;CACjB,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,SAAS;EAC1C,IAAI,SAAS,IAAI,QAAQ,YAAY,MAAM,cAAc,6BAA6B;EACtF,MAAM,SAAS,KAAK,UAAU,MAAM;EACpC,UAAU;EACV,IAAI,SAAS,SAAS,QAAQ,YAAY,MAAM,cAAc,2BAA2B;EACzF,cAAc;EACd,IAAI,aAAA,SAAoC,MAAM,cAAc,yBAAyB;EACrF,OAAO,KAAK,QAAQ,SAAS,QAAQ,SAAS,MAAM,CAAC;EACrD,UAAU;CACZ;CAEA,IAAI,SAAS,kBAAkB,uBAAuB,QAAQ,YAC5D,MAAM,cAAc,4BAA4B;CAElD,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,QACJ,kBAAkB,KAAK,qBAAqB,IACxC;EACE,MAAM,QAAQ,OAAO,QAAQ,SAAS,QAAQ,SAAS,eAAe,CAAC;EACvE,SAAS,QAAQ,OAAO,QAAQ,SAAS,SAAS,eAAe,CAAC;CACpE,IACA,KAAA;CAEN,OAAO;EACL;EACA;EACA;EACA;EACA,WAAW,KAAK,SAAS,CAAC,IAAI,OAAO;EACrC;CACF;AACF;AAEA,SAAgB,qBAAqB,OAAiC;CACpE,IAAI;CACJ,IAAI,iBAAiB,OACnB,aAAa;EAAE,MAAM,MAAM,QAAQ;EAAS,SAAS,MAAM;CAAQ;MAC9D,IAAI,OAAO,UAAU,UAC1B,aAAa;EAAE,MAAM;EAAS,SAAS;CAAM;MACxC,IACL,SACA,OAAO,UAAU,YACjB,OAAQ,MAA6B,SAAS,YAC9C,OAAQ,MAAgC,YAAY,UAEpD,aAAa;EACX,MAAO,MAA0B;EACjC,SAAU,MAA0B;CACtC;MAEA,aAAa;EAAE,MAAM;EAAS,SAAS,OAAO,KAAK;CAAE;CAIvD,WAAW,OAAO,WAAW,KAAK,MAAM,GAAG,IAAI;CAC/C,WAAW,UAAU,WAAW,QAAQ,MAAM,GAAG,IAAI;CACrD,OACE,IAAI,YAAY,CAAC,CAAC,OAAO,WAAW,IAAI,CAAC,CAAC,aACxC,IAAI,YAAY,CAAC,CAAC,OAAO,WAAW,OAAO,CAAC,CAAC,aAC/C,wBACA;EACA,WAAW,UAAU,WAAW,QAAQ,MAAM,GAAG,KAAK,IAAI,GAAG,WAAW,QAAQ,SAAS,GAAG,CAAC;EAC7F,IAAI,WAAW,QAAQ,WAAW,GAChC,WAAW,OAAO,WAAW,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,WAAW,KAAK,SAAS,GAAG,CAAC;CAExF;CACA,OAAO;AACT;;;;;;;;;;;;;;AClVA,MAAa,2BAA2B;AAMxC,MAAa,wCACX;AAEF,MAAa,gCAAgC;AAC7C,MAAa,0BAA0B;AACvC,MAAa,8BAA8B;AAE3C,MAAa,yBAAyB;AACtC,MAAa,uBAAuB,KAAK,IAAI,0BAA0B,2BAA2B;AAElG,MAAa,sBACX;AAGF,MAAa,gCAAgC;AClB7C,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;;AAiH3B,SAAgB,gBAAgB,OAAuB;CACrD,IAAI,OAAO;CACX,KAAK,MAAM,QAAQ,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,GAAG;EAClD,QAAQ;EACR,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,OAAO,SAAS;AAClB;AAEA,SAAS,UAAU,QAAgB,OAAe,aAA6B;CAC7E,MAAM,QAAQ,KAAK,IAAI,IAAI,cAAc,EAAA,CAAG,SAAS,EAAE,CAAC,CAAC,MAAM;CAC/D,MAAM,QAAQ,gBAAgB,KAAK,IAAI;CACvC,OAAO,GAAG,OAAO,GAAG,uBAAuB,GAAG,MAAM,SAAS,EAAE,CAAC,CAAC,SAAS,OAAO,GAAG;AACtF;AAEA,SAAgB,oBAAoB,OAAuB;CACzD,OAAO,UAAU,eAAe,OAAA,EAAyB;AAC3D;AAEA,SAAgB,mBAAmB,OAAuB;CACxD,OAAO,UAAU,cAAc,OAAA,EAAwB;AACzD;AAEA,SAAgB,gBAAgB,QAAwB;CACtD,OAAO,UAAU,WAAW,QAAA,EAAsB;AACpD;AAEA,SAAgB,0BAAoC;CAClD,OAAO,MAAM,KACX,EAAE,QAAA,GAA2B,IAC5B,GAAG,UAAU,eAAe,uBAAuB,GAAG,MAAM,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,GAC3F;AACF;AAEA,SAAS,KAAQ,WAAiC,MAAiB;CACjE,OAAO,UAAU,IAAI,UAAU,WAAW,IAAI,CAAC;AACjD;AAEA,SAAS,QAAQ,KAA0E;CACzF,MAAM,YAAY,IAAI,UAAU,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,IAAI,GAAG;CACrE,OAAO,CAAC,OAAO,IAAI,aAAa,GAAG,UAAU,GAAG,IAAI,SAAS,UAAU,UAAU,GAAG,IAAI,OAAO;AACjG;AAEA,SAAS,aAAgB,QAAa,OAA+C;CACnF,MAAM,yBAAS,IAAI,IAAiB;CACpC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,MAAM,KAAK;EACxB,MAAM,QAAQ,OAAO,IAAI,IAAI;EAC7B,IAAI,OAAO,MAAM,KAAK,KAAK;OACtB,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;CAC/B;CACA,OAAO;AACT;AAEA,SAAS,UAAU,MAAsB,OAAgC;CACvE,OAAO,KAAK,UAAU,MAAM,SAAS,KAAK,WAAW,MAAM;AAC7D;AAEA,SAAS,aAAa,MAAkB,OAA2B;CACjE,MAAM,SAAS,KAAK,IAAI,KAAK,YAAY,MAAM,UAAU;CACzD,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS;EAC3C,MAAM,aAAa,KAAK,SAAS,MAAM;EACvC,IAAI,eAAe,GAAG,OAAO;CAC/B;CACA,OAAO,KAAK,aAAa,MAAM;AACjC;AAEA,eAAe,UACb,QACA,WACiB;CACjB,IAAI,UAAU;CACd,KAAK,IAAI,SAAS,GAAG,SAAS,OAAO,QAAQ,UAAU,oBACrD,YAAY,MAAM,UAAU,OAAO,MAAM,QAAQ,SAAS,kBAAkB,CAAC,EAAA,CAAG;CAElF,OAAO;AACT;AAEA,SAAgB,oBAAoB,UAAgD;CAClF,MAAM,eAAe,OAAO,YAAwD;EAClF,MAAM,cAAc,aAAa,QAAQ,QAAQ,SAAS,mBAAmB,KAAK,KAAK,CAAC;EACxF,MAAM,WAAW,aAAa,QAAQ,QAAQ,SAAS,gBAAgB,KAAK,MAAM,CAAC;EACnF,MAAM,WAAmC,CAAC;EAC1C,KAAK,MAAM,CAAC,MAAM,UAAU,aAAa;GACvC,MAAM,SAAS,KAAK,SAAS,YAAY,IAAI;GAC7C,SAAS,KACP,UACE,MAAM,KAAK,UAAU;IACnB,OAAO,KAAK;IACZ,OAAO;KAAE,OAAO,QAAQ;KAAO,QAAQ,KAAK;IAAO;GACrD,EAAE,IACD,UAAU,OAAO,aAAa,KAAK,CACtC,CACF;EACF;EACA,KAAK,MAAM,CAAC,MAAM,UAAU,UAAU;GACpC,MAAM,SAAS,KAAK,SAAS,SAAS,IAAI;GAC1C,SAAS,KACP,UACE,MAAM,KAAK,UAAU;IACnB,QAAQ,KAAK;IACb,OAAO;KAAE,OAAO,QAAQ;KAAO,QAAQ,KAAK;IAAO;GACrD,EAAE,IACD,UAAU,OAAO,aAAa,KAAK,CACtC,CACF;EACF;EACA,QAAQ,MAAM,QAAQ,IAAI,QAAQ,EAAA,CAAG,QAAQ,OAAO,UAAU,QAAQ,OAAO,CAAC;CAChF;CAEA,OAAO;EACL,MAAM,UAAU,KAAK,oBAAoB,sBAAsB;GAC7D,OAAO,MAAM,KAAK,SAAS,YAAY,oBAAoB,IAAI,KAAK,CAAC,CAAC,CAAC,UACrE,IAAI,OACJ,QAAQ,GAAG,GACX,oBACA,oBACF;EACF;EAEA,MAAM,SAAS,SAAS;GACtB,MAAM,gBAAgB,SAAS;GAK/B,MAAM,QAAQ,KAAK,IAAI,KAAM,KAAK,IAAI,GAHpC,OAAO,kBAAkB,YAAY,OAAO,SAAS,aAAa,IAC9D,KAAK,MAAM,aAAa,IACxB,GACiD,CAAC;GACxD,MAAM,QAAQ,MAAM,QAAQ,IAC1B,wBAAwB,CAAC,CAAC,KAAK,SAC7B,KAAK,SAAS,YAAY,IAAI,CAAC,CAAC,KAAK;IAAE,GAAG;IAAS,OAAO,QAAQ;GAAE,CAAC,CACvE,CACF;GACA,MAAM,UAAU,IAAI,YAAY;GAChC,MAAM,aAAa,MAChB,SAAS,cACR,UAAU,KAAK,KAAK,WAAW;IAAE;IAAO,aAAa,QAAQ,OAAO,MAAM,IAAI;GAAE,EAAE,CACpF,CAAC,CACA,UAAU,MAAM,UAAU;IACzB,MAAM,WAAW,aAAa,KAAK,aAAa,MAAM,WAAW;IACjE,OAAO,SAAS,UAAU,CAAC,WAAW;GACxC,CAAC;GACH,MAAM,OAAO,WAAW,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK;GAChE,MAAM,eACJ,WAAW,UAAU,SAAS,MAAM,OAAO,UAAU,MAAM,aAAa;GAC1E,OAAO;IACL,MAAM;IACN,eAAe;IACf,QAAQ,eAAe,KAAA,IAAY,KAAK,GAAG,EAAE,CAAC,EAAE;GAClD;EACF;EAEA,eAAe,OAAO;GACpB,OAAO,KAAK,SAAS,YAAY,mBAAmB,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK;EACvE;EAEA,YAAY,QAAQ;GAClB,OAAO,KAAK,SAAS,SAAS,gBAAgB,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM;EACnE;EAEA,MAAM,YAAY,OAAO,OAAO;GAC9B,MAAM,kBAAkB,OAAO,WAAW;GAC1C,MAAM,cAAc,KAAK,SAAS,YAAY,mBAAmB,KAAK,CAAC;GACvE,MAAM,aAAa,MAAM,YAAY,QAAQ,OAAO,OAAO,eAAe;GAC1E,IAAI,CAAC,WAAW,WAAW,CAAC,UAAU,WAAW,QAAQ,KAAK,GAC5D,OAAO;IAAE,UAAU;IAAO,QAAQ,WAAW;GAAO;GAGtD,MAAM,eAAe,WAAW,UAAU,WAAW,UAAU,KAAA;GAC/D,MAAM,eAAe,KAAK,SAAS,SAAS,gBAAgB,MAAM,MAAM,CAAC;GACzE,MAAM,gBAAgB,gBAAgB;GACtC,IAAI;GACJ,IAAI;IACF,cAAc,MAAM,aAAa,QAAQ,MAAM,QAAQ,OAAO,aAAa;GAC7E,SAAS,OAAO;IACd,MAAM,QAAQ,WAAW,CACvB,GAAI,eAAe,CAAC,YAAY,aAAa,OAAO,OAAO,YAAY,CAAC,IAAI,CAAC,GAC7E,aAAa,aAAa,MAAM,QAAQ,OAAO,aAAa,CAC9D,CAAC;IACD,MAAM;GACR;GACA,IAAI,CAAC,YAAY,SAAS;IACxB,IAAI,cAAc,MAAM,YAAY,aAAa,OAAO,OAAO,YAAY;IAC3E,OAAO;KAAE,UAAU;KAAO,QAAQ,YAAY;IAAO;GACvD;GAOA,OAAO;IAAE,UAAU;IAAM,aAAA;KAJvB,SAAS,GAAG,gBAAgB,IAAI,GAAG,YAAY;KAC/C;KACA,eAAe,YAAY;IAEM;GAAE;EACvC;EAEA,MAAM,oBAAoB,OAAO,QAAQ,gBAAgB,OAAO,aAAa;GAO3E,IAAI,EAAC,MANqB,KAAK,SAAS,YAAY,mBAAmB,KAAK,CAAC,CAAC,CAAC,SAC7E,OACA,gBACA,OACA,aAAa,YACf,EAAA,CACiB,QAAQ;GACzB,MAAM,KAAK,SAAS,SAAS,gBAAgB,MAAM,CAAC,CAAC,CAAC,QACpD,QACA,gBACA,OACA,aAAa,aACf;EACF;EAEA,MAAM,uBAAuB,OAAO,OAAO,aAAa;GACtD,MAAM,WAAiC,CAAC;GACxC,IAAI,YAAY,cACd,SAAS,KACP,KAAK,SAAS,YAAY,mBAAmB,KAAK,CAAC,CAAC,CAAC,aACnD,OACA,OACA,YAAY,YACd,CACF;GAEF,IAAI,YAAY,eACd,SAAS,KACP,KAAK,SAAS,SAAS,gBAAgB,MAAM,MAAM,CAAC,CAAC,CAAC,aACpD,MAAM,QACN,OACA,YAAY,aACd,CACF;GAEF,MAAM,QAAQ,IAAI,QAAQ;EAC5B;EAEA,MAAM,mBAAmB,SAAS;GAChC,OAAO,EAAE,SAAS,MAAM,aAAa,OAAO,EAAE;EAChD;EAEA,MAAM,UAAU,SAAS;GACvB,MAAM,CAAC,SAAS,eAAe,MAAM,QAAQ,IAAI,CAC/C,KAAK,SAAS,YAAY,oBAAoB,QAAQ,KAAK,CAAC,CAAC,CAAC,UAC5D,QAAQ,OACR,QAAQ,MACR,QAAQ,SACV,GACA,aAAa;IAAE,OAAO,QAAQ;IAAO,OAAO,QAAQ;GAAM,CAAC,CAC7D,CAAC;GACD,OAAO,EAAE,SAAS,QAAQ,UAAU,YAAY;EAClD;CACF;AACF;;;;ACtXA,MAAa,8BAAc,IAAI,IAAI;CACjC;CACA;CACA;CACA;CACA;AACF,CAAU;;;;;AAMV,SAAgB,YAAY,KAAa,OAAyB;CAChE,IACE,YAAY,IAAI,GAA0D,KAC1E,OAAO,UAAU,UACjB;EACA,MAAM,OAAO,IAAI,KAAK,KAAK;EAC3B,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,QAAQ;CAChD;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,UAAU,OAA2B;CACnD,IAAI,MAAM;CACV,MAAM,QAAQ;CACd,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,OACrC,OAAO,OAAO,aAAa,GAAG,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC;CAE5D,OAAO,KAAK,GAAG;AACjB;AAEA,SAAgB,UAAU,MAA0B;CAClD,MAAM,MAAM,KAAK,IAAI;CACrB,MAAM,MAAM,IAAI,WAAW,IAAI,MAAM;CACrC,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAC9B,IAAI,KAAK,IAAI,WAAW,CAAC;CAE3B,OAAO;AACT;;;;AAKA,SAAgB,mBAAmB,MAAc,OAAyB;CACxE,IAAI,iBAAiB,YACnB,OAAO;EACL,QAAQ;EACR,MAAM,UAAU,KAAK;CACvB;CAEF,OAAO;AACT;;;;;;;AAQA,SAAgB,kBAAkB,KAAa,OAAyB;CACtE,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,MAAM;EAEZ,IAAI,IAAI,WAAW,gBAAgB,OAAO,IAAI,SAAS,UACrD,OAAO,UAAU,IAAI,IAAI;EAG3B,IAAI,IAAI,iBAAiB,QAAQ,MAAM,QAAQ,IAAI,IAAI,GACrD,OAAO,IAAI,WAAW,IAAI,IAAgB;CAE9C;CACA,OAAO,YAAY,KAAK,KAAK;AAC/B;;AAGA,SAAgB,UAAU,OAAwB;CAChD,OAAO,KAAK,UAAU,OAAO,kBAAkB;AACjD;;AAGA,SAAgB,MAAS,MAAiB;CACxC,OAAO,KAAK,MAAM,MAAM,iBAAiB;AAC3C;;;;;;;;;;;;;;;;AC1EA,SAAS,YAA2B,KAAa,OAAyB;CAGxE,MAAM,WACJ,QAAQ,OAAO,SAAS,WAAY,KAAiC,OAAO,KAAA;CAC9E,IAAI,oBAAoB,MACtB,OAAO;EAAE,QAAQ;EAAQ,KAAK,SAAS,YAAY;CAAE;CAEvD,IAAI,iBAAiB,YACnB,OAAO;EAAE,QAAQ;EAAc,MAAM,UAAU,KAAK;CAAE;CAExD,OAAO;AACT;AAEA,SAAS,WAAW,KAAa,OAAyB;CACxD,IAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,MAAM;EACZ,IAAI,IAAI,WAAW,UAAU,OAAO,IAAI,QAAQ,UAC9C,OAAO,IAAI,KAAK,IAAI,GAAG;EAEzB,IAAI,IAAI,WAAW,gBAAgB,OAAO,IAAI,SAAS,UACrD,OAAO,UAAU,IAAI,IAAI;CAE7B;CAEA,OAAO,YAAY,KAAK,KAAK;AAC/B;AAEA,SAAgB,aAAa,OAAwB;CACnD,OAAO,KAAK,UAAU,OAAO,WAAW;AAC1C;AAEA,SAAgB,SAAY,MAAiB;CAC3C,OAAO,KAAK,MAAM,MAAM,UAAU;AACpC;;;ACtCA,SAAgB,QAA0B,KAAQ;CAChD,MAAM,QAAQ,CAAC;CAGf,KAAK,MAAM,OAAO,KAChB,IAAI,IAAI,SAAS,MACf,MAAM,OAAO,IAAI;MAEjB,MAAM,OAAO,KAAA;CAGjB,OAAO;AACT"}
|