orez 0.2.26 → 0.2.27

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.
Files changed (103) hide show
  1. package/dist/cf-do/worker.d.ts.map +1 -1
  2. package/dist/cf-do/worker.js +9 -1
  3. package/dist/cf-do/worker.js.map +1 -1
  4. package/dist/pg-proxy-do-backend.d.ts +2 -0
  5. package/dist/pg-proxy-do-backend.d.ts.map +1 -1
  6. package/dist/pg-proxy-do-backend.js +49 -7
  7. package/dist/pg-proxy-do-backend.js.map +1 -1
  8. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
  9. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
  10. package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
  11. package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
  12. package/dist/pg-sqlite-compiler/index.d.ts +12 -0
  13. package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
  14. package/dist/pg-sqlite-compiler/index.js +59 -0
  15. package/dist/pg-sqlite-compiler/index.js.map +1 -0
  16. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
  17. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
  18. package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
  19. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
  20. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
  21. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
  22. package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
  23. package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
  24. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
  25. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
  26. package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
  27. package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
  28. package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
  29. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
  30. package/dist/pg-sqlite-compiler/passes/index.js +39 -0
  31. package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
  32. package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
  33. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
  34. package/dist/pg-sqlite-compiler/passes/types.js +103 -0
  35. package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
  36. package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
  37. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
  38. package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
  39. package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
  40. package/dist/pg-sqlite-compiler/types.d.ts +55 -0
  41. package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
  42. package/dist/pg-sqlite-compiler/types.js +2 -0
  43. package/dist/pg-sqlite-compiler/types.js.map +1 -0
  44. package/package.json +7 -2
  45. package/src/cf-do/.wrangler/cache/cf.json +1 -1
  46. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  47. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  48. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  49. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  50. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +11 -0
  51. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +134 -0
  52. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +11 -0
  53. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +134 -0
  54. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +1059 -0
  55. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +8 -0
  56. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +1059 -0
  57. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +8 -0
  58. package/src/cf-do/ARCHITECTURE.md +10 -0
  59. package/src/cf-do/CHAT_E2E.md +213 -0
  60. package/src/cf-do/worker.ts +11 -3
  61. package/src/cli.test.ts +3 -1
  62. package/src/pg-proxy-do-backend.ts +44 -10
  63. package/src/pg-sqlite-compiler/README.md +53 -0
  64. package/src/pg-sqlite-compiler/catalog/seed.ts +524 -0
  65. package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +307 -0
  66. package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +377 -0
  67. package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +12 -0
  68. package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +447 -0
  69. package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +32 -0
  70. package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +397 -0
  71. package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +337 -0
  72. package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +337 -0
  73. package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +537 -0
  74. package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +1837 -0
  75. package/src/pg-sqlite-compiler/index.ts +73 -0
  76. package/src/pg-sqlite-compiler/integration.test.ts +136 -0
  77. package/src/pg-sqlite-compiler/passes/ast-utils.ts +113 -0
  78. package/src/pg-sqlite-compiler/passes/catalog.ts +65 -0
  79. package/src/pg-sqlite-compiler/passes/datetime.ts +74 -0
  80. package/src/pg-sqlite-compiler/passes/index.ts +49 -0
  81. package/src/pg-sqlite-compiler/passes/types.ts +156 -0
  82. package/src/pg-sqlite-compiler/smoke.test.ts +69 -0
  83. package/src/pg-sqlite-compiler/test/catalog.test.ts +171 -0
  84. package/src/pg-sqlite-compiler/test/corpus.test.ts +161 -0
  85. package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +102 -0
  86. package/src/pg-sqlite-compiler/test/oracle.ts +237 -0
  87. package/src/pg-sqlite-compiler/test/types.test.ts +109 -0
  88. package/src/pg-sqlite-compiler/types.ts +63 -0
  89. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
  90. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
  91. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
  92. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
  93. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
  94. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
  95. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
  96. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
  97. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
  98. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
  99. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
  100. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
  101. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
  102. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
  103. /package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/{204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite → 0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite} +0 -0
