@ewhauser/world-celld 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ewhauser/world-celld",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "celld (self-hosted Durable Objects) World implementation for the Vercel Workflow DevKit",
5
5
  "keywords": [
6
6
  "celld",
@@ -58,6 +58,8 @@
58
58
  "lint": "oxlint --type-aware --deny-warnings --report-unused-disable-directives .",
59
59
  "test": "vitest run",
60
60
  "test:integration": "vitest run --config vitest.integration.config.ts",
61
+ "test:perf:index": "vitest run --config vitest.index-perf.config.ts",
62
+ "test:perf:index:baseline": "node scripts/measure-index-main-baseline.mjs",
61
63
  "test:perf:minio": "bash test/perf/minio/run.sh",
62
64
  "typecheck": "tsc --noEmit",
63
65
  "check:worker-bundle": "node scripts/check-worker-bundle.mjs",
@@ -73,7 +75,7 @@
73
75
  "devDependencies": {
74
76
  "@cloudflare/workers-types": "^5.20260708.1",
75
77
  "@ewhauser/world-celld": "link:.",
76
- "@types/node": "^22.19.0",
78
+ "@types/node": "^26.1.2",
77
79
  "@workflow/world-testing": "5.0.0-beta.42",
78
80
  "esbuild": "^0.28.2",
79
81
  "oxfmt": "^0.63.0",
@@ -1,164 +0,0 @@
1
- //#region src/vendor/shared/serialization.ts
2
- /** Standard Date fields used across all worlds */
3
- const DATE_FIELDS = /* @__PURE__ */ new Set([
4
- "createdAt",
5
- "updatedAt",
6
- "startedAt",
7
- "completedAt",
8
- "retryAfter"
9
- ]);
10
- /**
11
- * JSON reviver that converts ISO date strings to Date objects.
12
- * Use with JSON.parse(json, dateReviver).
13
- */
14
- function dateReviver(key, value) {
15
- if (DATE_FIELDS.has(key) && typeof value === "string") {
16
- const date = new Date(value);
17
- return Number.isNaN(date.getTime()) ? value : date;
18
- }
19
- return value;
20
- }
21
- /**
22
- * Base64 helpers. Modified vs upstream: implemented over atob/btoa instead of
23
- * Buffer so this module is safe inside the celld worker bundle (workerd has
24
- * no Buffer global); atob/btoa are global in both workerd and Node >= 16.
25
- */
26
- function b64encode(bytes) {
27
- let bin = "";
28
- const CHUNK = 32768;
29
- for (let i = 0; i < bytes.length; i += CHUNK) bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
30
- return btoa(bin);
31
- }
32
- function b64decode(text) {
33
- const bin = atob(text);
34
- const out = new Uint8Array(bin.length);
35
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
36
- return out;
37
- }
38
- /**
39
- * JSON replacer that encodes Uint8Array as a tagged object with base64 data.
40
- */
41
- function uint8ArrayReplacer(_key, value) {
42
- if (value instanceof Uint8Array) return {
43
- __type: "Uint8Array",
44
- data: b64encode(value)
45
- };
46
- return value;
47
- }
48
- /**
49
- * JSON reviver that decodes tagged Uint8Array objects and ISO date strings.
50
- * Supports both:
51
- * - New format: { __type: 'Uint8Array', data: '<base64>' }
52
- * - Legacy format: { __uint8array: true, data: [1, 2, 3] }
53
- */
54
- function uint8ArrayReviver(key, value) {
55
- if (value && typeof value === "object") {
56
- const obj = value;
57
- if (obj.__type === "Uint8Array" && typeof obj.data === "string") return b64decode(obj.data);
58
- if (obj.__uint8array === true && Array.isArray(obj.data)) return new Uint8Array(obj.data);
59
- }
60
- return dateReviver(key, value);
61
- }
62
- /** Stringify with Uint8Array support. */
63
- function stringify(value) {
64
- return JSON.stringify(value, uint8ArrayReplacer);
65
- }
66
- /** Parse with Uint8Array and Date support. */
67
- function parse(text) {
68
- return JSON.parse(text, uint8ArrayReviver);
69
- }
70
- //#endregion
71
- //#region src/codec.ts
72
- /**
73
- * RPC wire codec for the Node client <-> worker router HTTP boundary.
74
- *
75
- * Extends the shared tagged-JSON format (`{__type:'Uint8Array', data}` +
76
- * DATE_FIELDS revival) with explicitly tagged Dates: on Cloudflare the DO RPC
77
- * boundary is structured clone, which preserves EVERY Date — including ones
78
- * outside the well-known DATE_FIELDS (e.g. inside applyEvent outcomes and
79
- * user payloads). The HTTP hop must preserve them too, so Dates travel as
80
- * `{__type:'Date', iso}`.
81
- *
82
- * Runs on both sides of the wire, including inside the celld worker — so no
83
- * Node built-ins (Buffer): base64 goes through atob/btoa.
84
- */
85
- function rpcReplacer(key, value) {
86
- const original = this && typeof this === "object" ? this[key] : void 0;
87
- if (original instanceof Date) return {
88
- __type: "Date",
89
- iso: original.toISOString()
90
- };
91
- if (value instanceof Uint8Array) return {
92
- __type: "Uint8Array",
93
- data: b64encode(value)
94
- };
95
- return value;
96
- }
97
- function rpcReviver(key, value) {
98
- if (value && typeof value === "object") {
99
- const obj = value;
100
- if (obj.__type === "Date" && typeof obj.iso === "string") return new Date(obj.iso);
101
- if (obj.__type === "Uint8Array" && typeof obj.data === "string") return b64decode(obj.data);
102
- }
103
- return dateReviver(key, value);
104
- }
105
- function rpcStringify(value) {
106
- return JSON.stringify(value, rpcReplacer);
107
- }
108
- function rpcParse(text) {
109
- return JSON.parse(text, rpcReviver);
110
- }
111
- //#endregion
112
- //#region src/vendor/shared/util.ts
113
- function compact(obj) {
114
- const value = {};
115
- for (const key in obj) if (obj[key] !== null) value[key] = obj[key];
116
- else value[key] = void 0;
117
- return value;
118
- }
119
- //#endregion
120
- //#region src/retention.ts
121
- const CLEANUP_RECORD_KEY = "retention:cleanup";
122
- const TOMBSTONE_KEY = "retention:tombstone";
123
- const INDEX_MARKER_PREFIX = "retention:index:";
124
- const HOOK_MARKER_PREFIX = "retention:hook:";
125
- function sortableTimestamp(date) {
126
- return date.getTime().toString().padStart(13, "0");
127
- }
128
- function workflowRunIndexKey(run) {
129
- return `run:${run.workflowName}:${sortableTimestamp(run.createdAt)}:${run.runId}`;
130
- }
131
- function globalRunIndexKey(run) {
132
- return `runall:${sortableTimestamp(run.createdAt)}:${run.runId}`;
133
- }
134
- function correlationIndexKey(correlationId, createdAt, eventId, runId) {
135
- return `correlation:${encodeURIComponent(correlationId)}:${sortableTimestamp(createdAt)}:${eventId}:${runId}`;
136
- }
137
- function indexMarkerKey(indexKey) {
138
- return `${INDEX_MARKER_PREFIX}${encodeURIComponent(indexKey)}`;
139
- }
140
- function hookMarkerKey(reference) {
141
- return `${HOOK_MARKER_PREFIX}${encodeURIComponent(reference.hookId)}:${encodeURIComponent(reference.token)}`;
142
- }
143
- function cleanupTombstone(record, now) {
144
- return {
145
- version: 1,
146
- runId: record.runId,
147
- terminalStatus: record.terminalStatus,
148
- completedAt: record.completedAt,
149
- expiredAt: record.dueAt,
150
- tombstonedAt: now
151
- };
152
- }
153
- function expiredRead(tombstone) {
154
- return {
155
- ok: false,
156
- code: "RUN_EXPIRED",
157
- message: `Workflow run "${tombstone.runId}" expired at ${tombstone.expiredAt.toISOString()}`,
158
- tombstone
159
- };
160
- }
161
- //#endregion
162
- export { cleanupTombstone as a, globalRunIndexKey as c, workflowRunIndexKey as d, compact as f, stringify as g, parse as h, TOMBSTONE_KEY as i, hookMarkerKey as l, rpcStringify as m, HOOK_MARKER_PREFIX as n, correlationIndexKey as o, rpcParse as p, INDEX_MARKER_PREFIX as r, expiredRead as s, CLEANUP_RECORD_KEY as t, indexMarkerKey as u };
163
-
164
- //# sourceMappingURL=retention-bLY1vcOu.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"retention-bLY1vcOu.js","names":[],"sources":["../src/vendor/shared/serialization.ts","../src/codec.ts","../src/vendor/shared/util.ts","../src/retention.ts"],"sourcesContent":["/** 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","import type { TerminalWorkflowRunStatus, WorkflowRun } from '@workflow/world';\n\nexport const CLEANUP_RECORD_KEY = 'retention:cleanup';\nexport const TOMBSTONE_KEY = 'retention:tombstone';\nexport const INDEX_MARKER_PREFIX = 'retention:index:';\nexport const HOOK_MARKER_PREFIX = 'retention:hook:';\n\nexport type CleanupPhase = 'retained' | 'index' | 'streams' | 'queues' | 'payload' | 'tombstoned';\n\nexport interface CleanupRecord {\n version: 1;\n runId: string;\n workflowName: string;\n createdAt: Date;\n completedAt: Date;\n terminalStatus: TerminalWorkflowRunStatus;\n dueAt: Date;\n queueShards: number;\n phase: CleanupPhase;\n attempts: number;\n lastError?: string;\n lastAttemptAt?: Date;\n tombstonedAt?: Date;\n deletedPayloadKeys: number;\n deletedStreams: number;\n deletedQueueMessages: number;\n}\n\nexport interface RunTombstone {\n version: 1;\n runId: string;\n terminalStatus: TerminalWorkflowRunStatus;\n completedAt: Date;\n expiredAt: Date;\n tombstonedAt: Date;\n}\n\nexport type RunReadOutcome<T> =\n | { ok: true; value: T }\n | { ok: false; code: 'RUN_EXPIRED'; message: string; tombstone: RunTombstone };\n\nexport interface HookIndexReference {\n hookId: string;\n token: string;\n}\n\nexport interface ExpireRunIndexesRequest {\n runId: string;\n keys: string[];\n hooks: HookIndexReference[];\n expiredAt: number;\n}\n\nexport interface ExpireRunIndexesResult {\n deleted: number;\n}\n\nexport interface ExpireRunStreamsResult {\n streams: string[];\n}\n\nexport interface ExpireStreamResult {\n deleted: boolean;\n chunks: number;\n}\n\nexport interface ExpireQueueRunResult {\n deleted: number;\n}\n\nexport interface ScheduleCleanupRequest {\n retentionMs: number;\n queueShards: number;\n}\n\nexport function sortableTimestamp(date: Date): string {\n return date.getTime().toString().padStart(13, '0');\n}\n\nexport function workflowRunIndexKey(\n run: Pick<WorkflowRun, 'workflowName' | 'createdAt' | 'runId'>,\n) {\n return `run:${run.workflowName}:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nexport function globalRunIndexKey(run: Pick<WorkflowRun, 'createdAt' | 'runId'>) {\n return `runall:${sortableTimestamp(run.createdAt)}:${run.runId}`;\n}\n\nexport function correlationIndexKey(\n correlationId: string,\n createdAt: Date,\n eventId: string,\n runId: string,\n): string {\n return `correlation:${encodeURIComponent(correlationId)}:${sortableTimestamp(createdAt)}:${eventId}:${runId}`;\n}\n\nexport function indexMarkerKey(indexKey: string): string {\n return `${INDEX_MARKER_PREFIX}${encodeURIComponent(indexKey)}`;\n}\n\nexport function hookMarkerKey(reference: HookIndexReference): string {\n return `${HOOK_MARKER_PREFIX}${encodeURIComponent(reference.hookId)}:${encodeURIComponent(reference.token)}`;\n}\n\nexport function cleanupTombstone(record: CleanupRecord, now: Date): RunTombstone {\n return {\n version: 1,\n runId: record.runId,\n terminalStatus: record.terminalStatus,\n completedAt: record.completedAt,\n expiredAt: record.dueAt,\n tombstonedAt: now,\n };\n}\n\nexport function expiredRead<T>(tombstone: RunTombstone): RunReadOutcome<T> {\n return {\n ok: false,\n code: 'RUN_EXPIRED',\n message: `Workflow run \"${tombstone.runId}\" expired at ${tombstone.expiredAt.toISOString()}`,\n tombstone,\n };\n}\n"],"mappings":"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"}