orez 0.2.27 → 0.2.30
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/dist/cf-do/worker.d.ts +3 -0
- package/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +37 -15
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +0 -8
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +0 -8
- package/src/cf-do/ARCHITECTURE.md +0 -93
- package/src/cf-do/CHAT_E2E.md +0 -213
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1041
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -40
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7191
- package/src/pg-proxy.ts +0 -1087
- package/src/pg-sqlite-compiler/README.md +0 -53
- package/src/pg-sqlite-compiler/catalog/seed.ts +0 -524
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +0 -307
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +0 -377
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +0 -12
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +0 -447
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +0 -32
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +0 -397
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +0 -537
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +0 -1837
- package/src/pg-sqlite-compiler/index.ts +0 -73
- package/src/pg-sqlite-compiler/integration.test.ts +0 -136
- package/src/pg-sqlite-compiler/passes/ast-utils.ts +0 -113
- package/src/pg-sqlite-compiler/passes/catalog.ts +0 -65
- package/src/pg-sqlite-compiler/passes/datetime.ts +0 -74
- package/src/pg-sqlite-compiler/passes/index.ts +0 -49
- package/src/pg-sqlite-compiler/passes/types.ts +0 -156
- package/src/pg-sqlite-compiler/smoke.test.ts +0 -69
- package/src/pg-sqlite-compiler/test/catalog.test.ts +0 -171
- package/src/pg-sqlite-compiler/test/corpus.test.ts +0 -161
- package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +0 -102
- package/src/pg-sqlite-compiler/test/oracle.ts +0 -237
- package/src/pg-sqlite-compiler/test/types.test.ts +0 -109
- package/src/pg-sqlite-compiler/types.ts +0 -63
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../worker.ts", "../../../../do-sql-tracking.ts", "../../../watermark.ts", "../../../../../../soot/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts", "../../../../../../soot/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts", "../bundle-vYmw0E/middleware-insertion-facade.js", "../../../../../../soot/node_modules/wrangler/templates/middleware/common.ts", "../bundle-vYmw0E/middleware-loader.entry.ts"],
|
|
4
|
-
"sourceRoot": "/Users/n8/orez/src/cf-do/.wrangler/tmp/dev-qbho19",
|
|
5
|
-
"sourcesContent": ["// @ts-nocheck \u2014 cloudflare:workers types not available in orez\nimport { DurableObject } from 'cloudflare:workers'\n\nimport { trackedChangeRow } from '../do-sql-tracking.js'\nimport { DurableWatermarkState } from './watermark.js'\n\n/**\n * zero-do: Durable Object that exposes raw SQL execution over ctx.storage.sql.\n *\n * The production Cloudflare path runs real zero-cache via\n * src/worker/zero-cache-embed-cf.ts, with DoBackend calling this DO for\n * Postgres-protocol-backed SQL. The WS sync handler here is kept for\n * development/protocol experiments only; it is not the production replacement\n * for zero-cache.\n *\n * Modes:\n * WS /sync/v49/connect \u2014 bespoke Zero sync protocol (dev/protocol testing)\n * POST /exec \u2014 raw SQL execution (from DoBackend adapter)\n * POST /batch \u2014 atomic batch execution via ctx.storage.transaction()\n */\n\ninterface Env {\n ZERO_DO: DurableObjectNamespace\n}\ninterface SchemaTable {\n primaryKey: string[]\n columns: Record<string, { type: string; optional?: boolean }>\n}\ninterface ClientSchema {\n tables: Record<string, SchemaTable>\n}\ninterface DesiredQuery {\n hash: string\n tableNames: string[]\n}\ninterface DesiredQueryPatchOp {\n op: 'put' | 'del' | 'clear'\n hash?: string\n name?: string\n ast?: any\n}\ninterface CrudOp {\n op: 'insert' | 'update' | 'upsert' | 'delete'\n tableName: string\n value?: Record<string, unknown>\n primaryKey?: string[]\n}\ninterface PushMutation {\n type: string\n name: string\n clientID: string\n id: number\n args: unknown[]\n}\ninterface PushBody {\n clientGroupID?: string\n mutations: PushMutation[]\n}\ninterface SqlTrack {\n tableName: string\n operation: 'INSERT' | 'UPDATE' | 'DELETE'\n returnRows?: boolean\n rowColumns?: string[]\n}\ninterface SqlExecStatement {\n sql: string\n params?: unknown[]\n track?: SqlTrack\n}\ninterface SocketAttachment {\n clientID: string\n clientGroupID: string\n userID: string\n cookie: string | null\n initialized: boolean\n desiredTableNames: string[]\n desiredQueries: DesiredQuery[]\n}\ninterface HibernatableWebSocket extends WebSocket {\n serializeAttachment(value: SocketAttachment): void\n deserializeAttachment(): SocketAttachment | undefined\n}\n\nconst SCHEMA_VERSION = 1\nconst SQL_ERROR_SNIPPET_RADIUS = 1600\nconst SQL_ERROR_FALLBACK_LIMIT = 4000\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n}\n\nfunction sqliteErrorOffset(message: string): number | null {\n const marker = 'offset '\n const start = message.indexOf(marker)\n if (start < 0) return null\n let index = start + marker.length\n let digits = ''\n while (index < message.length) {\n const code = message.charCodeAt(index)\n if (code < 48 || code > 57) break\n digits += message[index]\n index++\n }\n if (!digits) return null\n const offset = Number(digits)\n return Number.isFinite(offset) ? offset : null\n}\n\nfunction sqlErrorSnippet(sql: string, message: string): string {\n const offset = sqliteErrorOffset(message)\n if (offset !== null) {\n const start = Math.max(0, offset - SQL_ERROR_SNIPPET_RADIUS)\n const end = Math.min(sql.length, offset + SQL_ERROR_SNIPPET_RADIUS)\n return `${start > 0 ? '...' : ''}${sql.slice(start, end)}${end < sql.length ? '...' : ''}`\n }\n if (sql.length <= SQL_ERROR_FALLBACK_LIMIT) return sql\n return `${sql.slice(0, SQL_ERROR_FALLBACK_LIMIT)}...`\n}\n\nexport class ZeroDO extends DurableObject {\n private sql: any\n private watermarks: DurableWatermarkState\n private schemaTables = new Set<string>()\n private tableSchemas = new Map<string, SchemaTable>()\n\n constructor(ctx: DurableObjectState, env: Env) {\n super(ctx, env)\n this.sql = ctx.storage.sql\n this.watermarks = new DurableWatermarkState(this.sql)\n }\n\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url)\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': '*',\n },\n })\n }\n if (url.pathname.startsWith('/sync/v') && url.pathname.endsWith('/connect'))\n return this.handleSyncConnect(request, url)\n if (\n (url.pathname === '/zero/push' || url.pathname === '/api/zero/push') &&\n request.method === 'POST'\n )\n return this.handleHttpPush(request)\n if (url.pathname === '/exec' && request.method === 'POST')\n return this.handleExec(request)\n if (url.pathname === '/batch' && request.method === 'POST')\n return this.handleBatch(request)\n if (\n url.pathname === '/changes' &&\n (request.method === 'GET' || request.method === 'POST')\n )\n return this.handleChanges(request, url)\n if (url.pathname === '/notify' && request.method === 'POST')\n return Response.json({ ok: true, cookie: this.cookie() })\n return new Response('not found', { status: 404 })\n }\n\n // \u2500\u2500 Zero sync protocol \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private handleSyncConnect(request: Request, url: URL): Response {\n if (request.headers.get('upgrade')?.toLowerCase() !== 'websocket') {\n return new Response('expected websocket upgrade', { status: 426 })\n }\n const pair = new WebSocketPair()\n const client = pair[0]\n const server = pair[1] as HibernatableWebSocket\n\n const clientID = url.searchParams.get('clientID') || 'anon'\n const clientGroupID = url.searchParams.get('clientGroupID') || 'default'\n const userID = url.searchParams.get('userID') || 'anon'\n const wsid = url.searchParams.get('wsid') || crypto.randomUUID()\n const baseCookie = url.searchParams.get('baseCookie')\n\n this.ctx.acceptWebSocket(server)\n server.serializeAttachment({\n clientID,\n clientGroupID,\n userID,\n cookie: baseCookie ? baseCookie : null,\n initialized: false,\n desiredTableNames: [],\n desiredQueries: [],\n })\n this.sendJSON(server, ['connected', { wsid, timestamp: Date.now() }])\n\n const secProtocol = request.headers.get('sec-websocket-protocol')\n if (secProtocol) {\n const initData = decodeInitConnection(secProtocol)\n if (initData) {\n const clientSchema = initData[1]?.clientSchema as ClientSchema | undefined\n const patch = (initData[1]?.desiredQueriesPatch || []) as DesiredQueryPatchOp[]\n this.applyDesiredQueries(server, patch, clientSchema)\n }\n }\n return new Response(null, {\n status: 101,\n headers: secProtocol ? { 'Sec-WebSocket-Protocol': secProtocol } : undefined,\n webSocket: client,\n } as ResponseInit & { webSocket: WebSocket })\n }\n\n async webSocketMessage(socket: WebSocket, messageData: string | ArrayBuffer) {\n this.watermarks.ensureTables()\n const ws = socket as HibernatableWebSocket\n const attachment = this.readSocketAttachment(ws)\n if (!attachment) return\n const message = this.parseMessage(messageData)\n if (!message) return\n const body = message[1] || {}\n\n switch (message[0]) {\n case 'initConnection':\n case 'changeDesiredQueries':\n this.applyDesiredQueries(\n ws,\n (body.desiredQueriesPatch || []) as DesiredQueryPatchOp[],\n body.clientSchema as ClientSchema | undefined\n )\n break\n case 'push':\n this.handlePush(ws, attachment, message[1] as PushBody)\n break\n case 'pull':\n this.handlePull(ws, message[1] as any)\n break\n case 'ping':\n this.sendJSON(ws, ['pong', {}])\n break\n }\n }\n\n webSocketClose(\n _socket: WebSocket,\n _code: number,\n _reason: string,\n _wasClean: boolean\n ) {}\n\n private applyDesiredQueries(\n socket: HibernatableWebSocket,\n patch: DesiredQueryPatchOp[],\n clientSchema?: ClientSchema\n ) {\n const attachment = this.readSocketAttachment(socket)\n if (!attachment) return\n if (clientSchema) this.ensureSchemaTables(clientSchema)\n\n let nextAttachment = this.applyDesiredQueryPatch(attachment, patch)\n socket.serializeAttachment(nextAttachment)\n\n if (!nextAttachment.initialized) {\n nextAttachment = this.sendSyncPoke(\n socket,\n { ...nextAttachment, initialized: true },\n { lastMutationIDChanges: {}, rowsPatch: [] }\n )\n }\n\n if (patch.length === 0) return\n\n const rowsPatch = [\n { op: 'clear' as const },\n ...this.rowsPatchForTables(nextAttachment.desiredTableNames),\n ]\n this.sendSyncPoke(socket, nextAttachment, {\n gotQueriesPatch: this.gotQueriesPatch(patch),\n rowsPatch,\n })\n }\n\n private applyDesiredQueryPatch(\n attachment: SocketAttachment,\n patch: DesiredQueryPatchOp[]\n ): SocketAttachment {\n const desiredQueries = new Map<string, string[]>()\n for (const query of attachment.desiredQueries || [])\n desiredQueries.set(query.hash, query.tableNames)\n\n for (const op of patch) {\n if (op.op === 'clear') {\n desiredQueries.clear()\n } else if (op.op === 'put' && op.hash) {\n desiredQueries.set(op.hash, this.resolveTablesFromPatch([op]))\n } else if (op.op === 'del' && op.hash) {\n desiredQueries.delete(op.hash)\n }\n }\n\n const queries = [...desiredQueries.entries()].map(([hash, tableNames]) => ({\n hash,\n tableNames,\n }))\n return {\n ...attachment,\n desiredQueries: queries,\n desiredTableNames: [...new Set(queries.flatMap((query) => query.tableNames))],\n }\n }\n\n private gotQueriesPatch(patch: DesiredQueryPatchOp[]) {\n const got: Array<{ op: 'put' | 'del'; hash: string } | { op: 'clear' }> = []\n for (const op of patch) {\n if (op.op === 'clear') got.push({ op: 'clear' })\n else if (op.hash) got.push({ op: op.op, hash: op.hash })\n }\n return got\n }\n\n private rowsPatchForTables(tableNames: string[]): any[] {\n const rowsPatch: any[] = []\n for (const tn of tableNames) {\n if (!this.tableExists(tn)) continue\n for (const row of this.readAllRows(tn))\n rowsPatch.push({ op: 'put', tableName: tn, value: row })\n }\n return rowsPatch\n }\n\n private resolveTablesFromPatch(patch: DesiredQueryPatchOp[]): string[] {\n const tables: string[] = []\n for (const op of patch) {\n const tableFromName = this.tableNameFromOperationName(op.name)\n if (tableFromName) tables.push(tableFromName)\n if (op.ast) this.extractTableFromAST(op.ast, tables)\n }\n return tables\n }\n\n private extractTableFromAST(ast: any, tables: string[]) {\n if (ast?.table) tables.push(ast.table)\n if (ast?.related)\n for (const rel of ast.related) {\n if (rel?.subquery?.table) tables.push(rel.subquery.table)\n if (rel?.subquery?.related) this.extractTableFromAST(rel.subquery, tables)\n }\n }\n\n private handlePush(socket: WebSocket, attachment: SocketAttachment, body: PushBody) {\n const mutations = Array.isArray(body?.mutations) ? body.mutations : []\n const before = this.watermark()\n const mutationResults: any[] = []\n const lastMutationIDChanges: Record<string, number> = {}\n for (const m of mutations) {\n const result = this.applyMutation(m)\n mutationResults.push({ id: { clientID: m.clientID, id: m.id }, result })\n lastMutationIDChanges[m.clientID] = m.id\n }\n this.sendJSON(socket, ['pushResponse', { mutations: mutationResults }])\n const after = this.watermark()\n const changes = after > before ? this.readChangesSince(before) : []\n const rowsPatch = changes.map((c) => this.syncRowPatchFromChange(c))\n if (Object.keys(lastMutationIDChanges).length > 0 || rowsPatch.length > 0)\n this.broadcastMutationPoke(attachment, {\n lastMutationIDChanges,\n rowsPatch,\n })\n }\n\n private async handleHttpPush(request: Request): Promise<Response> {\n try {\n const body = (await request.json()) as any\n const before = this.watermark()\n const mutations = Array.isArray(body?.mutations) ? body.mutations : []\n const mutationResults: any[] = []\n const lastMutationIDChanges: Record<string, number> = {}\n for (const m of mutations) {\n const result = this.applyMutation(m)\n mutationResults.push({ id: { clientID: m.clientID, id: m.id }, result })\n lastMutationIDChanges[m.clientID] = m.id\n }\n const after = this.watermark()\n const changes = after > before ? this.readChangesSince(before) : []\n const rowsPatch = changes.map((c) => this.syncRowPatchFromChange(c))\n if (Object.keys(lastMutationIDChanges).length > 0 || rowsPatch.length > 0)\n this.broadcastPoke(body?.clientGroupID || 'default', {\n lastMutationIDChanges,\n rowsPatch,\n })\n return Response.json({ mutations: mutationResults })\n } catch (err: any) {\n return Response.json({ error: err.message }, { status: 500 })\n }\n }\n\n private handlePull(socket: HibernatableWebSocket, body: { requestID?: string }) {\n this.sendJSON(socket, [\n 'pull',\n {\n requestID: body?.requestID || crypto.randomUUID(),\n cookie: this.cookie(),\n lastMutationIDChanges: {},\n patch: [],\n },\n ])\n }\n\n // \u2500\u2500 SQL execution endpoints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async handleExec(request: Request): Promise<Response> {\n let sql = ''\n try {\n const body = (await request.json()) as {\n sql: string\n params?: unknown[]\n track?: SqlTrack\n }\n sql = body.sql\n const params = Array.isArray(body.params) ? body.params : []\n // Only wrap in ctx.storage.transaction() when the call has change-tracking\n // side effects (executeSQL writes BOTH the user table AND _zero_changes,\n // which must commit together to keep source-tab sync flicker-free). A\n // bare /exec is single-statement and ctx.storage.sql already serializes;\n // the transaction wrap was adding ~2-5ms \u00D7 every call, which on chat's\n // 27k-stmt boot pushed orez backend startup past chat's 60s wait-for-port.\n const result = body.track\n ? await this.ctx.storage.transaction(() =>\n this.executeSQL(sql, params, body.track)\n )\n : this.executeSQL(sql, params)\n return Response.json(result)\n } catch (err: any) {\n const suffix = sql ? ` while executing: ${sqlErrorSnippet(sql, err.message)}` : ''\n console.error(`[exec-500] ${err.message} :: SQL=${sql.slice(0, 800)}`)\n return Response.json({ error: `${err.message}${suffix}` }, { status: 500 })\n }\n }\n\n /** Execute multiple statements atomically via ctx.storage.transaction() */\n private async handleBatch(request: Request): Promise<Response> {\n try {\n const { statements } = (await request.json()) as {\n statements: Array<string | SqlExecStatement>\n }\n const allRows = await this.ctx.storage.transaction(() => {\n const results: any[] = []\n for (const statement of statements) {\n const item = typeof statement === 'string' ? { sql: statement } : statement\n if (!item?.sql?.trim()) continue\n try {\n results.push(\n this.executeSQL(\n item.sql,\n Array.isArray(item.params) ? item.params : [],\n item.track\n )\n )\n } catch (err: any) {\n throw new Error(\n `${err.message} while executing: ${sqlErrorSnippet(item.sql, err.message)}`\n )\n }\n }\n return results\n })\n return Response.json({ results: allRows })\n } catch (err: any) {\n return Response.json({ error: err.message }, { status: 500 })\n }\n }\n\n private async handleChanges(request: Request, url: URL): Promise<Response> {\n try {\n let watermark = Number(\n url.searchParams.get('watermark') ?? url.searchParams.get('since') ?? 0\n )\n let limit = Number(url.searchParams.get('limit') ?? 1000)\n if (request.method === 'POST') {\n const body = (await request.json().catch(() => ({}))) as {\n watermark?: unknown\n since?: unknown\n limit?: unknown\n }\n watermark = Number(body.watermark ?? body.since ?? watermark)\n limit = Number(body.limit ?? limit)\n }\n if (!Number.isFinite(watermark) || watermark < 0) watermark = 0\n if (!Number.isFinite(limit) || limit <= 0) limit = 1000\n return Response.json({\n watermark: this.watermark(),\n changes: this.readChangesSince(watermark).slice(0, Math.min(limit, 10_000)),\n })\n } catch (err: any) {\n return Response.json({ error: err.message }, { status: 500 })\n }\n }\n\n private executeSQL(\n sql: string,\n params: unknown[] = [],\n track?: SqlTrack\n ): { rows: Record<string, unknown>[]; columns: string[]; affectedRows?: number } {\n const cursor = this.sql.exec(sql, ...params)\n const columns = Array.isArray(cursor.columnNames) ? cursor.columnNames : []\n const rows = this.cursorRows(cursor)\n if (!track) return { rows, columns }\n\n for (const row of rows) {\n const trackedRow = trackedChangeRow(row, track)\n if (track.operation === 'DELETE')\n this.appendTrackedChange(track.tableName, 'DELETE', null, trackedRow)\n else this.appendTrackedChange(track.tableName, track.operation, trackedRow, null)\n }\n\n return {\n rows: track.returnRows ? rows : [],\n columns: track.returnRows ? columns : [],\n affectedRows: rows.length,\n }\n }\n\n private cursorRows(cursor: any): Record<string, unknown>[] {\n return cursor.toArray().map((row: any) => {\n const obj: Record<string, unknown> = {}\n for (const k of Object.keys(row)) obj[k] = row[k]\n return obj\n })\n }\n\n // \u2500\u2500 CRUD operations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private applyMutation(mutation: PushMutation) {\n if (mutation.type === 'crud' && mutation.name === '_zero_crud') {\n return this.applyCrudMutation(mutation)\n }\n if (mutation.name === '_zero_cleanupResults') return {}\n if (mutation.type === 'custom') return this.applyTableMutation(mutation)\n return {\n error: 'app',\n message: `unsupported mutation ${mutation.type}:${mutation.name}`,\n }\n }\n\n private applyTableMutation(mutation: PushMutation) {\n const [tableName, action] = this.tableActionFromMutationName(mutation.name)\n if (!tableName || !action)\n return { error: 'app', message: `invalid mutation name ${mutation.name}` }\n if (!this.tableExists(tableName))\n return { error: 'app', message: `unknown table ${tableName}` }\n const value = (mutation.args[0] || {}) as Record<string, unknown>\n const primaryKey = this.primaryKeyForTable(tableName, [])\n\n if (action === 'insert') this.insertRow(tableName, value, primaryKey)\n else if (action === 'upsert') this.upsertRow(tableName, value, primaryKey)\n else if (action === 'delete') this.deleteRow(tableName, value, primaryKey)\n else this.updateRow(tableName, value, primaryKey)\n return {}\n }\n\n private tableActionFromMutationName(name: string): [string, string] {\n if (name.includes('|')) return name.split('|', 2) as [string, string]\n return name.split('.', 2) as [string, string]\n }\n\n private tableNameFromOperationName(name?: string): string | null {\n if (!name) return null\n return name.split(/[.|]/, 1)[0] || null\n }\n\n private applyCrudMutation(mutation: PushMutation) {\n const arg = mutation.args[0] as { ops?: CrudOp[] } | undefined\n const ops = Array.isArray(arg?.ops) ? arg.ops : []\n for (const crud of ops) {\n if (!crud?.tableName) return { error: 'app', message: 'invalid crud mutation' }\n if (!this.tableExists(crud.tableName))\n return { error: 'app', message: `unknown table ${crud.tableName}` }\n const value = crud.value || {}\n const primaryKey = this.primaryKeyForTable(crud.tableName, crud.primaryKey || [])\n if (crud.op === 'insert') this.insertRow(crud.tableName, value, primaryKey)\n else if (crud.op === 'upsert') this.upsertRow(crud.tableName, value, primaryKey)\n else if (crud.op === 'update') this.updateRow(crud.tableName, value, primaryKey)\n else if (crud.op === 'delete') this.deleteRow(crud.tableName, value, primaryKey)\n }\n return {}\n }\n\n private insertRow(tn: string, value: Record<string, unknown>, pk: string[]) {\n if (this.readRowByPrimaryKey(tn, value, pk)) return\n const row = this.storageRow(tn, value, true)\n const cols = Object.keys(row)\n if (!cols.length) return\n const qc = cols.map((c) => quoteIdent(c)).join(', ')\n const ph = cols.map(() => '?').join(', ')\n this.sql.exec(\n `INSERT INTO ${quoteIdent(tn)} (${qc}) VALUES (${ph})`,\n ...cols.map((c) => row[c])\n )\n const next = this.readRowByPrimaryKey(tn, value, pk) || this.normalizeRow(tn, row)\n this.appendChange(tn, 'INSERT', next, null)\n }\n\n private upsertRow(tn: string, value: Record<string, unknown>, pk: string[]) {\n const existing = this.readRowByPrimaryKey(tn, value, pk)\n if (existing) {\n this.updateRow(tn, value, pk)\n return\n }\n this.insertRow(tn, value, pk)\n }\n\n private updateRow(tn: string, value: Record<string, unknown>, pk: string[]) {\n if (!pk.length) return\n const existing = this.readRowByPrimaryKey(tn, value, pk)\n if (!existing) return\n const nk = Object.keys(value).filter((c) => !pk.includes(c))\n if (!nk.length) return\n const storage = this.storageRow(tn, value, false)\n this.sql.exec(\n `UPDATE ${quoteIdent(tn)} SET ${nk.map((c) => `${quoteIdent(c)} = ?`).join(', ')} WHERE ${this.primaryKeyWhere(pk)}`,\n ...nk.map((c) => storage[c]),\n ...pk.map((c) => this.storageColumnValue(tn, c, value[c]))\n )\n const next = this.readRowByPrimaryKey(tn, value, pk)\n if (next) this.appendChange(tn, 'UPDATE', next, existing)\n }\n\n private deleteRow(tn: string, value: Record<string, unknown>, pk: string[]) {\n if (!pk.length) return\n const existing = this.readRowByPrimaryKey(tn, value, pk)\n if (!existing) return\n this.sql.exec(\n `DELETE FROM ${quoteIdent(tn)} WHERE ${this.primaryKeyWhere(pk)}`,\n ...pk.map((c) => this.storageColumnValue(tn, c, value[c]))\n )\n this.appendChange(tn, 'DELETE', null, existing)\n }\n\n private appendChange(\n tn: string,\n op: 'INSERT' | 'UPDATE' | 'DELETE',\n rowData: Record<string, unknown> | null,\n oldData: Record<string, unknown> | null\n ) {\n this.appendTrackedChange(tn, op, rowData, oldData)\n }\n\n private appendTrackedChange(\n tableName: string,\n op: 'INSERT' | 'UPDATE' | 'DELETE',\n rowData: Record<string, unknown> | null,\n oldData: Record<string, unknown> | null\n ) {\n this.watermarks.ensureTables()\n const watermark = this.watermarks.next()\n this.sql.exec(\n 'INSERT INTO _zero_changes (watermark, table_name, op, row_data, old_data) VALUES (?, ?, ?, ?, ?)',\n watermark,\n tableName,\n op,\n rowData ? JSON.stringify(rowData) : null,\n oldData ? JSON.stringify(oldData) : null\n )\n this.watermarks.mark(watermark)\n }\n\n private readChangesSince(watermark: number) {\n this.watermarks.ensureTables()\n return this.sql\n .exec(\n 'SELECT watermark, table_name, op, row_data, old_data FROM _zero_changes WHERE watermark > ? ORDER BY watermark',\n watermark\n )\n .toArray()\n .map((row: any) => ({\n watermark: Number(row.watermark),\n tableName: String(row.table_name),\n op: String(row.op),\n rowData: row.row_data ? JSON.parse(String(row.row_data)) : null,\n oldData: row.old_data ? JSON.parse(String(row.old_data)) : null,\n }))\n }\n\n private watermark(): number {\n return this.watermarks.current()\n }\n\n private ensureSchemaTables(clientSchema: ClientSchema) {\n this.ensureSchemaMetadataTable()\n for (const [name, def] of Object.entries(clientSchema.tables)) {\n this.tableSchemas.set(name, def)\n this.sql.exec(\n 'INSERT OR REPLACE INTO _zero_schema_tables (name, schema_json) VALUES (?, ?)',\n name,\n JSON.stringify(def)\n )\n if (this.schemaTables.has(name)) continue\n const pk = def.primaryKey.map((c) => quoteIdent(c))\n const pkClause = pk.length ? `, PRIMARY KEY (${pk.join(', ')})` : ''\n const colDefs = Object.entries(def.columns).map(([cn, cd]) => {\n const t: Record<string, string> = {\n string: 'TEXT',\n number: 'REAL',\n boolean: 'INTEGER',\n json: 'TEXT',\n bigint: 'TEXT',\n }\n return `${quoteIdent(cn)} ${t[cd.type] || 'TEXT'}`\n })\n this.sql.exec(\n `CREATE TABLE IF NOT EXISTS ${quoteIdent(name)} (${colDefs.join(', ')}${pkClause})`\n )\n this.schemaTables.add(name)\n }\n }\n\n private ensureSchemaMetadataTable() {\n this.sql.exec(\n 'CREATE TABLE IF NOT EXISTS _zero_schema_tables (name TEXT PRIMARY KEY, schema_json TEXT NOT NULL)'\n )\n }\n\n private schemaForTable(tableName: string): SchemaTable | undefined {\n const cached = this.tableSchemas.get(tableName)\n if (cached) return cached\n try {\n this.ensureSchemaMetadataTable()\n const row = this.sql\n .exec('SELECT schema_json FROM _zero_schema_tables WHERE name = ?', tableName)\n .one()\n if (!row?.schema_json) return undefined\n const schema = JSON.parse(String(row.schema_json)) as SchemaTable\n this.tableSchemas.set(tableName, schema)\n return schema\n } catch {\n return undefined\n }\n }\n\n private tableExists(n: string): boolean {\n try {\n return !!this.sql\n .exec(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\", n)\n .one()\n } catch {\n return false\n }\n }\n\n private readAllRows(tn: string): Record<string, unknown>[] {\n try {\n return this.sql\n .exec(`SELECT * FROM ${quoteIdent(tn)}`)\n .toArray()\n .map((row: any) => this.normalizeRow(tn, row))\n } catch {\n return []\n }\n }\n\n private readRowByPrimaryKey(\n tn: string,\n value: Record<string, unknown>,\n pk: string[]\n ): Record<string, unknown> | null {\n if (!pk.length) return null\n try {\n const row = this.sql\n .exec(\n `SELECT * FROM ${quoteIdent(tn)} WHERE ${this.primaryKeyWhere(pk)}`,\n ...pk.map((c) => this.storageColumnValue(tn, c, value[c]))\n )\n .one()\n return row ? this.normalizeRow(tn, row) : null\n } catch {\n return null\n }\n }\n\n private primaryKeyWhere(pk: string[]): string {\n return pk.map((c) => `${quoteIdent(c)} = ?`).join(' AND ')\n }\n\n private primaryKeyForTable(tn: string, fallback: string[]): string[] {\n const schema = this.schemaForTable(tn)\n if (schema?.primaryKey?.length) return schema.primaryKey\n return fallback\n }\n\n private storageRow(\n tn: string,\n value: Record<string, unknown>,\n includeMissingSchemaColumns: boolean\n ): Record<string, unknown> {\n const schema = this.schemaForTable(tn)\n const row: Record<string, unknown> = {}\n if (schema && includeMissingSchemaColumns) {\n for (const column of Object.keys(schema.columns))\n row[column] = this.storageColumnValue(tn, column, value[column] ?? null)\n }\n for (const column of Object.keys(value)) {\n if (value[column] !== undefined)\n row[column] = this.storageColumnValue(tn, column, value[column])\n }\n return row\n }\n\n private storageColumnValue(tn: string, column: string, value: unknown): unknown {\n if (value === undefined || value === null) return null\n const type = this.schemaForTable(tn)?.columns?.[column]?.type\n if (type === 'boolean') return value ? 1 : 0\n if (type === 'json') return typeof value === 'string' ? value : JSON.stringify(value)\n if (type === 'number') return Number(value)\n if (type === 'bigint') return String(value)\n return value\n }\n\n private normalizeRow(\n tn: string,\n row: Record<string, unknown>\n ): Record<string, unknown> {\n const schema = this.schemaForTable(tn)\n const normalized: Record<string, unknown> = {}\n for (const key of Object.keys(row)) {\n const type = schema?.columns?.[key]?.type\n const value = row[key]\n if (value === null || value === undefined) {\n normalized[key] = null\n } else if (type === 'boolean') {\n normalized[key] =\n value === true || value === 1 || value === '1' || value === 'true'\n } else if (type === 'number') {\n normalized[key] = Number(value)\n } else if (type === 'json' && typeof value === 'string') {\n try {\n normalized[key] = JSON.parse(value)\n } catch {\n normalized[key] = value\n }\n } else {\n normalized[key] = value\n }\n }\n return normalized\n }\n\n private sendSyncPoke(\n socket: HibernatableWebSocket,\n attachment: SocketAttachment,\n part: {\n rowsPatch?: any[]\n gotQueriesPatch?: any[]\n lastMutationIDChanges?: Record<string, number>\n }\n ): SocketAttachment {\n const cookie = this.nextCookie()\n const pokeID = crypto.randomUUID()\n this.sendJSON(socket, [\n 'pokeStart',\n {\n pokeID,\n baseCookie: attachment.cookie,\n schemaVersions: {\n minSupportedVersion: SCHEMA_VERSION,\n maxSupportedVersion: SCHEMA_VERSION,\n },\n timestamp: Date.now(),\n },\n ])\n this.sendJSON(socket, ['pokePart', { pokeID, ...part }])\n this.sendJSON(socket, ['pokeEnd', { pokeID, cookie }])\n const nextAttachment = { ...attachment, cookie }\n socket.serializeAttachment(nextAttachment)\n return nextAttachment\n }\n\n private broadcastPoke(\n clientGroupID: string,\n part: { rowsPatch?: any[]; lastMutationIDChanges?: Record<string, number> }\n ) {\n for (const socket of this.ctx.getWebSockets()) {\n const ws = socket as HibernatableWebSocket\n const attachment = this.readSocketAttachment(ws)\n if (!attachment) continue\n if (attachment.clientGroupID !== clientGroupID) continue\n this.sendSyncPoke(ws, attachment, part)\n }\n }\n\n private broadcastMutationPoke(\n sourceAttachment: SocketAttachment,\n part: { rowsPatch?: any[]; lastMutationIDChanges?: Record<string, number> }\n ) {\n const rowsPatch = part.rowsPatch || []\n const changedTables = new Set(\n rowsPatch\n .map((op) => op?.tableName)\n .filter((tableName): tableName is string => !!tableName)\n )\n const hasLastMutationIDChanges =\n Object.keys(part.lastMutationIDChanges || {}).length > 0\n\n for (const socket of this.ctx.getWebSockets()) {\n const ws = socket as HibernatableWebSocket\n const attachment = this.readSocketAttachment(ws)\n if (!attachment) continue\n if (attachment.userID !== sourceAttachment.userID) continue\n\n const isSourceClientGroup =\n attachment.clientGroupID === sourceAttachment.clientGroupID\n const wantsChangedRows =\n changedTables.size > 0 &&\n attachment.desiredTableNames.some((tableName) => changedTables.has(tableName))\n\n const nextPart: {\n rowsPatch?: any[]\n lastMutationIDChanges?: Record<string, number>\n } = {}\n if (wantsChangedRows) nextPart.rowsPatch = rowsPatch\n if (isSourceClientGroup && hasLastMutationIDChanges)\n nextPart.lastMutationIDChanges = part.lastMutationIDChanges\n\n if (!nextPart.rowsPatch && !nextPart.lastMutationIDChanges) continue\n this.sendSyncPoke(ws, attachment, nextPart)\n }\n }\n\n private syncRowPatchFromChange(change: any): any {\n if (change.op === 'DELETE')\n return {\n op: 'del',\n tableName: change.tableName,\n id: this.primaryKeyValue(change.tableName, change.oldData || {}),\n }\n return {\n op: 'put',\n tableName: change.tableName,\n value: this.normalizeRow(change.tableName, change.rowData || {}),\n }\n }\n\n private primaryKeyValue(\n tableName: string,\n row: Record<string, unknown>\n ): Record<string, unknown> {\n const pk = this.primaryKeyForTable(tableName, [])\n if (pk.length) return Object.fromEntries(pk.map((column) => [column, row[column]]))\n if ('id' in row) return { id: row.id }\n return row\n }\n\n private cookie(): string {\n return String(this.watermark()).padStart(20, '0')\n }\n\n private nextCookie(): string {\n const watermark = this.watermarks.next()\n this.watermarks.mark(watermark)\n return String(watermark).padStart(20, '0')\n }\n\n private readSocketAttachment(socket: HibernatableWebSocket): SocketAttachment | null {\n const attachment = socket.deserializeAttachment()\n if (!attachment) return null\n return {\n ...attachment,\n initialized: attachment.initialized === true,\n desiredTableNames: attachment.desiredTableNames || [],\n desiredQueries: attachment.desiredQueries || [],\n }\n }\n\n private sendJSON(socket: WebSocket, msg: unknown) {\n try {\n socket.send(JSON.stringify(msg))\n } catch {}\n }\n private parseMessage(data: string | ArrayBuffer): unknown {\n try {\n return JSON.parse(typeof data === 'string' ? data : new TextDecoder().decode(data))\n } catch {\n return null\n }\n }\n}\n\nexport default {\n async fetch(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url)\n const id = env.ZERO_DO.idFromName('singleton')\n if (url.pathname.startsWith('/sync/v') && url.pathname.endsWith('/connect')) {\n return env.ZERO_DO.get(id).fetch(request)\n }\n if (\n (url.pathname === '/zero/push' || url.pathname === '/api/zero/push') &&\n request.method === 'POST'\n ) {\n return env.ZERO_DO.get(id).fetch(request)\n }\n if (url.pathname === '/exec' && request.method === 'POST') {\n return env.ZERO_DO.get(id).fetch(request)\n }\n if (url.pathname === '/batch' && request.method === 'POST') {\n return env.ZERO_DO.get(id).fetch(request)\n }\n if (\n url.pathname === '/changes' &&\n (request.method === 'GET' || request.method === 'POST')\n ) {\n return env.ZERO_DO.get(id).fetch(request)\n }\n if (url.pathname === '/notify' && request.method === 'POST') {\n return env.ZERO_DO.get(id).fetch(request)\n }\n return new Response('not found', { status: 404 })\n },\n}\n\nfunction decodeInitConnection(\n secProtocol: string\n): [string, Record<string, unknown>] | null {\n try {\n const decoded = decodeURIComponent(secProtocol)\n const bytes = Uint8Array.from(atob(decoded), (char) => char.charCodeAt(0))\n const protocols = JSON.parse(new TextDecoder().decode(bytes)) as {\n initConnectionMessage?: unknown\n }\n const message = protocols.initConnectionMessage\n if (Array.isArray(message) && message[0] === 'initConnection') {\n return message as [string, Record<string, unknown>]\n }\n return null\n } catch {\n return null\n }\n}\n\ninterface DurableObjectState {\n storage: { sql: any; transaction<T>(fn: () => T | Promise<T>): Promise<T> }\n acceptWebSocket(socket: WebSocket, tags?: string[]): void\n getWebSockets(tag?: string): WebSocket[]\n}\ninterface WebSocketPair {\n 0: WebSocket\n 1: WebSocket\n}\ndeclare const WebSocketPair: { new (): { 0: WebSocket; 1: WebSocket } }\n", "export const RETURNING_INTERNAL_PREFIX = '__orez_returning_'\n\nexport interface TrackedRowFilter {\n rowColumns?: string[]\n}\n\nexport function trackedChangeRow(\n row: Record<string, unknown>,\n track: TrackedRowFilter\n): Record<string, unknown> {\n const allowed = track.rowColumns ? new Set(track.rowColumns) : null\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(row)) {\n if (key.startsWith(RETURNING_INTERNAL_PREFIX)) continue\n if (allowed && !allowed.has(key)) continue\n out[key] = value\n }\n return out\n}\n", "export interface DurableSqlResult {\n one(): Record<string, unknown> | undefined\n toArray(): Array<Record<string, unknown>>\n}\n\nexport interface DurableSqlStorage {\n exec(sql: string, ...params: unknown[]): DurableSqlResult\n}\n\nconst WATERMARK_STATE_TABLE = '_zero_change_state'\n\nfunction quoteIdent(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n}\n\nfunction finitePositiveNumber(value: unknown): number {\n const number = Number(value ?? 0)\n return Number.isFinite(number) && number > 0 ? number : 0\n}\n\nexport class DurableWatermarkState {\n constructor(private readonly sql: DurableSqlStorage) {}\n\n ensureTables(): void {\n this.sql.exec(\n \"CREATE TABLE IF NOT EXISTS _zero_changes (watermark INTEGER PRIMARY KEY AUTOINCREMENT, table_name TEXT NOT NULL, op TEXT NOT NULL CHECK (op IN ('INSERT', 'UPDATE', 'DELETE')), row_data TEXT, old_data TEXT, created_at INTEGER NOT NULL DEFAULT (unixepoch()))\"\n )\n this.sql.exec(\n `CREATE TABLE IF NOT EXISTS ${quoteIdent(WATERMARK_STATE_TABLE)} (id INTEGER PRIMARY KEY CHECK (id = 1), last_value INTEGER NOT NULL DEFAULT 0)`\n )\n this.setWatermarkState(this.watermarkState())\n }\n\n next(): number {\n return this.current() + 1\n }\n\n mark(watermark: number): void {\n this.setWatermarkState(watermark)\n this.updateWatermarkSequences(watermark)\n }\n\n current(): number {\n this.ensureTables()\n const state = this.watermarkState()\n const row = this.sql\n .exec('SELECT COALESCE(MAX(watermark), 0) AS watermark FROM _zero_changes')\n .one() as { watermark?: unknown } | undefined\n const tableWatermark = finitePositiveNumber(row?.watermark)\n const sequenceWatermark = this.watermarkSequenceValue()\n const watermark = Math.max(state, tableWatermark, sequenceWatermark)\n if (watermark > state) this.setWatermarkState(watermark)\n if (watermark > sequenceWatermark) this.updateWatermarkSequences(watermark)\n return watermark\n }\n\n private watermarkState(): number {\n try {\n const table = quoteIdent(WATERMARK_STATE_TABLE)\n const row = this.sql.exec(`SELECT last_value FROM ${table} WHERE id = 1`).one() as\n | { last_value?: unknown }\n | undefined\n return finitePositiveNumber(row?.last_value)\n } catch {\n return 0\n }\n }\n\n private setWatermarkState(watermark: number): void {\n const table = quoteIdent(WATERMARK_STATE_TABLE)\n this.sql.exec(`INSERT OR IGNORE INTO ${table} (id, last_value) VALUES (1, 0)`)\n this.sql.exec(`UPDATE ${table} SET last_value = ? WHERE id = 1`, watermark)\n }\n\n private watermarkSequenceValue(): number {\n let watermark = 0\n for (const name of this.watermarkSequenceTables()) {\n try {\n const row = this.sql\n .exec(`SELECT last_value, is_called FROM ${quoteIdent(name)} WHERE dummy = 1`)\n .one() as { last_value?: unknown; is_called?: unknown } | undefined\n if (!row || !row.is_called) continue\n watermark = Math.max(watermark, finitePositiveNumber(row.last_value))\n } catch {\n /* not an orez sequence table */\n }\n }\n return watermark\n }\n\n private watermarkSequenceTables(): string[] {\n return this.sql\n .exec(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%zero_watermark%'\"\n )\n .toArray()\n .map((row) => String(row.name || ''))\n .filter(Boolean)\n }\n\n private updateWatermarkSequences(watermark: number): void {\n for (const name of this.watermarkSequenceTables()) {\n const table = quoteIdent(name)\n try {\n this.sql.exec(\n `INSERT OR IGNORE INTO ${table} (dummy, last_value, is_called) VALUES (1, ?, 1)`,\n watermark\n )\n this.sql.exec(\n `UPDATE ${table} SET last_value = ?, is_called = 1 WHERE dummy = 1`,\n watermark\n )\n } catch {\n /* not an orez sequence table */\n }\n }\n }\n}\n", "import type { Middleware } from \"./common\";\n\nconst drainBody: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} finally {\n\t\ttry {\n\t\t\tif (request.body !== null && !request.bodyUsed) {\n\t\t\t\tconst reader = request.body.getReader();\n\t\t\t\twhile (!(await reader.read()).done) {}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.error(\"Failed to drain the unused request body.\", e);\n\t\t}\n\t}\n};\n\nexport default drainBody;\n", "import type { Middleware } from \"./common\";\n\ninterface JsonError {\n\tmessage?: string;\n\tname?: string;\n\tstack?: string;\n\tcause?: JsonError;\n}\n\nfunction reduceError(e: any): JsonError {\n\treturn {\n\t\tname: e?.name,\n\t\tmessage: e?.message ?? String(e),\n\t\tstack: e?.stack,\n\t\tcause: e?.cause === undefined ? undefined : reduceError(e.cause),\n\t};\n}\n\n// See comment in `bundle.ts` for details on why this is needed\nconst jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {\n\ttry {\n\t\treturn await middlewareCtx.next(request, env);\n\t} catch (e: any) {\n\t\tconst error = reduceError(e);\n\t\treturn Response.json(error, {\n\t\t\tstatus: 500,\n\t\t\theaders: { \"MF-Experimental-Error-Stack\": \"true\" },\n\t\t});\n\t}\n};\n\nexport default jsonError;\n", "\t\t\t\timport worker, * as OTHER_EXPORTS from \"/Users/n8/orez/src/cf-do/worker.ts\";\n\t\t\t\timport * as __MIDDLEWARE_0__ from \"/Users/n8/soot/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts\";\nimport * as __MIDDLEWARE_1__ from \"/Users/n8/soot/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts\";\n\n\t\t\t\texport * from \"/Users/n8/orez/src/cf-do/worker.ts\";\n\t\t\t\tconst MIDDLEWARE_TEST_INJECT = \"__INJECT_FOR_TESTING_WRANGLER_MIDDLEWARE__\";\n\t\t\t\texport const __INTERNAL_WRANGLER_MIDDLEWARE__ = [\n\t\t\t\t\t\n\t\t\t\t\t__MIDDLEWARE_0__.default,__MIDDLEWARE_1__.default\n\t\t\t\t]\n\t\t\t\texport default worker;", "export type Awaitable<T> = T | Promise<T>;\n// TODO: allow dispatching more events?\nexport type Dispatcher = (\n\ttype: \"scheduled\",\n\tinit: { cron?: string }\n) => Awaitable<void>;\n\nexport type IncomingRequest = Request<\n\tunknown,\n\tIncomingRequestCfProperties<unknown>\n>;\n\nexport interface MiddlewareContext {\n\tdispatch: Dispatcher;\n\tnext(request: IncomingRequest, env: any): Awaitable<Response>;\n}\n\nexport type Middleware = (\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tmiddlewareCtx: MiddlewareContext\n) => Awaitable<Response>;\n\nconst __facade_middleware__: Middleware[] = [];\n\n// The register functions allow for the insertion of one or many middleware,\n// We register internal middleware first in the stack, but have no way of controlling\n// the order that addMiddleware is run in service workers so need an internal function.\nexport function __facade_register__(...args: (Middleware | Middleware[])[]) {\n\t__facade_middleware__.push(...args.flat());\n}\nexport function __facade_registerInternal__(\n\t...args: (Middleware | Middleware[])[]\n) {\n\t__facade_middleware__.unshift(...args.flat());\n}\n\nfunction __facade_invokeChain__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tmiddlewareChain: Middleware[]\n): Awaitable<Response> {\n\tconst [head, ...tail] = middlewareChain;\n\tconst middlewareCtx: MiddlewareContext = {\n\t\tdispatch,\n\t\tnext(newRequest, newEnv) {\n\t\t\treturn __facade_invokeChain__(newRequest, newEnv, ctx, dispatch, tail);\n\t\t},\n\t};\n\treturn head(request, env, ctx, middlewareCtx);\n}\n\nexport function __facade_invoke__(\n\trequest: IncomingRequest,\n\tenv: any,\n\tctx: ExecutionContext,\n\tdispatch: Dispatcher,\n\tfinalMiddleware: Middleware\n): Awaitable<Response> {\n\treturn __facade_invokeChain__(request, env, ctx, dispatch, [\n\t\t...__facade_middleware__,\n\t\tfinalMiddleware,\n\t]);\n}\n", "// This loads all middlewares exposed on the middleware object and then starts\n// the invocation chain. The big idea is that we can add these to the middleware\n// export dynamically through wrangler, or we can potentially let users directly\n// add them as a sort of \"plugin\" system.\n\nimport ENTRY, { __INTERNAL_WRANGLER_MIDDLEWARE__ } from \"/Users/n8/orez/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js\";\nimport { __facade_invoke__, __facade_register__, Dispatcher } from \"/Users/n8/soot/node_modules/wrangler/templates/middleware/common.ts\";\nimport type { WorkerEntrypointConstructor } from \"/Users/n8/orez/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js\";\n\n// Preserve all the exports from the worker\nexport * from \"/Users/n8/orez/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js\";\n\nclass __Facade_ScheduledController__ implements ScheduledController {\n\treadonly #noRetry: ScheduledController[\"noRetry\"];\n\n\tconstructor(\n\t\treadonly scheduledTime: number,\n\t\treadonly cron: string,\n\t\tnoRetry: ScheduledController[\"noRetry\"]\n\t) {\n\t\tthis.#noRetry = noRetry;\n\t}\n\n\tnoRetry() {\n\t\tif (!(this instanceof __Facade_ScheduledController__)) {\n\t\t\tthrow new TypeError(\"Illegal invocation\");\n\t\t}\n\t\t// Need to call native method immediately in case uncaught error thrown\n\t\tthis.#noRetry();\n\t}\n}\n\nfunction wrapExportedHandler(worker: ExportedHandler): ExportedHandler {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn worker;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\tconst fetchDispatcher: ExportedHandlerFetchHandler = function (\n\t\trequest,\n\t\tenv,\n\t\tctx\n\t) {\n\t\tif (worker.fetch === undefined) {\n\t\t\tthrow new Error(\"Handler does not export a fetch() function.\");\n\t\t}\n\t\treturn worker.fetch(request, env, ctx);\n\t};\n\n\treturn {\n\t\t...worker,\n\t\tfetch(request, env, ctx) {\n\t\t\tconst dispatcher: Dispatcher = function (type, init) {\n\t\t\t\tif (type === \"scheduled\" && worker.scheduled !== undefined) {\n\t\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\t\tDate.now(),\n\t\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t\t() => {}\n\t\t\t\t\t);\n\t\t\t\t\treturn worker.scheduled(controller, env, ctx);\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn __facade_invoke__(request, env, ctx, dispatcher, fetchDispatcher);\n\t\t},\n\t};\n}\n\nfunction wrapWorkerEntrypoint(\n\tklass: WorkerEntrypointConstructor\n): WorkerEntrypointConstructor {\n\t// If we don't have any middleware defined, just return the handler as is\n\tif (\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__ === undefined ||\n\t\t__INTERNAL_WRANGLER_MIDDLEWARE__.length === 0\n\t) {\n\t\treturn klass;\n\t}\n\t// Otherwise, register all middleware once\n\tfor (const middleware of __INTERNAL_WRANGLER_MIDDLEWARE__) {\n\t\t__facade_register__(middleware);\n\t}\n\n\t// `extend`ing `klass` here so other RPC methods remain callable\n\treturn class extends klass {\n\t\t#fetchDispatcher: ExportedHandlerFetchHandler<Record<string, unknown>> = (\n\t\t\trequest,\n\t\t\tenv,\n\t\t\tctx\n\t\t) => {\n\t\t\tthis.env = env;\n\t\t\tthis.ctx = ctx;\n\t\t\tif (super.fetch === undefined) {\n\t\t\t\tthrow new Error(\"Entrypoint class does not define a fetch() function.\");\n\t\t\t}\n\t\t\treturn super.fetch(request);\n\t\t};\n\n\t\t#dispatcher: Dispatcher = (type, init) => {\n\t\t\tif (type === \"scheduled\" && super.scheduled !== undefined) {\n\t\t\t\tconst controller = new __Facade_ScheduledController__(\n\t\t\t\t\tDate.now(),\n\t\t\t\t\tinit.cron ?? \"\",\n\t\t\t\t\t() => {}\n\t\t\t\t);\n\t\t\t\treturn super.scheduled(controller);\n\t\t\t}\n\t\t};\n\n\t\tfetch(request: Request<unknown, IncomingRequestCfProperties>) {\n\t\t\treturn __facade_invoke__(\n\t\t\t\trequest,\n\t\t\t\tthis.env,\n\t\t\t\tthis.ctx,\n\t\t\t\tthis.#dispatcher,\n\t\t\t\tthis.#fetchDispatcher\n\t\t\t);\n\t\t}\n\t};\n}\n\nlet WRAPPED_ENTRY: ExportedHandler | WorkerEntrypointConstructor | undefined;\nif (typeof ENTRY === \"object\") {\n\tWRAPPED_ENTRY = wrapExportedHandler(ENTRY);\n} else if (typeof ENTRY === \"function\") {\n\tWRAPPED_ENTRY = wrapWorkerEntrypoint(ENTRY);\n}\nexport default WRAPPED_ENTRY;\n"],
|
|
6
|
-
"mappings": "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",
|
|
7
|
-
"names": ["quoteIdent"]
|
|
8
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# orez/cf-do architecture - READ FIRST
|
|
2
|
-
|
|
3
|
-
## The fundamental constraint
|
|
4
|
-
|
|
5
|
-
**Cloudflare Durable Objects have a 128 MB memory budget per instance.**
|
|
6
|
-
|
|
7
|
-
PGlite + WASM Postgres + extension binaries pushes real deployments over that
|
|
8
|
-
budget. That is the dead end. zero-cache is still essential: it owns the sync
|
|
9
|
-
protocol, IVM/CVR machinery, replication handling, and client semantics.
|
|
10
|
-
|
|
11
|
-
The orez Cloudflare path is therefore:
|
|
12
|
-
|
|
13
|
-
- **yes** real `@rocicorp/zero` zero-cache
|
|
14
|
-
- **yes** Durable Object SQLite as the durable storage engine
|
|
15
|
-
- **yes** orez `DoBackend` serving Postgres-protocol semantics to zero-cache
|
|
16
|
-
- **no** PGlite
|
|
17
|
-
- **no** `pglite.wasm`, `pglite.data`, extension `.so` files, or WASM Postgres
|
|
18
|
-
|
|
19
|
-
## Production shape
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
┌────────────────────────────────────────────────────────────────┐
|
|
23
|
-
│ Worker │
|
|
24
|
-
│ │
|
|
25
|
-
│ /sync/v* and /api/zero/* ─────► ZERO_CACHE_DO singleton │
|
|
26
|
-
│ everything else ──────────────► ASSETS │
|
|
27
|
-
└────────────────────────────────────────────────────────────────┘
|
|
28
|
-
|
|
29
|
-
┌────────────────────────────────────────────────────────────────┐
|
|
30
|
-
│ ZERO_CACHE_DO Durable Object │
|
|
31
|
-
│ │
|
|
32
|
-
│ startZeroCacheEmbedCF() runs real zero-cache in-process │
|
|
33
|
-
│ │ │
|
|
34
|
-
│ ├─ zero-cache replica/CVR/CDB SQLite │
|
|
35
|
-
│ │ @rocicorp/zero-sqlite3 -> orez worker SQLite shim │
|
|
36
|
-
│ │ backed by ctx.storage.sql │
|
|
37
|
-
│ │ │
|
|
38
|
-
│ └─ zero-cache upstream Postgres connections │
|
|
39
|
-
│ postgres -> orez postgres browser shim │
|
|
40
|
-
│ DoBackend -> ZERO_SQL_DO /exec and /batch │
|
|
41
|
-
└────────────────────────────────────────────────────────────────┘
|
|
42
|
-
|
|
43
|
-
┌────────────────────────────────────────────────────────────────┐
|
|
44
|
-
│ ZERO_SQL_DO Durable Object │
|
|
45
|
-
│ │
|
|
46
|
-
│ ZeroDO raw SQL endpoints │
|
|
47
|
-
│ /exec, /batch, /changes, /notify, /__orez/* │
|
|
48
|
-
│ ctx.storage.sql │
|
|
49
|
-
│ _orez.changes populated by SQL tracking triggers │
|
|
50
|
-
└────────────────────────────────────────────────────────────────┘
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
All app traffic for a deployed project must use singleton DO IDs for both
|
|
54
|
-
`ZERO_CACHE_DO` and `ZERO_SQL_DO`; otherwise browser sessions will not share the
|
|
55
|
-
same zero-cache process and durable SQLite state.
|
|
56
|
-
|
|
57
|
-
## Important files
|
|
58
|
-
|
|
59
|
-
- `src/worker/zero-cache-embed-cf.ts` - starts real zero-cache inside a Durable
|
|
60
|
-
Object and wires its storage/network dependencies to CF-safe shims.
|
|
61
|
-
- `src/worker/cf-patches.ts` - patches zero-cache internals so its worker
|
|
62
|
-
graph and writer run in the Workers runtime.
|
|
63
|
-
- `src/pg-proxy-do-backend.ts` - translates Postgres protocol operations from
|
|
64
|
-
zero-cache into DO SQL endpoint requests.
|
|
65
|
-
- `src/cf-do/worker.ts` - `ZeroDO`, the generic DO SQL backend. It also still
|
|
66
|
-
contains a bespoke Zero sync protocol handler used for development and
|
|
67
|
-
protocol experiments, but the production Soot deploy path uses real
|
|
68
|
-
zero-cache through `startZeroCacheEmbedCF()`.
|
|
69
|
-
- `src/do-sql-tracking.ts` and `src/replication/*` - change tracking and
|
|
70
|
-
logical replication support over `_orez.changes`.
|
|
71
|
-
|
|
72
|
-
## What not to do
|
|
73
|
-
|
|
74
|
-
- Do not import `@electric-sql/pglite` into the Cloudflare DO deploy path.
|
|
75
|
-
- Do not bundle PGlite WASM/data/extensions into the deploy template.
|
|
76
|
-
- Do not replace zero-cache with the bespoke handler for production sync.
|
|
77
|
-
- Do not add a second fallback path that silently switches between PGlite,
|
|
78
|
-
bespoke sync, and zero-cache. There should be one production path:
|
|
79
|
-
zero-cache -> orez Postgres protocol -> DO SQLite.
|
|
80
|
-
|
|
81
|
-
If a future change needs more Postgres behavior, implement it in
|
|
82
|
-
`DoBackend`/the SQL translator or the DO SQL backend. Do not put PGlite back in
|
|
83
|
-
the request path.
|
|
84
|
-
|
|
85
|
-
## Running the chat e2e harness against this backend
|
|
86
|
-
|
|
87
|
-
See `src/cf-do/CHAT_E2E.md`. The DO path is exercised end-to-end by chat's
|
|
88
|
-
`--lite` mode harness, which has a hard 60-second `waitForPort(zero)` budget
|
|
89
|
-
during boot. Three amplification bugs in `DoBackend` were fixed
|
|
90
|
-
2026-05-26 (snapshot fan-out, metadata persist per-row HTTP, metadata persist
|
|
91
|
-
per-statement-in-tx); boot now completes in ~13s on a developer laptop. If
|
|
92
|
-
boot regresses past the budget again, re-capture the /exec distribution as
|
|
93
|
-
described in `CHAT_E2E.md` §3 before guessing at fixes.
|
package/src/cf-do/CHAT_E2E.md
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
# orez DO mode + chat e2e — what to know before debugging
|
|
2
|
-
|
|
3
|
-
If you landed here because chat e2e is failing against the orez Cloudflare DO
|
|
4
|
-
backend, read this whole file first. The same handful of pitfalls have eaten
|
|
5
|
-
multiple agent-sessions; the goal is for the next one to spend zero time
|
|
6
|
-
re-discovering them.
|
|
7
|
-
|
|
8
|
-
## 1. The architecture in one paragraph
|
|
9
|
-
|
|
10
|
-
chat in `--lite` mode launches `orez` as its database. orez exposes a Postgres
|
|
11
|
-
wire protocol on `VITE_PORT_POSTGRES`. zero-cache (also embedded in lite mode)
|
|
12
|
-
connects to orez over PG protocol and reads/writes both schema and data. orez,
|
|
13
|
-
in DO mode, forwards every translated SQL statement as an HTTP POST to a
|
|
14
|
-
wrangler-hosted `ZeroDO` worker. That worker executes against
|
|
15
|
-
`ctx.storage.sql` (DO SQLite). When chat e2e fails, it almost always fails at
|
|
16
|
-
**chat's 60-second `waitForPort(ports.zero, { timeoutMs: 60_000 })`** in
|
|
17
|
-
`scripts/test/e2e.ts`. That deadline measures orez+zero boot, including
|
|
18
|
-
hundreds of migration statements.
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
chat lite mode (one process tree)
|
|
22
|
-
├── bun run:dev orez --disable-wasm-sqlite ...
|
|
23
|
-
│ └── orez PG-protocol server (port 5632 by default; +PORT_OFFSET in tests)
|
|
24
|
-
│ └── DoBackend (src/pg-proxy-do-backend.ts)
|
|
25
|
-
│ └── HTTP POST /exec or /batch → wrangler dev (port 8799)
|
|
26
|
-
│ └── ZeroDO durable object (src/cf-do/worker.ts)
|
|
27
|
-
│ └── ctx.storage.sql (DO SQLite)
|
|
28
|
-
├── bun migrate run (chat's --on-db-ready callback)
|
|
29
|
-
└── zero-cache process — waits for orez PG, then reads schema, opens port 5048
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## 2. The single number that matters: 60 seconds
|
|
33
|
-
|
|
34
|
-
`scripts/test/e2e.ts` in chat does:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
await waitForPort(ports.postgres, { timeoutMs: 60_000 })
|
|
38
|
-
await waitForPort(ports.zero, { timeoutMs: 60_000 })
|
|
39
|
-
await waitForPort(ports.web, { timeoutMs: 120_000 })
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Postgres opens almost immediately (orez TCP server). Web opens after Vite. The
|
|
43
|
-
`zero` port is what kills you — zero-cache only opens it after orez has
|
|
44
|
-
finished applying all of chat's migrations AND zero has finished reading the
|
|
45
|
-
resulting schema. If boot takes >60s the whole harness fails with
|
|
46
|
-
`task: backend failed after 1m 1s` and the test runner never even starts.
|
|
47
|
-
|
|
48
|
-
**Do not "fix" this by bumping the timeout silently.** Boot time is a real
|
|
49
|
-
budget that needs to fit on a developer's laptop and CI. If you need more, do
|
|
50
|
-
it deliberately in `scripts/test-chat-e2e.ts` (the wrapper) by post-sync
|
|
51
|
-
patching the file — and document why.
|
|
52
|
-
|
|
53
|
-
## 3. Where boot HTTP calls go (measured 2026-05-26)
|
|
54
|
-
|
|
55
|
-
A 15s window of chat e2e boot, captured by adding
|
|
56
|
-
`console.log(\`[exec] ${sql.slice(0, 80)}\`)`at the top of`handleExec`in`src/cf-do/worker.ts`, produced ~3,120 /exec calls per 15s, split roughly:
|
|
57
|
-
|
|
58
|
-
| count | shape | source |
|
|
59
|
-
| ----- | --------------------------------------------------------- | ------------------ |
|
|
60
|
-
| 1858 | `INSERT INTO reaction(...) ON CONFLICT DO NOTHING` | chat seed data |
|
|
61
|
-
| 665 | `UPDATE reaction SET keywords=?,category=? WHERE value=?` | chat metadata fill |
|
|
62
|
-
| 550 | `INSERT OR REPLACE INTO "_orez_pg_metadata" ...` | orez (was per-row) |
|
|
63
|
-
| ~40 | misc DDL / catalog probes | migrations |
|
|
64
|
-
|
|
65
|
-
The first two are chat seed loops — you cannot reduce them without changing
|
|
66
|
-
chat. The third is **pure orez overhead** and was the obvious target.
|
|
67
|
-
|
|
68
|
-
To re-capture this distribution, add the same `console.log` temporarily, run
|
|
69
|
-
the harness, then:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
awk -F'[exec] ' '{print $2}' /tmp/wrangler-do.log | \
|
|
73
|
-
awk '{$1=""; print}' | sort | uniq -c | sort -rn | head -30
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 4. The three amplification bugs we fixed (commit landed 2026-05-26)
|
|
77
|
-
|
|
78
|
-
All in `src/pg-proxy-do-backend.ts`. Before/after:
|
|
79
|
-
|
|
80
|
-
### 4a. `persistDurableMetadata` was a per-row HTTP loop
|
|
81
|
-
|
|
82
|
-
It iterated `schemaMetadata` (all tables × all columns) and `publications` and
|
|
83
|
-
issued one `await doExecResult` per row. A migration that added five columns
|
|
84
|
-
to a table with already-known columns re-persisted _every_ metadata row, every
|
|
85
|
-
time. Fix: build one multi-row `INSERT OR REPLACE INTO ... VALUES (?,?,?,?),
|
|
86
|
-
(?,?,?,?),...` chunked at 200 rows (SQLite ~999 param cap / 4 cols).
|
|
87
|
-
|
|
88
|
-
### 4b. `applyStatementMetadata` was called after every SQL statement
|
|
89
|
-
|
|
90
|
-
Inside a chat migration transaction, this fired N times instead of once. Fix:
|
|
91
|
-
when `inTransaction`, set `txMetadataDirty = true` and skip; flush in
|
|
92
|
-
`commitTransaction` (and the existing `rollbackTransaction` persist already
|
|
93
|
-
covers the rollback path).
|
|
94
|
-
|
|
95
|
-
### 4c. `snapshotTransactionChangeTables` re-ran a `sqlite_master` scan per write
|
|
96
|
-
|
|
97
|
-
For every tracked write inside a transaction, this called
|
|
98
|
-
`tableExistsInDo('_zero_changes')`, `tableExistsInDo('_zero_change_state')`,
|
|
99
|
-
and a `SELECT name FROM sqlite_master WHERE name LIKE '%zero_watermark%'`.
|
|
100
|
-
The change tables only need snapshotting ONCE per transaction. Chat's seed
|
|
101
|
-
loops do thousands of tracked writes inside one tx — this was ~4 extra HTTPs
|
|
102
|
-
per write, easily 7,000+ wasted HTTPs during boot. Fix: add a
|
|
103
|
-
`txChangeTablesSnapshotted` boolean, early-return when true, reset in
|
|
104
|
-
`clearTransactionState`.
|
|
105
|
-
|
|
106
|
-
### 4d. `snapshotTransactionTable` probed sqlite_master on every first-write-per-tx
|
|
107
|
-
|
|
108
|
-
For every first write to a table within a transaction,
|
|
109
|
-
`snapshotTransactionTable` called `tableExistsInDo` (1 /exec to sqlite_master)
|
|
110
|
-
before doing the snapshot CREATE. But we already have `schemaMetadata` —
|
|
111
|
-
populated as a side-effect of every CREATE TABLE we translate — so if a
|
|
112
|
-
table is in there, it exists. Fix: check `schemaMetadata.has(table)` first
|
|
113
|
-
and only fall back to the sqlite_master probe for tables we haven't
|
|
114
|
-
registered. Saves one HTTP per first-tx-write per known table.
|
|
115
|
-
|
|
116
|
-
This last optimization is what closed the mutation-race gap for the unseen
|
|
117
|
-
"speed bellwether" test (see §5b). Removing it will likely cause that test
|
|
118
|
-
to start failing again.
|
|
119
|
-
|
|
120
|
-
**Combined effect:** orez backend boot dropped from "fails at 60s" to "ready in
|
|
121
|
-
~13s" against the same wrangler + same chat harness on the same laptop, and
|
|
122
|
-
**all 51 chat e2e tests pass on first attempt** (4.2 min total runtime).
|
|
123
|
-
|
|
124
|
-
## 5. Running chat e2e against your local changes
|
|
125
|
-
|
|
126
|
-
From `~/orez`:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
# 1. Build orez and start wrangler hosting the DO worker
|
|
130
|
-
bun run build
|
|
131
|
-
cd src/cf-do && bunx wrangler dev --port 8799 --local --no-show-interactive-dev-session > /tmp/wrangler-do.log 2>&1 &
|
|
132
|
-
cd -
|
|
133
|
-
|
|
134
|
-
# 2. Run the wrapper that syncs chat → test-chat and launches the e2e harness
|
|
135
|
-
PORT_OFFSET=30 bun scripts/test-chat-e2e.ts > /tmp/orez-e2e.log 2>&1 &
|
|
136
|
-
|
|
137
|
-
# 3. Tail logs
|
|
138
|
-
tail -f /tmp/orez-e2e.log
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
The wrapper (`scripts/test-chat-e2e.ts`) is the canonical entry point. It:
|
|
142
|
-
|
|
143
|
-
1. Mirrors `~/chat` (or wherever `CHAT_DIR` points) into `~/orez/test-chat/`.
|
|
144
|
-
2. Copies orez `dist/` into `test-chat/node_modules/orez/dist/`.
|
|
145
|
-
3. Sets `OREZ_DATA_DIR=/tmp/orez-{PORT_OFFSET}` and PORT_OFFSET-shifted ports.
|
|
146
|
-
4. Sets `DO_BACKEND_URL=http://127.0.0.1:8799` so orez picks the DO backend.
|
|
147
|
-
5. Runs `bun run test e2e --integration --lite` in `test-chat/`.
|
|
148
|
-
|
|
149
|
-
Multiple e2e runs share one wrangler instance. Reset DO state between runs by
|
|
150
|
-
deleting `~/orez/src/cf-do/.wrangler/state/v3/do/` (so migrations re-apply
|
|
151
|
-
fresh). Resetting only orez state without resetting DO state will leave
|
|
152
|
-
orphaned tables and miss migration-replay bugs.
|
|
153
|
-
|
|
154
|
-
## 5b. The "speed bellwether" test
|
|
155
|
-
|
|
156
|
-
`channel-unseen.test.ts` → `multiple channels track unseen independently` is
|
|
157
|
-
the test that fails first when the backend is too slow. It sends two
|
|
158
|
-
messages from a second browser context back-to-back then closes the context
|
|
159
|
-
without waiting for them to round-trip — so the mutation push has to clear
|
|
160
|
-
in the time between `sendMessageIn` returning and `ctxB.close()` returning.
|
|
161
|
-
|
|
162
|
-
A slow backend loses that race; the mutations get cancelled when the
|
|
163
|
-
context closes; user a never sees the unseen indicator update; the
|
|
164
|
-
`expectChannelUnseen(page, 'Ops', true)` assertion fails.
|
|
165
|
-
|
|
166
|
-
It is **not** flaky in a healthy config. If you see this test fail, the
|
|
167
|
-
backend is too slow. Fix the backend, don't "fix" the test. The optimization
|
|
168
|
-
in §4d (skip `tableExistsInDo` when schemaMetadata knows the table) is what
|
|
169
|
-
got HEAD over the line — adding HTTP round trips back to the
|
|
170
|
-
first-write-per-tx path will likely regress this test.
|
|
171
|
-
|
|
172
|
-
## 6. Common failure modes and what they mean
|
|
173
|
-
|
|
174
|
-
| symptom | likely cause |
|
|
175
|
-
| ---------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
176
|
-
| `task: backend failed after 1m 1s` | zero port didn't open within 60s — orez boot too slow |
|
|
177
|
-
| `task: backend failed` with no time | wrangler not running or `DO_BACKEND_URL` not set |
|
|
178
|
-
| `ECONNREFUSED 127.0.0.1:8799` | wrangler dev died or never started |
|
|
179
|
-
| `TG_OP is not defined` or trigger errors | chat trigger function uses Postgres `TG_OP`; orez skips these on DO |
|
|
180
|
-
| "Ignoring mutation from X with ID N as it was already processed" | chat-side mutation dedup, not orez — non-fatal |
|
|
181
|
-
| First test passes, second hangs | leftover state from previous run; reset `.wrangler/state/v3/do/` |
|
|
182
|
-
|
|
183
|
-
## 7. What never to touch
|
|
184
|
-
|
|
185
|
-
- **Chat source.** The test harness must be identical to what chat ships. If
|
|
186
|
-
you need to change behaviour for a test run, do it as a post-sync patch in
|
|
187
|
-
`scripts/test-chat-e2e.ts` and document why.
|
|
188
|
-
- **`maxFailures`, `timeout`, or `retries` in `test-chat/playwright.config.ts`.**
|
|
189
|
-
Those are mirrored from chat; touching them is a cheat that masks real
|
|
190
|
-
regressions and gets reverted next sync.
|
|
191
|
-
- **PGlite.** This whole path exists because PGlite doesn't fit in the
|
|
192
|
-
Cloudflare DO 128 MB budget. Do not add a PGlite fallback for DO mode.
|
|
193
|
-
|
|
194
|
-
## 8. Future optimization opportunities
|
|
195
|
-
|
|
196
|
-
If chat boot grows past the 60s budget again, the cheapest remaining wins
|
|
197
|
-
look like:
|
|
198
|
-
|
|
199
|
-
- **Batch the chat seed inserts on the orez side.** chat sends ~1,858 individual
|
|
200
|
-
`INSERT INTO reaction ... ON CONFLICT DO NOTHING` over the wire. If we
|
|
201
|
-
detect a series of identical-shape inserts within one tx, we could
|
|
202
|
-
accumulate and flush via `/batch` (one HTTP for N inserts). Risky because
|
|
203
|
-
prepared statements + bind params don't line up; would need careful
|
|
204
|
-
tracking.
|
|
205
|
-
- **Skip `tableExistsInDo` after first-time table creation.** `DoBackend`
|
|
206
|
-
could memoize known-existing tables for the life of the connection and
|
|
207
|
-
drop the probe.
|
|
208
|
-
- **Pipeline `/exec` HTTP calls.** Each call is round-tripped serially through
|
|
209
|
-
wrangler. A small queue + single in-flight HTTP/2 socket would amortize
|
|
210
|
-
the per-call overhead.
|
|
211
|
-
|
|
212
|
-
Do _not_ attempt these speculatively. Re-measure boot HTTP distribution first
|
|
213
|
-
(see §3), then target the largest bucket.
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { DurableWatermarkState } from './watermark.js'
|
|
4
|
-
|
|
5
|
-
class FakeResult {
|
|
6
|
-
constructor(private readonly rows: Array<Record<string, unknown>> = []) {}
|
|
7
|
-
|
|
8
|
-
one() {
|
|
9
|
-
return this.rows[0]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
toArray() {
|
|
13
|
-
return this.rows
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
class FakeSql {
|
|
18
|
-
changes: Array<{ watermark: number }> = []
|
|
19
|
-
state = 0
|
|
20
|
-
sequence = { name: '_orez___zero_watermark', last_value: 1, is_called: 0 }
|
|
21
|
-
|
|
22
|
-
exec(sql: string, ...params: unknown[]) {
|
|
23
|
-
if (sql.startsWith('CREATE TABLE IF NOT EXISTS')) return new FakeResult()
|
|
24
|
-
|
|
25
|
-
if (sql.startsWith('SELECT last_value FROM "_zero_change_state"')) {
|
|
26
|
-
return new FakeResult([{ last_value: this.state }])
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (sql.startsWith('INSERT OR IGNORE INTO "_zero_change_state"')) {
|
|
30
|
-
return new FakeResult()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (sql.startsWith('UPDATE "_zero_change_state" SET last_value = ?')) {
|
|
34
|
-
this.state = Number(params[0])
|
|
35
|
-
return new FakeResult()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
sql.startsWith('SELECT COALESCE(MAX(watermark), 0) AS watermark FROM _zero_changes')
|
|
40
|
-
) {
|
|
41
|
-
const watermark = Math.max(0, ...this.changes.map((change) => change.watermark))
|
|
42
|
-
return new FakeResult([{ watermark }])
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (sql.includes('sqlite_master') && sql.includes('%zero_watermark%')) {
|
|
46
|
-
return new FakeResult([{ name: this.sequence.name }])
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (sql.startsWith('SELECT last_value, is_called FROM "_orez___zero_watermark"')) {
|
|
50
|
-
return new FakeResult([
|
|
51
|
-
{
|
|
52
|
-
last_value: this.sequence.last_value,
|
|
53
|
-
is_called: this.sequence.is_called,
|
|
54
|
-
},
|
|
55
|
-
])
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (sql.startsWith('INSERT OR IGNORE INTO "_orez___zero_watermark"')) {
|
|
59
|
-
return new FakeResult()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (sql.startsWith('UPDATE "_orez___zero_watermark" SET last_value = ?')) {
|
|
63
|
-
this.sequence.last_value = Number(params[0])
|
|
64
|
-
this.sequence.is_called = 1
|
|
65
|
-
return new FakeResult()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
throw new Error(`unexpected fake sql: ${sql}`)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe('DurableWatermarkState', () => {
|
|
73
|
-
it('does not reuse watermarks after consumed changes are purged', () => {
|
|
74
|
-
const sql = new FakeSql()
|
|
75
|
-
const watermarks = new DurableWatermarkState(sql)
|
|
76
|
-
|
|
77
|
-
expect(watermarks.current()).toBe(0)
|
|
78
|
-
|
|
79
|
-
const first = watermarks.next()
|
|
80
|
-
sql.changes.push({ watermark: first })
|
|
81
|
-
watermarks.mark(first)
|
|
82
|
-
expect(first).toBe(1)
|
|
83
|
-
|
|
84
|
-
sql.changes = []
|
|
85
|
-
|
|
86
|
-
const second = watermarks.next()
|
|
87
|
-
sql.changes.push({ watermark: second })
|
|
88
|
-
watermarks.mark(second)
|
|
89
|
-
|
|
90
|
-
expect(second).toBe(2)
|
|
91
|
-
expect(sql.sequence).toMatchObject({ last_value: 2, is_called: 1 })
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('synchronizes from existing change rows and sequence state', () => {
|
|
95
|
-
const sql = new FakeSql()
|
|
96
|
-
sql.changes.push({ watermark: 7 })
|
|
97
|
-
const watermarks = new DurableWatermarkState(sql)
|
|
98
|
-
|
|
99
|
-
expect(watermarks.current()).toBe(7)
|
|
100
|
-
expect(sql.state).toBe(7)
|
|
101
|
-
expect(sql.sequence).toMatchObject({ last_value: 7, is_called: 1 })
|
|
102
|
-
})
|
|
103
|
-
})
|