@@ -0,0 +1,8 @@
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": ";;;;AACA,SAAS,qBAAqB;;;ACDvB,IAAM,4BAA4B;AAMlC,SAAS,iBACd,KACA,OACyB;AACzB,QAAM,UAAU,MAAM,aAAa,IAAI,IAAI,MAAM,UAAU,IAAI;AAC/D,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,IAAI,WAAW,yBAAyB,EAAG;AAC/C,QAAI,WAAW,CAAC,QAAQ,IAAI,GAAG,EAAG;AAClC,QAAI,GAAG,IAAI;AAAA,EACb;AACA,SAAO;AACT;AAZgB;;;ACGhB,IAAM,wBAAwB;AAE9B,SAAS,WAAW,MAAsB;AACxC,SAAO,IAAI,KAAK,QAAQ,MAAM,IAAI,CAAC;AACrC;AAFS;AAIT,SAAS,qBAAqB,OAAwB;AACpD,QAAM,SAAS,OAAO,SAAS,CAAC;AAChC,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAC1D;AAHS;AAKF,IAAM,wBAAN,MAA4B;AAAA,EACjC,YAA6B,KAAwB;AAAxB;AAAA,EAAyB;AAAA,EArBxD,OAoBmC;AAAA;AAAA;AAAA,EAGjC,eAAqB;AACnB,SAAK,IAAI;AAAA,MACP;AAAA,IACF;AACA,SAAK,IAAI;AAAA,MACP,8BAA8B,WAAW,qBAAqB,CAAC;AAAA,IACjE;AACA,SAAK,kBAAkB,KAAK,eAAe,CAAC;AAAA,EAC9C;AAAA,EAEA,OAAe;AACb,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AAAA,EAEA,KAAK,WAAyB;AAC5B,SAAK,kBAAkB,SAAS;AAChC,SAAK,yBAAyB,SAAS;AAAA,EACzC;AAAA,EAEA,UAAkB;AAChB,SAAK,aAAa;AAClB,UAAM,QAAQ,KAAK,eAAe;AAClC,UAAM,MAAM,KAAK,IACd,KAAK,oEAAoE,EACzE,IAAI;AACP,UAAM,iBAAiB,qBAAqB,KAAK,SAAS;AAC1D,UAAM,oBAAoB,KAAK,uBAAuB;AACtD,UAAM,YAAY,KAAK,IAAI,OAAO,gBAAgB,iBAAiB;AACnE,QAAI,YAAY,MAAO,MAAK,kBAAkB,SAAS;AACvD,QAAI,YAAY,kBAAmB,MAAK,yBAAyB,SAAS;AAC1E,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAyB;AAC/B,QAAI;AACF,YAAM,QAAQ,WAAW,qBAAqB;AAC9C,YAAM,MAAM,KAAK,IAAI,KAAK,0BAA0B,KAAK,eAAe,EAAE,IAAI;AAG9E,aAAO,qBAAqB,KAAK,UAAU;AAAA,IAC7C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,kBAAkB,WAAyB;AACjD,UAAM,QAAQ,WAAW,qBAAqB;AAC9C,SAAK,IAAI,KAAK,yBAAyB,KAAK,iCAAiC;AAC7E,SAAK,IAAI,KAAK,UAAU,KAAK,oCAAoC,SAAS;AAAA,EAC5E;AAAA,EAEQ,yBAAiC;AACvC,QAAI,YAAY;AAChB,eAAW,QAAQ,KAAK,wBAAwB,GAAG;AACjD,UAAI;AACF,cAAM,MAAM,KAAK,IACd,KAAK,qCAAqC,WAAW,IAAI,CAAC,kBAAkB,EAC5E,IAAI;AACP,YAAI,CAAC,OAAO,CAAC,IAAI,UAAW;AAC5B,oBAAY,KAAK,IAAI,WAAW,qBAAqB,IAAI,UAAU,CAAC;AAAA,MACtE,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,0BAAoC;AAC1C,WAAO,KAAK,IACT;AAAA,MACC;AAAA,IACF,EACC,QAAQ,EACR,IAAI,CAAC,QAAQ,OAAO,IAAI,QAAQ,EAAE,CAAC,EACnC,OAAO,OAAO;AAAA,EACnB;AAAA,EAEQ,yBAAyB,WAAyB;AACxD,eAAW,QAAQ,KAAK,wBAAwB,GAAG;AACjD,YAAM,QAAQ,WAAW,IAAI;AAC7B,UAAI;AACF,aAAK,IAAI;AAAA,UACP,yBAAyB,KAAK;AAAA,UAC9B;AAAA,QACF;AACA,aAAK,IAAI;AAAA,UACP,UAAU,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AFlCA,IAAM,iBAAiB;AACvB,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AAEjC,SAASA,YAAW,MAAsB;AACxC,SAAO,IAAI,KAAK,QAAQ,MAAM,IAAI,CAAC;AACrC;AAFS,OAAAA,aAAA;AAIT,SAAS,kBAAkB,SAAgC;AACzD,QAAM,SAAS;AACf,QAAM,QAAQ,QAAQ,QAAQ,MAAM;AACpC,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,QAAQ,OAAO;AAC3B,MAAI,SAAS;AACb,SAAO,QAAQ,QAAQ,QAAQ;AAC7B,UAAM,OAAO,QAAQ,WAAW,KAAK;AACrC,QAAI,OAAO,MAAM,OAAO,GAAI;AAC5B,cAAU,QAAQ,KAAK;AACvB;AAAA,EACF;AACA,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,OAAO,MAAM;AAC5B,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAfS;AAiBT,SAAS,gBAAgB,KAAa,SAAyB;AAC7D,QAAM,SAAS,kBAAkB,OAAO;AACxC,MAAI,WAAW,MAAM;AACnB,UAAM,QAAQ,KAAK,IAAI,GAAG,SAAS,wBAAwB;AAC3D,UAAM,MAAM,KAAK,IAAI,IAAI,QAAQ,SAAS,wBAAwB;AAClE,WAAO,GAAG,QAAQ,IAAI,QAAQ,EAAE,GAAG,IAAI,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,IAAI,SAAS,QAAQ,EAAE;AAAA,EAC1F;AACA,MAAI,IAAI,UAAU,yBAA0B,QAAO;AACnD,SAAO,GAAG,IAAI,MAAM,GAAG,wBAAwB,CAAC;AAClD;AATS;AAWF,IAAM,SAAN,cAAqB,cAAc;AAAA,EAvH1C,OAuH0C;AAAA;AAAA;AAAA,EAChC;AAAA,EACA;AAAA,EACA,eAAe,oBAAI,IAAY;AAAA,EAC/B,eAAe,oBAAI,IAAyB;AAAA,EAEpD,YAAY,KAAyB,KAAU;AAC7C,UAAM,KAAK,GAAG;AACd,SAAK,MAAM,IAAI,QAAQ;AACvB,SAAK,aAAa,IAAI,sBAAsB,KAAK,GAAG;AAAA,EACtD;AAAA,EAEA,MAAM,MAAM,SAAqC;AAC/C,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,QAAI,QAAQ,WAAW,WAAW;AAChC,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,SAAS;AAAA,UACP,+BAA+B;AAAA,UAC/B,gCAAgC;AAAA,UAChC,gCAAgC;AAAA,QAClC;AAAA,MACF,CAAC;AAAA,IACH;AACA,QAAI,IAAI,SAAS,WAAW,SAAS,KAAK,IAAI,SAAS,SAAS,UAAU;AACxE,aAAO,KAAK,kBAAkB,SAAS,GAAG;AAC5C,SACG,IAAI,aAAa,gBAAgB,IAAI,aAAa,qBACnD,QAAQ,WAAW;AAEnB,aAAO,KAAK,eAAe,OAAO;AACpC,QAAI,IAAI,aAAa,WAAW,QAAQ,WAAW;AACjD,aAAO,KAAK,WAAW,OAAO;AAChC,QAAI,IAAI,aAAa,YAAY,QAAQ,WAAW;AAClD,aAAO,KAAK,YAAY,OAAO;AACjC,QACE,IAAI,aAAa,eAChB,QAAQ,WAAW,SAAS,QAAQ,WAAW;AAEhD,aAAO,KAAK,cAAc,SAAS,GAAG;AACxC,QAAI,IAAI,aAAa,aAAa,QAAQ,WAAW;AACnD,aAAO,SAAS,KAAK,EAAE,IAAI,MAAM,QAAQ,KAAK,OAAO,EAAE,CAAC;AAC1D,WAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD;AAAA;AAAA,EAIQ,kBAAkB,SAAkB,KAAoB;AAC9D,QAAI,QAAQ,QAAQ,IAAI,SAAS,GAAG,YAAY,MAAM,aAAa;AACjE,aAAO,IAAI,SAAS,8BAA8B,EAAE,QAAQ,IAAI,CAAC;AAAA,IACnE;AACA,UAAM,OAAO,IAAI,cAAc;AAC/B,UAAM,SAAS,KAAK,CAAC;AACrB,UAAM,SAAS,KAAK,CAAC;AAErB,UAAM,WAAW,IAAI,aAAa,IAAI,UAAU,KAAK;AACrD,UAAM,gBAAgB,IAAI,aAAa,IAAI,eAAe,KAAK;AAC/D,UAAM,SAAS,IAAI,aAAa,IAAI,QAAQ,KAAK;AACjD,UAAM,OAAO,IAAI,aAAa,IAAI,MAAM,KAAK,OAAO,WAAW;AAC/D,UAAM,aAAa,IAAI,aAAa,IAAI,YAAY;AAEpD,SAAK,IAAI,gBAAgB,MAAM;AAC/B,WAAO,oBAAoB;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,aAAa,aAAa;AAAA,MAClC,aAAa;AAAA,MACb,mBAAmB,CAAC;AAAA,MACpB,gBAAgB,CAAC;AAAA,IACnB,CAAC;AACD,SAAK,SAAS,QAAQ,CAAC,aAAa,EAAE,MAAM,WAAW,KAAK,IAAI,EAAE,CAAC,CAAC;AAEpE,UAAM,cAAc,QAAQ,QAAQ,IAAI,wBAAwB;AAChE,QAAI,aAAa;AACf,YAAM,WAAW,qBAAqB,WAAW;AACjD,UAAI,UAAU;AACZ,cAAM,eAAe,SAAS,CAAC,GAAG;AAClC,cAAM,QAAS,SAAS,CAAC,GAAG,uBAAuB,CAAC;AACpD,aAAK,oBAAoB,QAAQ,OAAO,YAAY;AAAA,MACtD;AAAA,IACF;AACA,WAAO,IAAI,SAAS,MAAM;AAAA,MACxB,QAAQ;AAAA,MACR,SAAS,cAAc,EAAE,0BAA0B,YAAY,IAAI;AAAA,MACnE,WAAW;AAAA,IACb,CAA4C;AAAA,EAC9C;AAAA,EAEA,MAAM,iBAAiB,QAAmB,aAAmC;AAC3E,SAAK,WAAW,aAAa;AAC7B,UAAM,KAAK;AACX,UAAM,aAAa,KAAK,qBAAqB,EAAE;AAC/C,QAAI,CAAC,WAAY;AACjB,UAAM,UAAU,KAAK,aAAa,WAAW;AAC7C,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,QAAQ,CAAC,KAAK,CAAC;AAE5B,YAAQ,QAAQ,CAAC,GAAG;AAAA,MAClB,KAAK;AAAA,MACL,KAAK;AACH,aAAK;AAAA,UACH;AAAA,UACC,KAAK,uBAAuB,CAAC;AAAA,UAC9B,KAAK;AAAA,QACP;AACA;AAAA,MACF,KAAK;AACH,aAAK,WAAW,IAAI,YAAY,QAAQ,CAAC,CAAa;AACtD;AAAA,MACF,KAAK;AACH,aAAK,WAAW,IAAI,QAAQ,CAAC,CAAQ;AACrC;AAAA,MACF,KAAK;AACH,aAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC9B;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,eACE,SACA,OACA,SACA,WACA;AAAA,EAAC;AAAA,EAEK,oBACN,QACA,OACA,cACA;AACA,UAAM,aAAa,KAAK,qBAAqB,MAAM;AACnD,QAAI,CAAC,WAAY;AACjB,QAAI,aAAc,MAAK,mBAAmB,YAAY;AAEtD,QAAI,iBAAiB,KAAK,uBAAuB,YAAY,KAAK;AAClE,WAAO,oBAAoB,cAAc;AAEzC,QAAI,CAAC,eAAe,aAAa;AAC/B,uBAAiB,KAAK;AAAA,QACpB;AAAA,QACA,EAAE,GAAG,gBAAgB,aAAa,KAAK;AAAA,QACvC,EAAE,uBAAuB,CAAC,GAAG,WAAW,CAAC,EAAE;AAAA,MAC7C;AAAA,IACF;AAEA,QAAI,MAAM,WAAW,EAAG;AAExB,UAAM,YAAY;AAAA,MAChB,EAAE,IAAI,QAAiB;AAAA,MACvB,GAAG,KAAK,mBAAmB,eAAe,iBAAiB;AAAA,IAC7D;AACA,SAAK,aAAa,QAAQ,gBAAgB;AAAA,MACxC,iBAAiB,KAAK,gBAAgB,KAAK;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,uBACN,YACA,OACkB;AAClB,UAAM,iBAAiB,oBAAI,IAAsB;AACjD,eAAW,SAAS,WAAW,kBAAkB,CAAC;AAChD,qBAAe,IAAI,MAAM,MAAM,MAAM,UAAU;AAEjD,eAAW,MAAM,OAAO;AACtB,UAAI,GAAG,OAAO,SAAS;AACrB,uBAAe,MAAM;AAAA,MACvB,WAAW,GAAG,OAAO,SAAS,GAAG,MAAM;AACrC,uBAAe,IAAI,GAAG,MAAM,KAAK,uBAAuB,CAAC,EAAE,CAAC,CAAC;AAAA,MAC/D,WAAW,GAAG,OAAO,SAAS,GAAG,MAAM;AACrC,uBAAe,OAAO,GAAG,IAAI;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,GAAG,eAAe,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,UAAU,OAAO;AAAA,MACzE;AAAA,MACA;AAAA,IACF,EAAE;AACF,WAAO;AAAA,MACL,GAAG;AAAA,MACH,gBAAgB;AAAA,MAChB,mBAAmB,CAAC,GAAG,IAAI,IAAI,QAAQ,QAAQ,CAAC,UAAU,MAAM,UAAU,CAAC,CAAC;AAAA,IAC9E;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAA8B;AACpD,UAAM,MAAoE,CAAC;AAC3E,eAAW,MAAM,OAAO;AACtB,UAAI,GAAG,OAAO,QAAS,KAAI,KAAK,EAAE,IAAI,QAAQ,CAAC;AAAA,eACtC,GAAG,KAAM,KAAI,KAAK,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,KAAK,CAAC;AAAA,IACzD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,YAA6B;AACtD,UAAM,YAAmB,CAAC;AAC1B,eAAW,MAAM,YAAY;AAC3B,UAAI,CAAC,KAAK,YAAY,EAAE,EAAG;AAC3B,iBAAW,OAAO,KAAK,YAAY,EAAE;AACnC,kBAAU,KAAK,EAAE,IAAI,OAAO,WAAW,IAAI,OAAO,IAAI,CAAC;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAAuB,OAAwC;AACrE,UAAM,SAAmB,CAAC;AAC1B,eAAW,MAAM,OAAO;AACtB,YAAM,gBAAgB,KAAK,2BAA2B,GAAG,IAAI;AAC7D,UAAI,cAAe,QAAO,KAAK,aAAa;AAC5C,UAAI,GAAG,IAAK,MAAK,oBAAoB,GAAG,KAAK,MAAM;AAAA,IACrD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,KAAU,QAAkB;AACtD,QAAI,KAAK,MAAO,QAAO,KAAK,IAAI,KAAK;AACrC,QAAI,KAAK;AACP,iBAAW,OAAO,IAAI,SAAS;AAC7B,YAAI,KAAK,UAAU,MAAO,QAAO,KAAK,IAAI,SAAS,KAAK;AACxD,YAAI,KAAK,UAAU,QAAS,MAAK,oBAAoB,IAAI,UAAU,MAAM;AAAA,MAC3E;AAAA,EACJ;AAAA,EAEQ,WAAW,QAAmB,YAA8B,MAAgB;AAClF,UAAM,YAAY,MAAM,QAAQ,MAAM,SAAS,IAAI,KAAK,YAAY,CAAC;AACrE,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,kBAAyB,CAAC;AAChC,UAAM,wBAAgD,CAAC;AACvD,eAAW,KAAK,WAAW;AACzB,YAAM,SAAS,KAAK,cAAc,CAAC;AACnC,sBAAgB,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;AACvE,4BAAsB,EAAE,QAAQ,IAAI,EAAE;AAAA,IACxC;AACA,SAAK,SAAS,QAAQ,CAAC,gBAAgB,EAAE,WAAW,gBAAgB,CAAC,CAAC;AACtE,UAAM,QAAQ,KAAK,UAAU;AAC7B,UAAM,UAAU,QAAQ,SAAS,KAAK,iBAAiB,MAAM,IAAI,CAAC;AAClE,UAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,KAAK,uBAAuB,CAAC,CAAC;AACnE,QAAI,OAAO,KAAK,qBAAqB,EAAE,SAAS,KAAK,UAAU,SAAS;AACtE,WAAK,sBAAsB,YAAY;AAAA,QACrC;AAAA,QACA;AAAA,MACF,CAAC;AAAA,EACL;AAAA,EAEA,MAAc,eAAe,SAAqC;AAChE,QAAI;AACF,YAAM,OAAQ,MAAM,QAAQ,KAAK;AACjC,YAAM,SAAS,KAAK,UAAU;AAC9B,YAAM,YAAY,MAAM,QAAQ,MAAM,SAAS,IAAI,KAAK,YAAY,CAAC;AACrE,YAAM,kBAAyB,CAAC;AAChC,YAAM,wBAAgD,CAAC;AACvD,iBAAW,KAAK,WAAW;AACzB,cAAM,SAAS,KAAK,cAAc,CAAC;AACnC,wBAAgB,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;AACvE,8BAAsB,EAAE,QAAQ,IAAI,EAAE;AAAA,MACxC;AACA,YAAM,QAAQ,KAAK,UAAU;AAC7B,YAAM,UAAU,QAAQ,SAAS,KAAK,iBAAiB,MAAM,IAAI,CAAC;AAClE,YAAM,YAAY,QAAQ,IAAI,CAAC,MAAM,KAAK,uBAAuB,CAAC,CAAC;AACnE,UAAI,OAAO,KAAK,qBAAqB,EAAE,SAAS,KAAK,UAAU,SAAS;AACtE,aAAK,cAAc,MAAM,iBAAiB,WAAW;AAAA,UACnD;AAAA,UACA;AAAA,QACF,CAAC;AACH,aAAO,SAAS,KAAK,EAAE,WAAW,gBAAgB,CAAC;AAAA,IACrD,SAAS,KAAU;AACjB,aAAO,SAAS,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA,EAEQ,WAAW,QAA+B,MAA8B;AAC9E,SAAK,SAAS,QAAQ;AAAA,MACpB;AAAA,MACA;AAAA,QACE,WAAW,MAAM,aAAa,OAAO,WAAW;AAAA,QAChD,QAAQ,KAAK,OAAO;AAAA,QACpB,uBAAuB,CAAC;AAAA,QACxB,OAAO,CAAC;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,MAAc,WAAW,SAAqC;AAC5D,QAAI,MAAM;AACV,QAAI;AACF,YAAM,OAAQ,MAAM,QAAQ,KAAK;AAKjC,YAAM,KAAK;AACX,YAAM,SAAS,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AAO3D,YAAM,SAAS,KAAK,QAChB,MAAM,KAAK,IAAI,QAAQ;AAAA,QAAY,MACjC,KAAK,WAAW,KAAK,QAAQ,KAAK,KAAK;AAAA,MACzC,IACA,KAAK,WAAW,KAAK,MAAM;AAC/B,aAAO,SAAS,KAAK,MAAM;AAAA,IAC7B,SAAS,KAAU;AACjB,YAAM,SAAS,MAAM,qBAAqB,gBAAgB,KAAK,IAAI,OAAO,CAAC,KAAK;AAChF,cAAQ,MAAM,cAAc,IAAI,OAAO,WAAW,IAAI,MAAM,GAAG,GAAG,CAAC,EAAE;AACrE,aAAO,SAAS,KAAK,EAAE,OAAO,GAAG,IAAI,OAAO,GAAG,MAAM,GAAG,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,YAAY,SAAqC;AAC7D,QAAI;AACF,YAAM,EAAE,WAAW,IAAK,MAAM,QAAQ,KAAK;AAG3C,YAAM,UAAU,MAAM,KAAK,IAAI,QAAQ,YAAY,MAAM;AACvD,cAAM,UAAiB,CAAC;AACxB,mBAAW,aAAa,YAAY;AAClC,gBAAM,OAAO,OAAO,cAAc,WAAW,EAAE,KAAK,UAAU,IAAI;AAClE,cAAI,CAAC,MAAM,KAAK,KAAK,EAAG;AACxB,cAAI;AACF,oBAAQ;AAAA,cACN,KAAK;AAAA,gBACH,KAAK;AAAA,gBACL,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AAAA,gBAC5C,KAAK;AAAA,cACP;AAAA,YACF;AAAA,UACF,SAAS,KAAU;AACjB,kBAAM,IAAI;AAAA,cACR,GAAG,IAAI,OAAO,qBAAqB,gBAAgB,KAAK,KAAK,IAAI,OAAO,CAAC;AAAA,YAC3E;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AACD,aAAO,SAAS,KAAK,EAAE,SAAS,QAAQ,CAAC;AAAA,IAC3C,SAAS,KAAU;AACjB,aAAO,SAAS,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,SAAkB,KAA6B;AACzE,QAAI;AACF,UAAI,YAAY;AAAA,QACd,IAAI,aAAa,IAAI,WAAW,KAAK,IAAI,aAAa,IAAI,OAAO,KAAK;AAAA,MACxE;AACA,UAAI,QAAQ,OAAO,IAAI,aAAa,IAAI,OAAO,KAAK,GAAI;AACxD,UAAI,QAAQ,WAAW,QAAQ;AAC7B,cAAM,OAAQ,MAAM,QAAQ,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAKnD,oBAAY,OAAO,KAAK,aAAa,KAAK,SAAS,SAAS;AAC5D,gBAAQ,OAAO,KAAK,SAAS,KAAK;AAAA,MACpC;AACA,UAAI,CAAC,OAAO,SAAS,SAAS,KAAK,YAAY,EAAG,aAAY;AAC9D,UAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,SAAQ;AACnD,aAAO,SAAS,KAAK;AAAA,QACnB,WAAW,KAAK,UAAU;AAAA,QAC1B,SAAS,KAAK,iBAAiB,SAAS,EAAE,MAAM,GAAG,KAAK,IAAI,OAAO,GAAM,CAAC;AAAA,MAC5E,CAAC;AAAA,IACH,SAAS,KAAU;AACjB,aAAO,SAAS,KAAK,EAAE,OAAO,IAAI,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA,EAEQ,WACN,KACA,SAAoB,CAAC,GACrB,OAC+E;AAC/E,UAAM,SAAS,KAAK,IAAI,KAAK,KAAK,GAAG,MAAM;AAC3C,UAAM,UAAU,MAAM,QAAQ,OAAO,WAAW,IAAI,OAAO,cAAc,CAAC;AAC1E,UAAM,OAAO,KAAK,WAAW,MAAM;AACnC,QAAI,CAAC,MAAO,QAAO,EAAE,MAAM,QAAQ;AAEnC,eAAW,OAAO,MAAM;AACtB,YAAM,aAAa,iBAAiB,KAAK,KAAK;AAC9C,UAAI,MAAM,cAAc;AACtB,aAAK,oBAAoB,MAAM,WAAW,UAAU,MAAM,UAAU;AAAA,UACjE,MAAK,oBAAoB,MAAM,WAAW,MAAM,WAAW,YAAY,IAAI;AAAA,IAClF;AAEA,WAAO;AAAA,MACL,MAAM,MAAM,aAAa,OAAO,CAAC;AAAA,MACjC,SAAS,MAAM,aAAa,UAAU,CAAC;AAAA,MACvC,cAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAAA,EAEQ,WAAW,QAAwC;AACzD,WAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,QAAa;AACxC,YAAM,MAA+B,CAAC;AACtC,iBAAW,KAAK,OAAO,KAAK,GAAG,EAAG,KAAI,CAAC,IAAI,IAAI,CAAC;AAChD,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,cAAc,UAAwB;AAC5C,QAAI,SAAS,SAAS,UAAU,SAAS,SAAS,cAAc;AAC9D,aAAO,KAAK,kBAAkB,QAAQ;AAAA,IACxC;AACA,QAAI,SAAS,SAAS,uBAAwB,QAAO,CAAC;AACtD,QAAI,SAAS,SAAS,SAAU,QAAO,KAAK,mBAAmB,QAAQ;AACvE,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS,wBAAwB,SAAS,IAAI,IAAI,SAAS,IAAI;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,mBAAmB,UAAwB;AACjD,UAAM,CAAC,WAAW,MAAM,IAAI,KAAK,4BAA4B,SAAS,IAAI;AAC1E,QAAI,CAAC,aAAa,CAAC;AACjB,aAAO,EAAE,OAAO,OAAO,SAAS,yBAAyB,SAAS,IAAI,GAAG;AAC3E,QAAI,CAAC,KAAK,YAAY,SAAS;AAC7B,aAAO,EAAE,OAAO,OAAO,SAAS,iBAAiB,SAAS,GAAG;AAC/D,UAAM,QAAS,SAAS,KAAK,CAAC,KAAK,CAAC;AACpC,UAAM,aAAa,KAAK,mBAAmB,WAAW,CAAC,CAAC;AAExD,QAAI,WAAW,SAAU,MAAK,UAAU,WAAW,OAAO,UAAU;AAAA,aAC3D,WAAW,SAAU,MAAK,UAAU,WAAW,OAAO,UAAU;AAAA,aAChE,WAAW,SAAU,MAAK,UAAU,WAAW,OAAO,UAAU;AAAA,QACpE,MAAK,UAAU,WAAW,OAAO,UAAU;AAChD,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,4BAA4B,MAAgC;AAClE,QAAI,KAAK,SAAS,GAAG,EAAG,QAAO,KAAK,MAAM,KAAK,CAAC;AAChD,WAAO,KAAK,MAAM,KAAK,CAAC;AAAA,EAC1B;AAAA,EAEQ,2BAA2B,MAA8B;AAC/D,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,MAAM,QAAQ,CAAC,EAAE,CAAC,KAAK;AAAA,EACrC;AAAA,EAEQ,kBAAkB,UAAwB;AAChD,UAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,UAAM,MAAM,MAAM,QAAQ,KAAK,GAAG,IAAI,IAAI,MAAM,CAAC;AACjD,eAAW,QAAQ,KAAK;AACtB,UAAI,CAAC,MAAM,UAAW,QAAO,EAAE,OAAO,OAAO,SAAS,wBAAwB;AAC9E,UAAI,CAAC,KAAK,YAAY,KAAK,SAAS;AAClC,eAAO,EAAE,OAAO,OAAO,SAAS,iBAAiB,KAAK,SAAS,GAAG;AACpE,YAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,YAAM,aAAa,KAAK,mBAAmB,KAAK,WAAW,KAAK,cAAc,CAAC,CAAC;AAChF,UAAI,KAAK,OAAO,SAAU,MAAK,UAAU,KAAK,WAAW,OAAO,UAAU;AAAA,eACjE,KAAK,OAAO,SAAU,MAAK,UAAU,KAAK,WAAW,OAAO,UAAU;AAAA,eACtE,KAAK,OAAO,SAAU,MAAK,UAAU,KAAK,WAAW,OAAO,UAAU;AAAA,eACtE,KAAK,OAAO,SAAU,MAAK,UAAU,KAAK,WAAW,OAAO,UAAU;AAAA,IACjF;AACA,WAAO,CAAC;AAAA,EACV;AAAA,EAEQ,UAAU,IAAY,OAAgC,IAAc;AAC1E,QAAI,KAAK,oBAAoB,IAAI,OAAO,EAAE,EAAG;AAC7C,UAAM,MAAM,KAAK,WAAW,IAAI,OAAO,IAAI;AAC3C,UAAM,OAAO,OAAO,KAAK,GAAG;AAC5B,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,KAAK,KAAK,IAAI,CAAC,MAAMA,YAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AACnD,UAAM,KAAK,KAAK,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACxC,SAAK,IAAI;AAAA,MACP,eAAeA,YAAW,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE;AAAA,MACnD,GAAG,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;AAAA,IAC3B;AACA,UAAM,OAAO,KAAK,oBAAoB,IAAI,OAAO,EAAE,KAAK,KAAK,aAAa,IAAI,GAAG;AACjF,SAAK,aAAa,IAAI,UAAU,MAAM,IAAI;AAAA,EAC5C;AAAA,EAEQ,UAAU,IAAY,OAAgC,IAAc;AAC1E,UAAM,WAAW,KAAK,oBAAoB,IAAI,OAAO,EAAE;AACvD,QAAI,UAAU;AACZ,WAAK,UAAU,IAAI,OAAO,EAAE;AAC5B;AAAA,IACF;AACA,SAAK,UAAU,IAAI,OAAO,EAAE;AAAA,EAC9B;AAAA,EAEQ,UAAU,IAAY,OAAgC,IAAc;AAC1E,QAAI,CAAC,GAAG,OAAQ;AAChB,UAAM,WAAW,KAAK,oBAAoB,IAAI,OAAO,EAAE;AACvD,QAAI,CAAC,SAAU;AACf,UAAM,KAAK,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;AAC3D,QAAI,CAAC,GAAG,OAAQ;AAChB,UAAM,UAAU,KAAK,WAAW,IAAI,OAAO,KAAK;AAChD,SAAK,IAAI;AAAA,MACP,UAAUA,YAAW,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAGA,YAAW,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,UAAU,KAAK,gBAAgB,EAAE,CAAC;AAAA,MAClH,GAAG,GAAG,IAAI,CAAC,MAAM,QAAQ,CAAC,CAAC;AAAA,MAC3B,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,mBAAmB,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAAA,IAC3D;AACA,UAAM,OAAO,KAAK,oBAAoB,IAAI,OAAO,EAAE;AACnD,QAAI,KAAM,MAAK,aAAa,IAAI,UAAU,MAAM,QAAQ;AAAA,EAC1D;AAAA,EAEQ,UAAU,IAAY,OAAgC,IAAc;AAC1E,QAAI,CAAC,GAAG,OAAQ;AAChB,UAAM,WAAW,KAAK,oBAAoB,IAAI,OAAO,EAAE;AACvD,QAAI,CAAC,SAAU;AACf,SAAK,IAAI;AAAA,MACP,eAAeA,YAAW,EAAE,CAAC,UAAU,KAAK,gBAAgB,EAAE,CAAC;AAAA,MAC/D,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,mBAAmB,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAAA,IAC3D;AACA,SAAK,aAAa,IAAI,UAAU,MAAM,QAAQ;AAAA,EAChD;AAAA,EAEQ,aACN,IACA,IACA,SACA,SACA;AACA,SAAK,oBAAoB,IAAI,IAAI,SAAS,OAAO;AAAA,EACnD;AAAA,EAEQ,oBACN,WACA,IACA,SACA,SACA;AACA,SAAK,WAAW,aAAa;AAC7B,UAAM,YAAY,KAAK,WAAW,KAAK;AACvC,SAAK,IAAI;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,MACpC,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,IACtC;AACA,SAAK,WAAW,KAAK,SAAS;AAAA,EAChC;AAAA,EAEQ,iBAAiB,WAAmB;AAC1C,SAAK,WAAW,aAAa;AAC7B,WAAO,KAAK,IACT;AAAA,MACC;AAAA,MACA;AAAA,IACF,EACC,QAAQ,EACR,IAAI,CAAC,SAAc;AAAA,MAClB,WAAW,OAAO,IAAI,SAAS;AAAA,MAC/B,WAAW,OAAO,IAAI,UAAU;AAAA,MAChC,IAAI,OAAO,IAAI,EAAE;AAAA,MACjB,SAAS,IAAI,WAAW,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,IAAI;AAAA,MAC3D,SAAS,IAAI,WAAW,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,IAAI;AAAA,IAC7D,EAAE;AAAA,EACN;AAAA,EAEQ,YAAoB;AAC1B,WAAO,KAAK,WAAW,QAAQ;AAAA,EACjC;AAAA,EAEQ,mBAAmB,cAA4B;AACrD,SAAK,0BAA0B;AAC/B,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,aAAa,MAAM,GAAG;AAC7D,WAAK,aAAa,IAAI,MAAM,GAAG;AAC/B,WAAK,IAAI;AAAA,QACP;AAAA,QACA;AAAA,QACA,KAAK,UAAU,GAAG;AAAA,MACpB;AACA,UAAI,KAAK,aAAa,IAAI,IAAI,EAAG;AACjC,YAAM,KAAK,IAAI,WAAW,IAAI,CAAC,MAAMA,YAAW,CAAC,CAAC;AAClD,YAAM,WAAW,GAAG,SAAS,kBAAkB,GAAG,KAAK,IAAI,CAAC,MAAM;AAClE,YAAM,UAAU,OAAO,QAAQ,IAAI,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM;AAC5D,cAAM,IAA4B;AAAA,UAChC,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,MAAM;AAAA,UACN,QAAQ;AAAA,QACV;AACA,eAAO,GAAGA,YAAW,EAAE,CAAC,IAAI,EAAE,GAAG,IAAI,KAAK,MAAM;AAAA,MAClD,CAAC;AACD,WAAK,IAAI;AAAA,QACP,8BAA8BA,YAAW,IAAI,CAAC,KAAK,QAAQ,KAAK,IAAI,CAAC,GAAG,QAAQ;AAAA,MAClF;AACA,WAAK,aAAa,IAAI,IAAI;AAAA,IAC5B;AAAA,EACF;AAAA,EAEQ,4BAA4B;AAClC,SAAK,IAAI;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAe,WAA4C;AACjE,UAAM,SAAS,KAAK,aAAa,IAAI,SAAS;AAC9C,QAAI,OAAQ,QAAO;AACnB,QAAI;AACF,WAAK,0BAA0B;AAC/B,YAAM,MAAM,KAAK,IACd,KAAK,8DAA8D,SAAS,EAC5E,IAAI;AACP,UAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,YAAM,SAAS,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC;AACjD,WAAK,aAAa,IAAI,WAAW,MAAM;AACvC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,YAAY,GAAoB;AACtC,QAAI;AACF,aAAO,CAAC,CAAC,KAAK,IACX,KAAK,gEAAgE,CAAC,EACtE,IAAI;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,YAAY,IAAuC;AACzD,QAAI;AACF,aAAO,KAAK,IACT,KAAK,iBAAiBA,YAAW,EAAE,CAAC,EAAE,EACtC,QAAQ,EACR,IAAI,CAAC,QAAa,KAAK,aAAa,IAAI,GAAG,CAAC;AAAA,IACjD,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEQ,oBACN,IACA,OACA,IACgC;AAChC,QAAI,CAAC,GAAG,OAAQ,QAAO;AACvB,QAAI;AACF,YAAM,MAAM,KAAK,IACd;AAAA,QACC,iBAAiBA,YAAW,EAAE,CAAC,UAAU,KAAK,gBAAgB,EAAE,CAAC;AAAA,QACjE,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,mBAAmB,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC;AAAA,MAC3D,EACC,IAAI;AACP,aAAO,MAAM,KAAK,aAAa,IAAI,GAAG,IAAI;AAAA,IAC5C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,gBAAgB,IAAsB;AAC5C,WAAO,GAAG,IAAI,CAAC,MAAM,GAAGA,YAAW,CAAC,CAAC,MAAM,EAAE,KAAK,OAAO;AAAA,EAC3D;AAAA,EAEQ,mBAAmB,IAAY,UAA8B;AACnE,UAAM,SAAS,KAAK,eAAe,EAAE;AACrC,QAAI,QAAQ,YAAY,OAAQ,QAAO,OAAO;AAC9C,WAAO;AAAA,EACT;AAAA,EAEQ,WACN,IACA,OACA,6BACyB;AACzB,UAAM,SAAS,KAAK,eAAe,EAAE;AACrC,UAAM,MAA+B,CAAC;AACtC,QAAI,UAAU,6BAA6B;AACzC,iBAAW,UAAU,OAAO,KAAK,OAAO,OAAO;AAC7C,YAAI,MAAM,IAAI,KAAK,mBAAmB,IAAI,QAAQ,MAAM,MAAM,KAAK,IAAI;AAAA,IAC3E;AACA,eAAW,UAAU,OAAO,KAAK,KAAK,GAAG;AACvC,UAAI,MAAM,MAAM,MAAM;AACpB,YAAI,MAAM,IAAI,KAAK,mBAAmB,IAAI,QAAQ,MAAM,MAAM,CAAC;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,IAAY,QAAgB,OAAyB;AAC9E,QAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,UAAM,OAAO,KAAK,eAAe,EAAE,GAAG,UAAU,MAAM,GAAG;AACzD,QAAI,SAAS,UAAW,QAAO,QAAQ,IAAI;AAC3C,QAAI,SAAS,OAAQ,QAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACpF,QAAI,SAAS,SAAU,QAAO,OAAO,KAAK;AAC1C,QAAI,SAAS,SAAU,QAAO,OAAO,KAAK;AAC1C,WAAO;AAAA,EACT;AAAA,EAEQ,aACN,IACA,KACyB;AACzB,UAAM,SAAS,KAAK,eAAe,EAAE;AACrC,UAAM,aAAsC,CAAC;AAC7C,eAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,YAAM,OAAO,QAAQ,UAAU,GAAG,GAAG;AACrC,YAAM,QAAQ,IAAI,GAAG;AACrB,UAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,mBAAW,GAAG,IAAI;AAAA,MACpB,WAAW,SAAS,WAAW;AAC7B,mBAAW,GAAG,IACZ,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,UAAU;AAAA,MAChE,WAAW,SAAS,UAAU;AAC5B,mBAAW,GAAG,IAAI,OAAO,KAAK;AAAA,MAChC,WAAW,SAAS,UAAU,OAAO,UAAU,UAAU;AACvD,YAAI;AACF,qBAAW,GAAG,IAAI,KAAK,MAAM,KAAK;AAAA,QACpC,QAAQ;AACN,qBAAW,GAAG,IAAI;AAAA,QACpB;AAAA,MACF,OAAO;AACL,mBAAW,GAAG,IAAI;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,aACN,QACA,YACA,MAKkB;AAClB,UAAM,SAAS,KAAK,WAAW;AAC/B,UAAM,SAAS,OAAO,WAAW;AACjC,SAAK,SAAS,QAAQ;AAAA,MACpB;AAAA,MACA;AAAA,QACE;AAAA,QACA,YAAY,WAAW;AAAA,QACvB,gBAAgB;AAAA,UACd,qBAAqB;AAAA,UACrB,qBAAqB;AAAA,QACvB;AAAA,QACA,WAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF,CAAC;AACD,SAAK,SAAS,QAAQ,CAAC,YAAY,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC;AACvD,SAAK,SAAS,QAAQ,CAAC,WAAW,EAAE,QAAQ,OAAO,CAAC,CAAC;AACrD,UAAM,iBAAiB,EAAE,GAAG,YAAY,OAAO;AAC/C,WAAO,oBAAoB,cAAc;AACzC,WAAO;AAAA,EACT;AAAA,EAEQ,cACN,eACA,MACA;AACA,eAAW,UAAU,KAAK,IAAI,cAAc,GAAG;AAC7C,YAAM,KAAK;AACX,YAAM,aAAa,KAAK,qBAAqB,EAAE;AAC/C,UAAI,CAAC,WAAY;AACjB,UAAI,WAAW,kBAAkB,cAAe;AAChD,WAAK,aAAa,IAAI,YAAY,IAAI;AAAA,IACxC;AAAA,EACF;AAAA,EAEQ,sBACN,kBACA,MACA;AACA,UAAM,YAAY,KAAK,aAAa,CAAC;AACrC,UAAM,gBAAgB,IAAI;AAAA,MACxB,UACG,IAAI,CAAC,OAAO,IAAI,SAAS,EACzB,OAAO,CAAC,cAAmC,CAAC,CAAC,SAAS;AAAA,IAC3D;AACA,UAAM,2BACJ,OAAO,KAAK,KAAK,yBAAyB,CAAC,CAAC,EAAE,SAAS;AAEzD,eAAW,UAAU,KAAK,IAAI,cAAc,GAAG;AAC7C,YAAM,KAAK;AACX,YAAM,aAAa,KAAK,qBAAqB,EAAE;AAC/C,UAAI,CAAC,WAAY;AACjB,UAAI,WAAW,WAAW,iBAAiB,OAAQ;AAEnD,YAAM,sBACJ,WAAW,kBAAkB,iBAAiB;AAChD,YAAM,mBACJ,cAAc,OAAO,KACrB,WAAW,kBAAkB,KAAK,CAAC,cAAc,cAAc,IAAI,SAAS,CAAC;AAE/E,YAAM,WAGF,CAAC;AACL,UAAI,iBAAkB,UAAS,YAAY;AAC3C,UAAI,uBAAuB;AACzB,iBAAS,wBAAwB,KAAK;AAExC,UAAI,CAAC,SAAS,aAAa,CAAC,SAAS,sBAAuB;AAC5D,WAAK,aAAa,IAAI,YAAY,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA,EAEQ,uBAAuB,QAAkB;AAC/C,QAAI,OAAO,OAAO;AAChB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,WAAW,OAAO;AAAA,QAClB,IAAI,KAAK,gBAAgB,OAAO,WAAW,OAAO,WAAW,CAAC,CAAC;AAAA,MACjE;AACF,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,WAAW,OAAO;AAAA,MAClB,OAAO,KAAK,aAAa,OAAO,WAAW,OAAO,WAAW,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AAAA,EAEQ,gBACN,WACA,KACyB;AACzB,UAAM,KAAK,KAAK,mBAAmB,WAAW,CAAC,CAAC;AAChD,QAAI,GAAG,OAAQ,QAAO,OAAO,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC;AAClF,QAAI,QAAQ,IAAK,QAAO,EAAE,IAAI,IAAI,GAAG;AACrC,WAAO;AAAA,EACT;AAAA,EAEQ,SAAiB;AACvB,WAAO,OAAO,KAAK,UAAU,CAAC,EAAE,SAAS,IAAI,GAAG;AAAA,EAClD;AAAA,EAEQ,aAAqB;AAC3B,UAAM,YAAY,KAAK,WAAW,KAAK;AACvC,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO,OAAO,SAAS,EAAE,SAAS,IAAI,GAAG;AAAA,EAC3C;AAAA,EAEQ,qBAAqB,QAAwD;AACnF,UAAM,aAAa,OAAO,sBAAsB;AAChD,QAAI,CAAC,WAAY,QAAO;AACxB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aAAa,WAAW,gBAAgB;AAAA,MACxC,mBAAmB,WAAW,qBAAqB,CAAC;AAAA,MACpD,gBAAgB,WAAW,kBAAkB,CAAC;AAAA,IAChD;AAAA,EACF;AAAA,EAEQ,SAAS,QAAmB,KAAc;AAChD,QAAI;AACF,aAAO,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,IACjC,QAAQ;AAAA,IAAC;AAAA,EACX;AAAA,EACQ,aAAa,MAAqC;AACxD,QAAI;AACF,aAAO,KAAK,MAAM,OAAO,SAAS,WAAW,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAAA,IACpF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;AAAA,EACb,MAAM,MAAM,SAAkB,KAA6B;AACzD,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAM,KAAK,IAAI,QAAQ,WAAW,WAAW;AAC7C,QAAI,IAAI,SAAS,WAAW,SAAS,KAAK,IAAI,SAAS,SAAS,UAAU,GAAG;AAC3E,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,SACG,IAAI,aAAa,gBAAgB,IAAI,aAAa,qBACnD,QAAQ,WAAW,QACnB;AACA,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,QAAI,IAAI,aAAa,WAAW,QAAQ,WAAW,QAAQ;AACzD,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,QAAI,IAAI,aAAa,YAAY,QAAQ,WAAW,QAAQ;AAC1D,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,QACE,IAAI,aAAa,eAChB,QAAQ,WAAW,SAAS,QAAQ,WAAW,SAChD;AACA,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,QAAI,IAAI,aAAa,aAAa,QAAQ,WAAW,QAAQ;AAC3D,aAAO,IAAI,QAAQ,IAAI,EAAE,EAAE,MAAM,OAAO;AAAA,IAC1C;AACA,WAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD;AACF;AAEA,SAAS,qBACP,aAC0C;AAC1C,MAAI;AACF,UAAM,UAAU,mBAAmB,WAAW;AAC9C,UAAM,QAAQ,WAAW,KAAK,KAAK,OAAO,GAAG,CAAC,SAAS,KAAK,WAAW,CAAC,CAAC;AACzE,UAAM,YAAY,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAG5D,UAAM,UAAU,UAAU;AAC1B,QAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,MAAM,kBAAkB;AAC7D,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAjBS;;;AGl/BT,IAAM,YAAwB,8BAAO,SAAS,KAAK,MAAM,kBAAkB;AAC1E,MAAI;AACH,WAAO,MAAM,cAAc,KAAK,SAAS,GAAG;AAAA,EAC7C,UAAE;AACD,QAAI;AACH,UAAI,QAAQ,SAAS,QAAQ,CAAC,QAAQ,UAAU;AAC/C,cAAM,SAAS,QAAQ,KAAK,UAAU;AACtC,eAAO,EAAE,MAAM,OAAO,KAAK,GAAG,MAAM;AAAA,QAAC;AAAA,MACtC;AAAA,IACD,SAAS,GAAG;AACX,cAAQ,MAAM,4CAA4C,CAAC;AAAA,IAC5D;AAAA,EACD;AACD,GAb8B;AAe9B,IAAO,6CAAQ;;;ACRf,SAAS,YAAY,GAAmB;AACvC,SAAO;AAAA,IACN,MAAM,GAAG;AAAA,IACT,SAAS,GAAG,WAAW,OAAO,CAAC;AAAA,IAC/B,OAAO,GAAG;AAAA,IACV,OAAO,GAAG,UAAU,SAAY,SAAY,YAAY,EAAE,KAAK;AAAA,EAChE;AACD;AAPS;AAUT,IAAM,YAAwB,8BAAO,SAAS,KAAK,MAAM,kBAAkB;AAC1E,MAAI;AACH,WAAO,MAAM,cAAc,KAAK,SAAS,GAAG;AAAA,EAC7C,SAAS,GAAQ;AAChB,UAAM,QAAQ,YAAY,CAAC;AAC3B,WAAO,SAAS,KAAK,OAAO;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS,EAAE,+BAA+B,OAAO;AAAA,IAClD,CAAC;AAAA,EACF;AACD,GAV8B;AAY9B,IAAO,2CAAQ;;;ACzBJ,IAAM,mCAAmC;AAAA,EAE9B;AAAA,EAAyB;AAC3C;AACA,IAAO,sCAAQ;;;ACcnB,IAAM,wBAAsC,CAAC;AAKtC,SAAS,uBAAuB,MAAqC;AAC3E,wBAAsB,KAAK,GAAG,KAAK,KAAK,CAAC;AAC1C;AAFgB;AAShB,SAAS,uBACR,SACA,KACA,KACA,UACA,iBACsB;AACtB,QAAM,CAAC,MAAM,GAAG,IAAI,IAAI;AACxB,QAAM,gBAAmC;AAAA,IACxC;AAAA,IACA,KAAK,YAAY,QAAQ;AACxB,aAAO,uBAAuB,YAAY,QAAQ,KAAK,UAAU,IAAI;AAAA,IACtE;AAAA,EACD;AACA,SAAO,KAAK,SAAS,KAAK,KAAK,aAAa;AAC7C;AAfS;AAiBF,SAAS,kBACf,SACA,KACA,KACA,UACA,iBACsB;AACtB,SAAO,uBAAuB,SAAS,KAAK,KAAK,UAAU;AAAA,IAC1D,GAAG;AAAA,IACH;AAAA,EACD,CAAC;AACF;AAXgB;;;AC3ChB,IAAM,iCAAN,MAAM,gCAA8D;AAAA,EAGnE,YACU,eACA,MACT,SACC;AAHQ;AACA;AAGT,SAAK,WAAW;AAAA,EACjB;AAAA,EArBD,OAYoE;AAAA;AAAA;AAAA,EAC1D;AAAA,EAUT,UAAU;AACT,QAAI,EAAE,gBAAgB,kCAAiC;AACtD,YAAM,IAAI,UAAU,oBAAoB;AAAA,IACzC;AAEA,SAAK,SAAS;AAAA,EACf;AACD;AAEA,SAAS,oBAAoB,QAA0C;AAEtE,MACC,qCAAqC,UACrC,iCAAiC,WAAW,GAC3C;AACD,WAAO;AAAA,EACR;AAEA,aAAW,cAAc,kCAAkC;AAC1D,wBAAoB,UAAU;AAAA,EAC/B;AAEA,QAAM,kBAA+C,gCACpD,SACA,KACA,KACC;AACD,QAAI,OAAO,UAAU,QAAW;AAC/B,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC9D;AACA,WAAO,OAAO,MAAM,SAAS,KAAK,GAAG;AAAA,EACtC,GATqD;AAWrD,SAAO;AAAA,IACN,GAAG;AAAA,IACH,MAAM,SAAS,KAAK,KAAK;AACxB,YAAM,aAAyB,gCAAU,MAAM,MAAM;AACpD,YAAI,SAAS,eAAe,OAAO,cAAc,QAAW;AAC3D,gBAAM,aAAa,IAAI;AAAA,YACtB,KAAK,IAAI;AAAA,YACT,KAAK,QAAQ;AAAA,YACb,MAAM;AAAA,YAAC;AAAA,UACR;AACA,iBAAO,OAAO,UAAU,YAAY,KAAK,GAAG;AAAA,QAC7C;AAAA,MACD,GAT+B;AAU/B,aAAO,kBAAkB,SAAS,KAAK,KAAK,YAAY,eAAe;AAAA,IACxE;AAAA,EACD;AACD;AAxCS;AA0CT,SAAS,qBACR,OAC8B;AAE9B,MACC,qCAAqC,UACrC,iCAAiC,WAAW,GAC3C;AACD,WAAO;AAAA,EACR;AAEA,aAAW,cAAc,kCAAkC;AAC1D,wBAAoB,UAAU;AAAA,EAC/B;AAGA,SAAO,cAAc,MAAM;AAAA,IAC1B,mBAAyE,wBACxE,SACA,KACA,QACI;AACJ,WAAK,MAAM;AACX,WAAK,MAAM;AACX,UAAI,MAAM,UAAU,QAAW;AAC9B,cAAM,IAAI,MAAM,sDAAsD;AAAA,MACvE;AACA,aAAO,MAAM,MAAM,OAAO;AAAA,IAC3B,GAXyE;AAAA,IAazE,cAA0B,wBAAC,MAAM,SAAS;AACzC,UAAI,SAAS,eAAe,MAAM,cAAc,QAAW;AAC1D,cAAM,aAAa,IAAI;AAAA,UACtB,KAAK,IAAI;AAAA,UACT,KAAK,QAAQ;AAAA,UACb,MAAM;AAAA,UAAC;AAAA,QACR;AACA,eAAO,MAAM,UAAU,UAAU;AAAA,MAClC;AAAA,IACD,GAT0B;AAAA,IAW1B,MAAM,SAAwD;AAC7D,aAAO;AAAA,QACN;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACN;AAAA,IACD;AAAA,EACD;AACD;AAnDS;AAqDT,IAAI;AACJ,IAAI,OAAO,wCAAU,UAAU;AAC9B,kBAAgB,oBAAoB,mCAAK;AAC1C,WAAW,OAAO,wCAAU,YAAY;AACvC,kBAAgB,qBAAqB,mCAAK;AAC3C;AACA,IAAO,kCAAQ;",
7
+ "names": ["quoteIdent"]
8
+ }
@@ -81,3 +81,13 @@ same zero-cache process and durable SQLite state.
81
81
  If a future change needs more Postgres behavior, implement it in
82
82
  `DoBackend`/the SQL translator or the DO SQL backend. Do not put PGlite back in
83
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.
@@ -0,0 +1,213 @@
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.
@@ -412,9 +412,17 @@ export class ZeroDO extends DurableObject {
412
412
  }
413
413
  sql = body.sql
414
414
  const params = Array.isArray(body.params) ? body.params : []
415
- const result = await this.ctx.storage.transaction(() =>
416
- this.executeSQL(sql, params, body.track)
417
- )
415
+ // Only wrap in ctx.storage.transaction() when the call has change-tracking
416
+ // side effects (executeSQL writes BOTH the user table AND _zero_changes,
417
+ // which must commit together to keep source-tab sync flicker-free). A
418
+ // bare /exec is single-statement and ctx.storage.sql already serializes;
419
+ // the transaction wrap was adding ~2-5ms × every call, which on chat's
420
+ // 27k-stmt boot pushed orez backend startup past chat's 60s wait-for-port.
421
+ const result = body.track
422
+ ? await this.ctx.storage.transaction(() =>
423
+ this.executeSQL(sql, params, body.track)
424
+ )
425
+ : this.executeSQL(sql, params)
418
426
  return Response.json(result)
419
427
  } catch (err: any) {
420
428
  const suffix = sql ? ` while executing: ${sqlErrorSnippet(sql, err.message)}` : ''
package/src/cli.test.ts CHANGED
@@ -7,7 +7,9 @@ function runCli(
7
7
  args: string[]
8
8
  ): Promise<{ stdout: string; stderr: string; code: number }> {
9
9
  return new Promise((res) => {
10
- const child = spawn('bun', [resolve('dist/cli.js'), ...args], {
10
+ // cli-entry.js is the actual entry (cli.js only exports `main`; runMain
11
+ // is invoked from cli-entry).
12
+ const child = spawn('bun', [resolve('dist/cli-entry.js'), ...args], {
11
13
  timeout: 10_000,
12
14
  env: { ...process.env, NODE_ENV: 'test' },
13
15
  })
@@ -4678,6 +4678,15 @@ export class DoBackend {
4678
4678
  private txSnapshot: TransactionMetadataSnapshot | null = null
4679
4679
  private txDataSnapshots = new Map<string, string | null>()
4680
4680
  private txSnapshotCounter = 0
4681
+ // once the change-feed tables (_zero_changes, _zero_change_state, *watermark*)
4682
+ // are snapshotted for this tx, every subsequent tracked write would otherwise
4683
+ // re-run a sqlite_master scan + 3 table-exists probes. cache the "done" flag
4684
+ // for the duration of the tx — chat seed loops do thousands of tracked
4685
+ // writes per migration and this lookup dominated boot.
4686
+ private txChangeTablesSnapshotted = false
4687
+ // pending persist while inside a transaction. flushed on commit so we don't
4688
+ // round-trip to durable storage after every DDL statement in a migration.
4689
+ private txMetadataDirty = false
4681
4690
 
4682
4691
  constructor(
4683
4692
  doUrl: string,
@@ -4815,20 +4824,29 @@ export class DoBackend {
4815
4824
  private async persistDurableMetadata(): Promise<void> {
4816
4825
  try {
4817
4826
  await this.ensureMetadataTable()
4827
+ const rows: Array<[string, string, string, string]> = []
4818
4828
  for (const [tableName, columns] of this.schemaMetadata) {
4819
4829
  for (const [columnName, metadata] of columns) {
4820
- await this.doExecResult(
4821
- `INSERT OR REPLACE INTO ${quoteIdentifier(METADATA_TABLE)}
4822
- (kind, key, subkey, value) VALUES (?, ?, ?, ?)`,
4823
- ['schema-column', tableName, columnName, JSON.stringify(metadata)]
4824
- )
4830
+ rows.push(['schema-column', tableName, columnName, JSON.stringify(metadata)])
4825
4831
  }
4826
4832
  }
4827
4833
  for (const [name, publication] of this.publications) {
4834
+ rows.push(['publication', name, '', this.publicationToJSON(publication)])
4835
+ }
4836
+ if (rows.length === 0) return
4837
+ // single multi-row INSERT OR REPLACE per chunk. previously this was one
4838
+ // HTTP roundtrip per row, which dominated boot when migrations touched
4839
+ // many columns. SQLite caps host params near 999; 4 cols × 200 rows
4840
+ // leaves comfortable headroom.
4841
+ const CHUNK = 200
4842
+ for (let i = 0; i < rows.length; i += CHUNK) {
4843
+ const chunk = rows.slice(i, i + CHUNK)
4844
+ const placeholders = chunk.map(() => '(?, ?, ?, ?)').join(', ')
4845
+ const params: string[] = []
4846
+ for (const row of chunk) params.push(...row)
4828
4847
  await this.doExecResult(
4829
- `INSERT OR REPLACE INTO ${quoteIdentifier(METADATA_TABLE)}
4830
- (kind, key, subkey, value) VALUES (?, ?, '', ?)`,
4831
- ['publication', name, this.publicationToJSON(publication)]
4848
+ `INSERT OR REPLACE INTO ${quoteIdentifier(METADATA_TABLE)} (kind, key, subkey, value) VALUES ${placeholders}`,
4849
+ params
4832
4850
  )
4833
4851
  }
4834
4852
  } catch {}
@@ -4924,6 +4942,8 @@ export class DoBackend {
4924
4942
  this.txSnapshot = null
4925
4943
  this.txDataSnapshots.clear()
4926
4944
  this.txSnapshotCounter = 0
4945
+ this.txChangeTablesSnapshotted = false
4946
+ this.txMetadataDirty = false
4927
4947
  }
4928
4948
 
4929
4949
  private newTransactionID(): string {
@@ -4945,7 +4965,9 @@ export class DoBackend {
4945
4965
  return
4946
4966
  }
4947
4967
  await this.dropTransactionSnapshots()
4968
+ const shouldPersist = this.txMetadataDirty
4948
4969
  this.clearTransactionState()
4970
+ if (shouldPersist) await this.persistDurableMetadata()
4949
4971
  }
4950
4972
 
4951
4973
  private async rollbackTransaction(): Promise<void> {
@@ -5157,7 +5179,13 @@ export class DoBackend {
5157
5179
  changed = true
5158
5180
  }
5159
5181
  }
5160
- if (changed) await this.persistDurableMetadata()
5182
+ if (changed) {
5183
+ // inside an explicit tx we batch the persist to commit time. chat's
5184
+ // migrations open one tx and run dozens of DDL statements; persisting
5185
+ // after every one was the hot path.
5186
+ if (this.inTransaction) this.txMetadataDirty = true
5187
+ else await this.persistDurableMetadata()
5188
+ }
5161
5189
  }
5162
5190
 
5163
5191
  private applySchemaMetadataChange(change: SchemaMetadataChange): void {
@@ -5815,7 +5843,11 @@ export class DoBackend {
5815
5843
  private async snapshotTransactionTable(table: string): Promise<void> {
5816
5844
  if (!this.inTransaction || this.txDataSnapshots.has(table)) return
5817
5845
  if (table.startsWith('_orez_tx_')) return
5818
- if (!(await this.tableExistsInDo(table))) {
5846
+ // skip the sqlite_master probe when we already have schema metadata for
5847
+ // the table — registration only happens after a successful CREATE, so its
5848
+ // presence is proof the table exists. saves one /exec on the first write
5849
+ // to each table per tx, which on chat's hot mutation paths matters.
5850
+ if (!this.schemaMetadata.has(table) && !(await this.tableExistsInDo(table))) {
5819
5851
  this.txDataSnapshots.set(table, null)
5820
5852
  return
5821
5853
  }
@@ -5827,6 +5859,8 @@ export class DoBackend {
5827
5859
  }
5828
5860
 
5829
5861
  private async snapshotTransactionChangeTables(): Promise<void> {
5862
+ if (this.txChangeTablesSnapshotted) return
5863
+ this.txChangeTablesSnapshotted = true
5830
5864
  await this.snapshotTransactionTable('_zero_changes')
5831
5865
  await this.snapshotTransactionTable('_zero_change_state')
5832
5866
  const result = await this.doExecResult(
@@ -0,0 +1,53 @@
1
+ # pg-sqlite-compiler
2
+
3
+ PostgreSQL SQL → SQLite SQL compiler. Single pass over the libpg_query AST,
4
+ emitting via pgsql-deparser with SQLite-specific overrides.
5
+
6
+ ## Architecture
7
+
8
+ ```
9
+ PG SQL ──► parseSync() (libpg-query WASM, real PG parser)
10
+
11
+ PG AST (RawStmt[])
12
+
13
+ passes[] (one visitor per concern)
14
+
15
+ PG AST (mutated)
16
+
17
+ emit() (pgsql-deparser + SQLite overrides)
18
+
19
+ SQLite SQL
20
+ ```
21
+
22
+ ## Passes
23
+
24
+ Each pass is a focused visitor for one PG → SQLite concern:
25
+
26
+ - `passes/datetime.ts` — NOW(), CURRENT_TIMESTAMP, EXTRACT, DATE_TRUNC, INTERVAL
27
+ - `passes/array.ts` — ARRAY[…], @>, <@, unnest, array literals
28
+ - `passes/cast.ts` — `::type`, CAST chains, PG → SQLite type names
29
+ - `passes/json.ts` — `->`, `->>`, jsonb_set/get/path
30
+ - `passes/create_table.ts` — type mappings, BIGSERIAL → INTEGER, defaults
31
+ - `passes/insert.ts` — ON CONFLICT semantics, RETURNING (mostly native)
32
+ - `passes/catalog.ts` — pg_class / pg_attribute / information_schema rewrites
33
+
34
+ ## Testing
35
+
36
+ Two layers:
37
+
38
+ 1. **Snapshot tests** (`test/*.test.ts`) — for each pass, fixed (input, expected
39
+ output) pairs. Fast, deterministic, tracked in git.
40
+
41
+ 2. **Oracle tests** (`test/oracle.test.ts`) — spawn pgsqlite, send query to it
42
+ via PG wire, send same query through our compiler + bun:sqlite, compare
43
+ result sets. Validates semantic equivalence, not text identity. Only runs
44
+ when pgsqlite binary is available (`scripts/pgsqlite/ensure.ts`).
45
+
46
+ The pgsqlite binary itself is NOT shipped. It's a dev-time / CI oracle only.
47
+
48
+ ## Why not just use pgsqlite?
49
+
50
+ pgsqlite is a Rust server (tokio + rusqlite); doesn't run in Cloudflare workerd.
51
+ We need PG → SQLite translation as a pure-TS library that compiles into a CF
52
+ Durable Object alongside zero-cache. So we reimplement the translation in TS,
53
+ using pgsqlite as our quality oracle.