@korajs/store 1.0.0-beta.12 → 1.0.0-beta.13

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 (92) hide show
  1. package/README.md +29 -37
  2. package/dist/adapters/better-sqlite3.cjs +90 -4
  3. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  4. package/dist/adapters/better-sqlite3.d.cts +21 -12
  5. package/dist/adapters/better-sqlite3.d.ts +21 -12
  6. package/dist/adapters/better-sqlite3.js +69 -5
  7. package/dist/adapters/better-sqlite3.js.map +1 -1
  8. package/dist/adapters/indexeddb.cjs +1316 -610
  9. package/dist/adapters/indexeddb.cjs.map +1 -1
  10. package/dist/adapters/indexeddb.d.cts +29 -3
  11. package/dist/adapters/indexeddb.d.ts +29 -3
  12. package/dist/adapters/indexeddb.js +247 -221
  13. package/dist/adapters/indexeddb.js.map +1 -1
  14. package/dist/adapters/sqlite-wasm-worker.cjs +803 -62
  15. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  16. package/dist/adapters/sqlite-wasm-worker.js +480 -62
  17. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  18. package/dist/adapters/sqlite-wasm.cjs +1032 -344
  19. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  20. package/dist/adapters/sqlite-wasm.d.cts +239 -18
  21. package/dist/adapters/sqlite-wasm.d.ts +239 -18
  22. package/dist/adapters/sqlite-wasm.js +111 -5
  23. package/dist/adapters/sqlite-wasm.js.map +1 -1
  24. package/dist/backup-4NUPDEMB.js +25 -0
  25. package/dist/chunk-32EDKWMI.js +876 -0
  26. package/dist/chunk-32EDKWMI.js.map +1 -0
  27. package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
  28. package/dist/chunk-A6GFH5IG.js +36 -0
  29. package/dist/chunk-A6GFH5IG.js.map +1 -0
  30. package/dist/chunk-DUKTWVZW.js +175 -0
  31. package/dist/chunk-DUKTWVZW.js.map +1 -0
  32. package/dist/chunk-HZTDBLDB.js +607 -0
  33. package/dist/chunk-HZTDBLDB.js.map +1 -0
  34. package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
  35. package/dist/chunk-J5AGXF3D.js.map +1 -0
  36. package/dist/chunk-MCQKEHOD.js +112 -0
  37. package/dist/chunk-MCQKEHOD.js.map +1 -0
  38. package/dist/chunk-P4V375FY.js +810 -0
  39. package/dist/chunk-P4V375FY.js.map +1 -0
  40. package/dist/chunk-QDUSJMFV.js +1095 -0
  41. package/dist/chunk-QDUSJMFV.js.map +1 -0
  42. package/dist/chunk-SJP2HTYZ.js +1003 -0
  43. package/dist/chunk-SJP2HTYZ.js.map +1 -0
  44. package/dist/chunk-W5F2LWG6.js +591 -0
  45. package/dist/chunk-W5F2LWG6.js.map +1 -0
  46. package/dist/chunk-Y5LRH7KI.js +5341 -0
  47. package/dist/chunk-Y5LRH7KI.js.map +1 -0
  48. package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
  49. package/dist/index.cjs +9371 -4240
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +1053 -110
  52. package/dist/index.d.ts +1053 -110
  53. package/dist/index.js +249 -3158
  54. package/dist/index.js.map +1 -1
  55. package/dist/internal.cjs +580 -264
  56. package/dist/internal.cjs.map +1 -1
  57. package/dist/internal.d.cts +15 -85
  58. package/dist/internal.d.ts +15 -85
  59. package/dist/internal.js +10 -8
  60. package/dist/restore-FR7XIKT2.js +299 -0
  61. package/dist/restore-FR7XIKT2.js.map +1 -0
  62. package/dist/sql-builder-C4u55jKW.d.cts +608 -0
  63. package/dist/sql-builder-CGckdBDL.d.ts +608 -0
  64. package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
  65. package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
  66. package/dist/sqlite-wasm-channel-MLHLKEED.js +12 -0
  67. package/dist/store-IMWKZLWB.js +16 -0
  68. package/dist/store-IMWKZLWB.js.map +1 -0
  69. package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
  70. package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
  71. package/package.json +3 -2
  72. package/dist/backup-5S7K2Z35.js +0 -14
  73. package/dist/chunk-GSJ7EEUC.js.map +0 -1
  74. package/dist/chunk-HS6DBMST.js +0 -383
  75. package/dist/chunk-HS6DBMST.js.map +0 -1
  76. package/dist/chunk-RJIEKEN4.js +0 -751
  77. package/dist/chunk-RJIEKEN4.js.map +0 -1
  78. package/dist/chunk-TUR7RGEP.js +0 -84
  79. package/dist/chunk-TUR7RGEP.js.map +0 -1
  80. package/dist/chunk-VDBJQ75K.js +0 -78
  81. package/dist/chunk-VDBJQ75K.js.map +0 -1
  82. package/dist/chunk-YT7MRFCP.js +0 -687
  83. package/dist/chunk-YT7MRFCP.js.map +0 -1
  84. package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
  85. package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
  86. package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
  87. package/dist/types-BOO9EPt1.d.ts +0 -170
  88. package/dist/types-C_GDavG-.d.cts +0 -170
  89. /package/dist/{backup-5S7K2Z35.js.map → backup-4NUPDEMB.js.map} +0 -0
  90. /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
  91. /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
  92. /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-MLHLKEED.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal.ts","../src/serialization/serializer.ts","../src/serialization/richtext-serializer.ts","../src/serialization/op-data-encoding.ts","../src/query/sql-builder.ts","../src/errors.ts","../src/lww/row-version.ts","../src/mutations/execute-insert.ts","../src/lww/field-versions.ts","../src/store/sequence-allocator.ts","../src/mutations/resolve-causal-deps.ts","../src/mutations/execute-update.ts","../src/state-machine/state-validator.ts","../src/mutations/execute-delete.ts"],"sourcesContent":["// Internal exports — shared within @kora packages but NOT part of the public API.\n// Other @kora packages can import from '@korajs/store/internal' if needed.\n\nexport {\n\tserializeOperation,\n\tdeserializeOperation,\n\tdeserializeOperationWithCollection,\n\tserializeRecord,\n\tdeserializeRecord,\n} from './serialization/serializer'\n\nexport {\n\tbuildSelectQuery,\n\tbuildCountQuery,\n\tbuildInsertQuery,\n\tbuildUpdateQuery,\n\tbuildLwwUpdateQuery,\n\tbuildSoftDeleteQuery,\n\tbuildLwwSoftDeleteQuery,\n\tbuildWhereClause,\n} from './query/sql-builder'\nexport {\n\tserializeRowVersion,\n\trowVersionFromRecord,\n\tisIncomingNewerThanRow,\n} from './lww/row-version'\nexport type { SqlQuery } from './query/sql-builder'\n\nexport type {\n\tOperationRow,\n\tRawCollectionRow,\n\tMetaRow,\n\tVersionVectorRow,\n\tSubscription,\n} from './types'\n\nexport { executeInsert } from './mutations/execute-insert'\nexport { executeUpdate } from './mutations/execute-update'\nexport { executeDelete } from './mutations/execute-delete'\nexport { resolveCausalDeps } from './mutations/resolve-causal-deps'\nexport type { ExecuteDeleteOptions } from './mutations/execute-delete'\nexport type { LocalMutationContext } from './mutations/types'\n","import { HybridLogicalClock } from '@korajs/core'\nimport type { CollectionDefinition, FieldDescriptor, Operation } from '@korajs/core'\nimport type { CollectionRecord, OperationRow, RawCollectionRow } from '../types'\nimport { decodeRichtext, encodeRichtext } from './richtext-serializer'\n\n/**\n * Serialize a JS record to SQL-compatible values for INSERT/UPDATE.\n * Transforms: boolean → 0/1, array → JSON string, richtext → Yjs binary update.\n *\n * @param data - The record data with JS-native types\n * @param fields - The field descriptors from the schema\n * @returns An object with SQL-compatible values\n */\nexport function serializeRecord(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tconst descriptor = fields[key]\n\t\tif (!descriptor) {\n\t\t\tresult[key] = value\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = serializeValue(value, descriptor)\n\t}\n\treturn result\n}\n\n/**\n * Deserialize a SQL row to JS-native types for the application layer.\n * Transforms: 0/1 → boolean, JSON string → array, strips _deleted, maps _created_at/_updated_at.\n *\n * @param row - The raw SQL row\n * @param fields - The field descriptors from the schema\n * @returns A CollectionRecord with JS-native types\n */\nexport function deserializeRecord(\n\trow: RawCollectionRow,\n\tfields: Record<string, FieldDescriptor>,\n): CollectionRecord {\n\tconst result: CollectionRecord = {\n\t\tid: row.id,\n\t\tcreatedAt: row._created_at,\n\t\tupdatedAt: row._updated_at,\n\t}\n\n\tfor (const [key, descriptor] of Object.entries(fields)) {\n\t\tconst rawValue = row[key]\n\t\tif (rawValue === undefined || rawValue === null) {\n\t\t\tresult[key] = rawValue ?? null\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = deserializeValue(rawValue, descriptor)\n\t}\n\n\treturn result\n}\n\n/**\n * Internal key used to embed atomicOps metadata in the data JSON column.\n * This avoids adding a new column to the ops table (no migration needed).\n */\nconst ATOMIC_OPS_KEY = '__kora_atomic_ops__'\n\n/**\n * Internal key used to embed transactionId in the data JSON column.\n */\nconst TX_ID_KEY = '__kora_tx_id__'\n\n/**\n * Internal key used to embed mutationName in the data JSON column.\n */\nconst MUTATION_NAME_KEY = '__kora_mutation__'\n\n/**\n * Serialize an Operation to a row for the operations log table.\n *\n * @param op - The operation to serialize\n * @returns An OperationRow suitable for SQL INSERT\n */\nexport function serializeOperation(op: Operation): OperationRow {\n\tconst hasMetadata = op.transactionId !== undefined || op.mutationName !== undefined\n\tlet dataPayload: Record<string, unknown> | null = null\n\tif (op.data) {\n\t\t// Embed metadata in the data JSON when present\n\t\tdataPayload = { ...op.data }\n\t\tif (op.atomicOps !== undefined && Object.keys(op.atomicOps).length > 0) {\n\t\t\tdataPayload[ATOMIC_OPS_KEY] = op.atomicOps\n\t\t}\n\t\tif (op.transactionId !== undefined) {\n\t\t\tdataPayload[TX_ID_KEY] = op.transactionId\n\t\t}\n\t\tif (op.mutationName !== undefined) {\n\t\t\tdataPayload[MUTATION_NAME_KEY] = op.mutationName\n\t\t}\n\t} else if (hasMetadata) {\n\t\t// For delete operations (data is null), we still need to store metadata\n\t\tdataPayload = {}\n\t\tif (op.transactionId !== undefined) {\n\t\t\tdataPayload[TX_ID_KEY] = op.transactionId\n\t\t}\n\t\tif (op.mutationName !== undefined) {\n\t\t\tdataPayload[MUTATION_NAME_KEY] = op.mutationName\n\t\t}\n\t}\n\n\treturn {\n\t\tid: op.id,\n\t\tnode_id: op.nodeId,\n\t\ttype: op.type,\n\t\trecord_id: op.recordId,\n\t\tdata: dataPayload ? JSON.stringify(dataPayload) : null,\n\t\tprevious_data: op.previousData ? JSON.stringify(op.previousData) : null,\n\t\ttimestamp: HybridLogicalClock.serialize(op.timestamp),\n\t\tsequence_number: op.sequenceNumber,\n\t\tcausal_deps: JSON.stringify(op.causalDeps),\n\t\tschema_version: op.schemaVersion,\n\t}\n}\n\n/**\n * Deserialize a row from the operations log table back to an Operation.\n *\n * @param row - The raw operation row from SQL\n * @returns The deserialized Operation object\n */\nexport function deserializeOperation(row: OperationRow): Operation {\n\tlet data: Record<string, unknown> | null = null\n\tlet atomicOps: Record<string, unknown> | undefined\n\tlet transactionId: string | undefined\n\tlet mutationName: string | undefined\n\n\tif (row.data) {\n\t\tconst parsed = JSON.parse(row.data) as Record<string, unknown>\n\t\t// Extract embedded metadata keys\n\t\tif (ATOMIC_OPS_KEY in parsed) {\n\t\t\tatomicOps = parsed[ATOMIC_OPS_KEY] as Record<string, unknown>\n\t\t}\n\t\tif (TX_ID_KEY in parsed) {\n\t\t\ttransactionId = parsed[TX_ID_KEY] as string\n\t\t}\n\t\tif (MUTATION_NAME_KEY in parsed) {\n\t\t\tmutationName = parsed[MUTATION_NAME_KEY] as string\n\t\t}\n\t\t// Remove metadata keys from data\n\t\tconst { [ATOMIC_OPS_KEY]: _a, [TX_ID_KEY]: _t, [MUTATION_NAME_KEY]: _m, ...rest } = parsed\n\t\tdata = Object.keys(rest).length > 0 ? rest : null\n\t}\n\n\treturn {\n\t\tid: row.id,\n\t\tnodeId: row.node_id,\n\t\ttype: row.type as Operation['type'],\n\t\tcollection: '', // Collection name is derived from the table name by the caller\n\t\trecordId: row.record_id,\n\t\tdata,\n\t\tpreviousData: row.previous_data\n\t\t\t? (JSON.parse(row.previous_data) as Record<string, unknown>)\n\t\t\t: null,\n\t\ttimestamp: HybridLogicalClock.deserialize(row.timestamp),\n\t\tsequenceNumber: row.sequence_number,\n\t\tcausalDeps: JSON.parse(row.causal_deps) as string[],\n\t\tschemaVersion: row.schema_version,\n\t\t...(atomicOps !== undefined ? { atomicOps: atomicOps as Operation['atomicOps'] } : {}),\n\t\t...(transactionId !== undefined ? { transactionId } : {}),\n\t\t...(mutationName !== undefined ? { mutationName } : {}),\n\t}\n}\n\n/**\n * Deserialize an operation row with collection name already known.\n */\nexport function deserializeOperationWithCollection(\n\trow: OperationRow,\n\tcollection: string,\n): Operation {\n\tconst op = deserializeOperation(row)\n\treturn { ...op, collection }\n}\n\nfunction serializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value ? 1 : 0\n\t\tcase 'array':\n\t\tcase 'object':\n\t\tcase 'json':\n\t\tcase 'blob':\n\t\t\treturn JSON.stringify(value)\n\t\tcase 'richtext':\n\t\t\t// May be a record-shaped value (string/bytes) or an op-data value\n\t\t\t// (tagged { $koraBytes }) — encodeRichtext accepts every form.\n\t\t\treturn encodeRichtext(value as Parameters<typeof encodeRichtext>[0])\n\t\tdefault:\n\t\t\treturn value\n\t}\n}\n\nfunction deserializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value === 1 || value === true\n\t\tcase 'array':\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn JSON.parse(value) as unknown[]\n\t\t\t}\n\t\t\treturn value\n\t\tcase 'object':\n\t\tcase 'json':\n\t\tcase 'blob':\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn JSON.parse(value) as unknown\n\t\t\t}\n\t\t\treturn value\n\t\tcase 'richtext':\n\t\t\treturn decodeRichtext(value)\n\t\tdefault:\n\t\t\treturn value\n\t}\n}\n","import * as Y from 'yjs'\nimport {\n\ttype KoraBytesValue,\n\tdecodeRichtextOpDataValue,\n\tisKoraBytesValue,\n\tisLegacyNumericByteObject,\n} from './op-data-encoding'\n\nconst TEXT_KEY = 'content'\n\nexport type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined\n\n/**\n * Encodes richtext values into Yjs document updates.\n *\n * Also accepts the tagged `{ $koraBytes }` form that binary richtext values\n * take inside `op.data` (and, for old dev databases, the pre-fix numeric-key\n * object shape), so every consumer that writes op-data values to columns\n * inherits the decoding. Plain strings keep their exact historical behavior:\n * encoded as a fresh Yjs document containing the string.\n */\nexport function encodeRichtext(value: RichtextInput | KoraBytesValue): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\t// Deterministic clientId so the same string encodes to the same bytes on every\n\t\t// device (a plain string is a replacement, not a collaborative actor). A random\n\t\t// clientId would make a string-derived richtext column diverge byte-wise across\n\t\t// replicas even when the text is identical.\n\t\tdoc.clientID = 0\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tif (isKoraBytesValue(value) || isLegacyNumericByteObject(value)) {\n\t\tconst decoded = decodeRichtextOpDataValue(value)\n\t\t// Both accepted shapes decode to bytes; strings were handled above.\n\t\treturn decoded instanceof Uint8Array ? decoded : null\n\t}\n\n\tthrow new Error('Richtext value must be a string, Uint8Array, ArrayBuffer, null, or undefined.')\n}\n\n/**\n * Decodes driver-provided richtext values into Uint8Array.\n */\nexport function decodeRichtext(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tthrow new Error(\n\t\t'Richtext storage value must be Uint8Array, ArrayBuffer, Buffer, null, or undefined.',\n\t)\n}\n\n/**\n * Reads plain text from a richtext Yjs state update.\n */\nexport function richtextToPlainText(value: RichtextInput): string {\n\tconst encoded = encodeRichtext(value)\n\tif (!encoded) return ''\n\n\tconst doc = new Y.Doc()\n\tY.applyUpdate(doc, encoded)\n\treturn doc.getText(TEXT_KEY).toString()\n}\n\n/**\n * Compares two richtext values by decoded plain text.\n * Yjs update bytes are not stable across separate encodes of the same string.\n */\nexport function richtextStatesEqual(a: unknown, b: unknown): boolean {\n\ttry {\n\t\treturn richtextToPlainText(a as RichtextInput) === richtextToPlainText(b as RichtextInput)\n\t} catch {\n\t\treturn false\n\t}\n}\n","import {\n\ttype KoraBytesValue,\n\tbase64ToBytes,\n\tbytesToBase64,\n\tdecodeBytesFromOpData,\n\tencodeBytesForOpData,\n\tisKoraBytesValue,\n\tisLegacyNumericByteObject,\n} from '@korajs/core'\nimport type { FieldDescriptor } from '@korajs/core'\n\n/**\n * The canonical binary op-data encoding lives in `@korajs/core` because\n * `op.data` is a core concept shared by store (creation, persistence) and merge\n * (CRDT merge). These re-exports keep existing store-internal import paths\n * working while the single source of truth is the core module.\n */\nexport type { KoraBytesValue }\nexport { base64ToBytes, bytesToBase64, isKoraBytesValue, isLegacyNumericByteObject }\n\n/** @deprecated Use {@link encodeRichtextForOpData}; kept for existing imports. */\nexport const encodeRichtextForOpData = encodeBytesForOpData\n/** @deprecated Use {@link decodeRichtextOpDataValue}; kept for existing imports. */\nexport const decodeRichtextOpDataValue = decodeBytesFromOpData\n\n/**\n * Apply {@link encodeBytesForOpData} to every richtext-typed field in an\n * operation's data payload. Called at operation creation, BEFORE the payload\n * is content-hashed, so the hash input, persisted JSON, wire payload, and the\n * value the merge engine sees are the identical canonical value. Non-richtext\n * fields are untouched.\n */\nexport function encodeRichtextFieldsForOpData(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = { ...data }\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (fields[key]?.kind !== 'richtext') {\n\t\t\tcontinue\n\t\t}\n\t\tif (typeof value === 'string' || value instanceof Uint8Array || value instanceof ArrayBuffer) {\n\t\t\tresult[key] = encodeBytesForOpData(value)\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Reverse of {@link encodeRichtextFieldsForOpData}: decode tagged (or legacy\n * numeric-key) richtext values in an op-data payload back to record-shaped\n * values (string or Uint8Array). Null/undefined and plain strings pass\n * through, preserving pre-fix behavior exactly.\n */\nexport function decodeRichtextFieldsFromOpData(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = { ...data }\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (fields[key]?.kind !== 'richtext' || value === null || value === undefined) {\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = decodeBytesFromOpData(value)\n\t}\n\treturn result\n}\n","import type { CollectionDefinition, FieldDescriptor } from '@korajs/core'\nimport { quoteIdent } from '@korajs/core'\nimport { QueryError } from '../errors'\nimport { lwwVersionWhereClause } from '../lww/row-version'\nimport type { QueryDescriptor, WhereOperators } from '../types'\n\n/**\n * Result of building a SQL query: the parameterized SQL string and its bound values.\n */\nexport interface SqlQuery {\n\tsql: string\n\tparams: unknown[]\n}\n\n/**\n * Build a SELECT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0` to exclude soft-deleted records.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query\n */\nexport function buildSelectQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT * FROM ${quoteIdent(descriptor.collection)}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\t// Always filter out soft-deleted records\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\tif (descriptor.orderBy.length > 0) {\n\t\tconst orderParts = descriptor.orderBy.map((o) => {\n\t\t\tvalidateFieldName(o.field, fields)\n\t\t\treturn `${quoteIdent(o.field)} ${o.direction.toUpperCase()}`\n\t\t})\n\t\tparts.push(`ORDER BY ${orderParts.join(', ')}`)\n\t}\n\n\tif (descriptor.limit !== undefined) {\n\t\tparts.push(`LIMIT ${descriptor.limit}`)\n\t}\n\n\tif (descriptor.offset !== undefined) {\n\t\tparts.push(`OFFSET ${descriptor.offset}`)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build a COUNT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0`.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query that returns { count: number }\n */\nexport function buildCountQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT COUNT(*) as count FROM ${quoteIdent(descriptor.collection)}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build an INSERT query for a collection record.\n *\n * @param collection - The collection name\n * @param record - The record data (already serialized with id, _created_at, _updated_at)\n * @returns A parameterized SQL query\n */\nexport function buildInsertQuery(collection: string, record: Record<string, unknown>): SqlQuery {\n\tconst columns = Object.keys(record)\n\tconst placeholders = columns.map(() => '?')\n\tconst params = Object.values(record)\n\n\tconst quotedColumns = columns.map((col) => quoteIdent(col))\n\tconst sql = `INSERT INTO ${quoteIdent(collection)} (${quotedColumns.join(', ')}) VALUES (${placeholders.join(', ')})`\n\treturn { sql, params }\n}\n\n/**\n * Build an UPDATE query for a collection record.\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param changes - The fields to update (already serialized)\n * @returns A parameterized SQL query\n */\nexport function buildUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tchanges: Record<string, unknown>,\n): SqlQuery {\n\tconst setClauses = Object.keys(changes).map((col) => `${quoteIdent(col)} = ?`)\n\tconst params = [...Object.values(changes), id]\n\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ?`\n\treturn { sql, params }\n}\n\n/**\n * Build a field-level fast-forward UPDATE: writes the changed field values\n * unconditionally (the caller has already proven per-field that the local value\n * still equals the base the remote wrote from, so the remote is the newest known\n * writer of those fields), while advancing the row's `_version` / `_updated_at`\n * watermark MONOTONICALLY — never regressing it below its current value.\n *\n * This is what makes per-field LWW correct on a per-row `_version` column: a\n * concurrent edit to a different field may have pushed `_version` past this\n * operation's timestamp, but that must not cause this operation's own field\n * change to be dropped, nor may materializing it move the watermark backward\n * (which would let a genuinely stale later operation win).\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param fieldChanges - Serialized field values to write unconditionally (must NOT include _version / _updated_at)\n * @param remoteVersion - The operation's serialized row version (lexicographically comparable)\n * @param wallTime - The operation's wall-clock time in ms\n * @param options - Optional extras. `maxCreatedAt` advances `_created_at` to the\n * greater of its current value and the given wall time — used for insert\n * collisions so every node converges on the max insert wall time regardless\n * of arrival order.\n */\nexport function buildFieldFastForwardUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tfieldChanges: Record<string, unknown>,\n\tremoteVersion: string,\n\twallTime: number,\n\toptions?: { maxCreatedAt?: number },\n): SqlQuery {\n\tconst fieldClauses = Object.keys(fieldChanges).map((col) => `${quoteIdent(col)} = ?`)\n\t// _version is a lexicographically-sortable string; _updated_at is a number.\n\t// Keep the greater of the current and incoming value for each (monotonic).\n\tconst setClauses = [\n\t\t...fieldClauses,\n\t\t'_version = CASE WHEN _version >= ? THEN _version ELSE ? END',\n\t\t'_updated_at = CASE WHEN _updated_at >= ? THEN _updated_at ELSE ? END',\n\t]\n\tconst params: unknown[] = [\n\t\t...Object.values(fieldChanges),\n\t\tremoteVersion,\n\t\tremoteVersion,\n\t\twallTime,\n\t\twallTime,\n\t]\n\tif (options?.maxCreatedAt !== undefined) {\n\t\tsetClauses.push('_created_at = CASE WHEN _created_at >= ? THEN _created_at ELSE ? END')\n\t\tparams.push(options.maxCreatedAt, options.maxCreatedAt)\n\t}\n\tparams.push(id)\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ?`\n\treturn { sql, params }\n}\n\n/**\n * Build a soft-delete query (SET _deleted = 1).\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param updatedAt - The timestamp to set on _updated_at\n * @returns A parameterized SQL query\n */\nexport function buildSoftDeleteQuery(\n\tcollection: string,\n\tid: string,\n\tupdatedAt: number,\n\tversion?: string,\n): SqlQuery {\n\tif (version !== undefined) {\n\t\treturn {\n\t\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ?`,\n\t\t\tparams: [updatedAt, version, id],\n\t\t}\n\t}\n\treturn {\n\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ? WHERE id = ?`,\n\t\tparams: [updatedAt, id],\n\t}\n}\n\n/**\n * Build an UPDATE that applies only when the row is missing or older than `remoteVersion`\n * (serialized HLC). Prevents stale remote sync from overwriting newer local materialized state.\n */\nexport function buildLwwUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tchanges: Record<string, unknown>,\n\tremoteVersion: string,\n): SqlQuery {\n\tconst setClauses = Object.keys(changes).map((col) => `${quoteIdent(col)} = ?`)\n\tconst lww = lwwVersionWhereClause(remoteVersion)\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ? AND ${lww.sql}`\n\tconst params = [...Object.values(changes), id, ...lww.params]\n\treturn { sql, params }\n}\n\n/**\n * Build a soft-delete that applies only when the row is older than `remoteVersion`.\n */\nexport function buildLwwSoftDeleteQuery(\n\tcollection: string,\n\tid: string,\n\tupdatedAt: number,\n\tversion: string,\n): SqlQuery {\n\tconst lww = lwwVersionWhereClause(version)\n\treturn {\n\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ? AND ${lww.sql}`,\n\t\tparams: [updatedAt, version, id, ...lww.params],\n\t}\n}\n\n/**\n * Build a WHERE clause from conditions, validating field names against the schema.\n *\n * @param where - The where conditions\n * @param fields - The field descriptors from the collection schema\n * @returns The SQL WHERE clause string and params, or null if no conditions\n */\nexport function buildWhereClause(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery | null {\n\tconst params: unknown[] = []\n\tconst result = buildWhereClauseParts(where, fields, params)\n\tif (!result) return null\n\treturn { sql: result, params }\n}\n\n// --- Internal helpers ---\n\nconst VALID_OPERATORS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in'])\n\nfunction buildWhereClauseParts(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n\tparams: unknown[],\n): string | null {\n\tconst conditions: string[] = []\n\n\tfor (const [fieldName, value] of Object.entries(where)) {\n\t\tvalidateFieldName(fieldName, fields)\n\t\tconst descriptor = fields[fieldName]\n\n\t\tif (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t// Operator object: { $gt: 5, $lt: 10 }\n\t\t\tconst ops = value as WhereOperators\n\t\t\tfor (const [op, opValue] of Object.entries(ops)) {\n\t\t\t\tif (!VALID_OPERATORS.has(op)) {\n\t\t\t\t\tthrow new QueryError(`Unknown operator \"${op}\" on field \"${fieldName}\"`, {\n\t\t\t\t\t\tfield: fieldName,\n\t\t\t\t\t\toperator: op,\n\t\t\t\t\t\tvalidOperators: [...VALID_OPERATORS],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconditions.push(buildOperatorCondition(fieldName, op, opValue, descriptor, params))\n\t\t\t}\n\t\t} else {\n\t\t\t// Shorthand: { completed: false } means { completed: { $eq: false } }\n\t\t\tconditions.push(buildOperatorCondition(fieldName, '$eq', value, descriptor, params))\n\t\t}\n\t}\n\n\tif (conditions.length === 0) return null\n\treturn conditions.join(' AND ')\n}\n\nfunction buildOperatorCondition(\n\tfieldName: string,\n\toperator: string,\n\tvalue: unknown,\n\tdescriptor: FieldDescriptor | undefined,\n\tparams: unknown[],\n): string {\n\t// Serialize boolean values to 0/1 for SQL comparison\n\tconst sqlValue =\n\t\tdescriptor?.kind === 'boolean' && typeof value === 'boolean' ? (value ? 1 : 0) : value\n\n\tconst column = quoteIdent(fieldName)\n\n\tswitch (operator) {\n\t\tcase '$eq':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${column} IS NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} = ?`\n\t\tcase '$ne':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${column} IS NOT NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} != ?`\n\t\tcase '$gt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} > ?`\n\t\tcase '$gte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} >= ?`\n\t\tcase '$lt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} < ?`\n\t\tcase '$lte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} <= ?`\n\t\tcase '$in': {\n\t\t\tif (!Array.isArray(sqlValue)) {\n\t\t\t\tthrow new QueryError(`$in operator requires an array value for field \"${fieldName}\"`, {\n\t\t\t\t\tfield: fieldName,\n\t\t\t\t\treceived: typeof sqlValue,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst placeholders = sqlValue.map(() => '?')\n\t\t\tfor (const item of sqlValue) {\n\t\t\t\tparams.push(\n\t\t\t\t\tdescriptor?.kind === 'boolean' && typeof item === 'boolean' ? (item ? 1 : 0) : item,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn `${column} IN (${placeholders.join(', ')})`\n\t\t}\n\t\tdefault:\n\t\t\tthrow new QueryError(`Unknown operator \"${operator}\"`, { operator })\n\t}\n}\n\nfunction validateFieldName(fieldName: string, fields: Record<string, FieldDescriptor>): void {\n\t// Allow schema fields plus metadata fields that map to query-able columns\n\tconst allowedFields = new Set([\n\t\t...Object.keys(fields),\n\t\t'id',\n\t\t'createdAt',\n\t\t'updatedAt',\n\t\t'_created_at',\n\t\t'_updated_at',\n\t])\n\tif (!allowedFields.has(fieldName)) {\n\t\tthrow new QueryError(\n\t\t\t`Unknown field \"${fieldName}\" in query. Available fields: ${[...allowedFields].join(', ')}`,\n\t\t\t{ field: fieldName },\n\t\t)\n\t}\n}\n","import { KoraError } from '@korajs/core'\n\n/**\n * Thrown when a query is invalid (bad field names, invalid operators, etc.).\n */\nexport class QueryError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'QUERY_ERROR', context)\n\t\tthis.name = 'QueryError'\n\t}\n}\n\n/**\n * Thrown when a record is not found by ID (findById, update, delete on missing record).\n */\nexport class RecordNotFoundError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(`Record \"${recordId}\" not found in collection \"${collection}\"`, 'RECORD_NOT_FOUND', {\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t})\n\t\tthis.name = 'RecordNotFoundError'\n\t}\n}\n\n/**\n * Thrown inside `applyRemoteOperation` when the row's version state changed\n * between the caller's snapshot read and the guarded write (a concurrent local\n * mutation landed in the window). The transaction is rolled back with nothing\n * written; the caller re-reads fresh state, recomputes its merge, and retries.\n * This is the optimistic-concurrency guard that keeps merge-engine results\n * (richtext, add-wins arrays, constraint resolutions) from clobbering newer\n * local edits they never saw.\n */\nexport class OptimisticLockError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(\n\t\t\t`Row version changed while merging record \"${recordId}\" in \"${collection}\"; retry with fresh state`,\n\t\t\t'OPTIMISTIC_LOCK',\n\t\t\t{ collection, recordId },\n\t\t)\n\t\tthis.name = 'OptimisticLockError'\n\t}\n}\n\n/**\n * Thrown when a storage adapter operation fails.\n */\nexport class AdapterError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'ADAPTER_ERROR', context)\n\t\tthis.name = 'AdapterError'\n\t}\n}\n\n/**\n * Thrown when an operation is attempted on a store that has not been opened.\n */\nexport class StoreNotOpenError extends KoraError {\n\tconstructor() {\n\t\tsuper('Store is not open. Call store.open() before performing operations.', 'STORE_NOT_OPEN')\n\t\tthis.name = 'StoreNotOpenError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).\n */\nexport class WorkerInitError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Worker initialization failed: ${message}`, 'WORKER_INIT_ERROR', context)\n\t\tthis.name = 'WorkerInitError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker does not respond within the configured timeout.\n */\nexport class WorkerTimeoutError extends KoraError {\n\tconstructor(operation: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\t`Worker did not respond within ${timeoutMs}ms for operation \"${operation}\"`,\n\t\t\t'WORKER_TIMEOUT',\n\t\t\t{ operation, timeoutMs },\n\t\t)\n\t\tthis.name = 'WorkerTimeoutError'\n\t}\n}\n\n/**\n * Thrown when a follower tab cannot reach a live leader tab over the multi-tab\n * broadcast channel (the leader tab was closed, crashed, or is unresponsive).\n * Distinct from {@link WorkerTimeoutError}: this fails fast on a confirmed-absent\n * leader instead of waiting out the full RPC timeout.\n */\nexport class NoLeaderError extends KoraError {\n\tconstructor(operation: string) {\n\t\tsuper(\n\t\t\t`No live leader tab is answering multi-tab storage RPC for operation \"${operation}\"`,\n\t\t\t'NO_LEADER',\n\t\t\t{ operation },\n\t\t)\n\t\tthis.name = 'NoLeaderError'\n\t}\n}\n\n/**\n * Thrown when IndexedDB persistence operations fail (serialize/deserialize).\n */\nexport class PersistenceError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Persistence error: ${message}`, 'PERSISTENCE_ERROR', context)\n\t\tthis.name = 'PersistenceError'\n\t}\n}\n","import { type HLCTimestamp, HybridLogicalClock } from '@korajs/core'\nimport type { RawCollectionRow } from '../types'\n\n/** Empty _version means legacy rows (compare by wallTime only via fallback). */\nexport const EMPTY_ROW_VERSION = ''\n\n/**\n * Serialize an HLC timestamp for storage in the `_version` column.\n * Lexicographic order matches {@link HybridLogicalClock.compare}.\n */\nexport function serializeRowVersion(timestamp: HLCTimestamp): string {\n\treturn HybridLogicalClock.serialize(timestamp)\n}\n\n/**\n * Read the version stamp stored on a materialized row.\n * Legacy rows without `_version` fall back to wallTime with logical 0.\n */\nexport function rowVersionFromRecord(\n\trow: Pick<RawCollectionRow, '_updated_at' | '_version'>,\n): HLCTimestamp {\n\tconst version = row._version\n\tif (typeof version === 'string' && version.length > 0) {\n\t\treturn HybridLogicalClock.deserialize(version)\n\t}\n\treturn { wallTime: row._updated_at, logical: 0, nodeId: '' }\n}\n\n/**\n * Returns true when `incoming` is strictly newer than the version on `row`.\n */\nexport function isIncomingNewerThanRow(\n\tincoming: HLCTimestamp,\n\trow: Pick<RawCollectionRow, '_updated_at' | '_version'>,\n): boolean {\n\treturn HybridLogicalClock.compare(incoming, rowVersionFromRecord(row)) > 0\n}\n\n/**\n * SQL fragment and params for LWW guards: apply only when row is missing or older.\n * Uses serialized `_version` for total order (matches HLC compare).\n */\nexport function lwwVersionWhereClause(remoteVersion: string): { sql: string; params: string[] } {\n\treturn {\n\t\tsql: '(_version = ? OR _version < ?)',\n\t\tparams: [EMPTY_ROW_VERSION, remoteVersion],\n\t}\n}\n","import {\n\tcreateOperation,\n\tgenerateUUIDv7,\n\ttransformSecretFieldsForWrite,\n\tvalidateRecord,\n} from '@korajs/core'\nimport { fieldVersionsForFields, serializeFieldVersions } from '../lww/field-versions'\nimport { serializeRowVersion } from '../lww/row-version'\nimport { buildInsertQuery } from '../query/sql-builder'\nimport { encodeRichtextFieldsForOpData } from '../serialization/op-data-encoding'\nimport { serializeOperation, serializeRecord } from '../serialization/serializer'\nimport { allocateNextSequenceInTransaction } from '../store/sequence-allocator'\nimport type { CollectionRecord } from '../types'\nimport { resolveCausalDeps } from './resolve-causal-deps'\nimport type { LocalMutationContext } from './types'\n\n/**\n * Insert a record and persist the operation log entry atomically.\n */\nexport async function executeInsert(\n\tctx: LocalMutationContext,\n\tdata: Record<string, unknown>,\n): Promise<CollectionRecord> {\n\tconst validated = validateRecord(ctx.collection, ctx.definition, data, 'insert')\n\tconst recordId = generateUUIDv7()\n\n\tfor (const [fieldName, descriptor] of Object.entries(ctx.definition.fields)) {\n\t\tif (descriptor.auto && descriptor.kind === 'timestamp') {\n\t\t\tvalidated[fieldName] = Date.now()\n\t\t}\n\t}\n\n\t// Transform secret fields to their at-rest form (hash/ciphertext) BEFORE the\n\t// operation is built, so plaintext never enters the op log, store, or sync.\n\tconst writeData = await transformSecretFieldsForWrite(\n\t\tvalidated,\n\t\tctx.definition,\n\t\tctx.secretKeyProvider,\n\t)\n\n\tconst causalDeps = resolveCausalDeps(ctx)\n\n\tlet operation!: Awaited<ReturnType<typeof createOperation>>\n\tlet record!: Record<string, unknown>\n\n\tawait ctx.adapter.transaction(async (tx) => {\n\t\tconst sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId)\n\t\toperation = await createOperation(\n\t\t\t{\n\t\t\t\tnodeId: ctx.nodeId,\n\t\t\t\ttype: 'insert',\n\t\t\t\tcollection: ctx.collection,\n\t\t\t\trecordId,\n\t\t\t\t// Binary richtext values are tagged as canonical JSON BEFORE the\n\t\t\t\t// operation is content-hashed, so the hash input, persisted JSON,\n\t\t\t\t// and wire payload are the identical value.\n\t\t\t\tdata: encodeRichtextFieldsForOpData(writeData, ctx.definition.fields),\n\t\t\t\tpreviousData: null,\n\t\t\t\tsequenceNumber,\n\t\t\t\tcausalDeps,\n\t\t\t\tschemaVersion: ctx.schema.version,\n\t\t\t},\n\t\t\tctx.clock,\n\t\t)\n\t\tctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction)\n\n\t\tconst serializedData = serializeRecord(writeData, ctx.definition.fields)\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\trecord = {\n\t\t\tid: recordId,\n\t\t\t...serializedData,\n\t\t\t_created_at: operation.timestamp.wallTime,\n\t\t\t_updated_at: operation.timestamp.wallTime,\n\t\t\t_version: version,\n\t\t\t// Stamp every inserted field with this operation's version so later\n\t\t\t// per-field LWW compares against a real writer, not the fallback.\n\t\t\t_field_versions: serializeFieldVersions(\n\t\t\t\tfieldVersionsForFields(Object.keys(serializedData), version),\n\t\t\t),\n\t\t}\n\n\t\tconst builtInsert = buildInsertQuery(ctx.collection, record)\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${ctx.collection}`,\n\t\t\tserializeOperation(operation) as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait tx.execute(builtInsert.sql, builtInsert.params)\n\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t})\n\n\tctx.onMutation(ctx.collection, operation)\n\n\treturn {\n\t\tid: recordId,\n\t\t...writeData,\n\t\tcreatedAt: operation.timestamp.wallTime,\n\t\tupdatedAt: operation.timestamp.wallTime,\n\t}\n}\n","import { EMPTY_ROW_VERSION } from './row-version'\n\n/**\n * Per-field last-writer versions for a materialized row.\n *\n * Each entry maps a field name to a serialized HLC timestamp (the same\n * lexicographically-sortable form used by the row `_version` column). Because\n * the serialized form sorts identically to {@link HybridLogicalClock.compare},\n * per-field last-write-wins is a plain string comparison: the greater string is\n * the newer writer. This makes field-level convergence deterministic and\n * independent of the order operations happen to arrive in.\n */\nexport type FieldVersions = Record<string, string>\n\n/**\n * Parse the `_field_versions` JSON column into a map. Tolerates null, empty,\n * and malformed values by returning an empty map — a row that predates\n * per-field tracking simply has no field versions and falls back to `_version`.\n */\nexport function parseFieldVersions(raw: unknown): FieldVersions {\n\tif (typeof raw !== 'string' || raw.length === 0) {\n\t\treturn {}\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as unknown\n\t\tif (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\treturn {}\n\t\t}\n\t\tconst result: FieldVersions = {}\n\t\tfor (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n\t\t\tif (typeof value === 'string') {\n\t\t\t\tresult[key] = value\n\t\t\t}\n\t\t}\n\t\treturn result\n\t} catch {\n\t\treturn {}\n\t}\n}\n\n/**\n * Serialize a field-version map for storage in the `_field_versions` column.\n */\nexport function serializeFieldVersions(versions: FieldVersions): string {\n\treturn JSON.stringify(versions)\n}\n\n/**\n * Build the initial field-version map for an insert: every supplied field is\n * stamped with the operation's version.\n */\nexport function fieldVersionsForFields(\n\tfieldNames: Iterable<string>,\n\tversion: string,\n): FieldVersions {\n\tconst versions: FieldVersions = {}\n\tfor (const field of fieldNames) {\n\t\tversions[field] = version\n\t}\n\treturn versions\n}\n\n/**\n * The effective stored version for a field: its own tracked version if present,\n * otherwise the row-level `_version` (so rows written before per-field tracking\n * still compare correctly), otherwise the empty sentinel (older than anything).\n */\nexport function effectiveFieldVersion(\n\tcurrent: FieldVersions,\n\tfield: string,\n\trowVersion: string | undefined,\n): string {\n\tconst own = current[field]\n\tif (own !== undefined) {\n\t\treturn own\n\t}\n\treturn rowVersion ?? EMPTY_ROW_VERSION\n}\n\n/**\n * Result of resolving an incoming operation against the current per-field\n * versions of a row.\n */\nexport interface PerFieldLwwResult {\n\t/** Fields the incoming operation wins (strictly newer than the stored version). */\n\twinners: string[]\n\t/** The merged field-version map to persist (winners advanced to `incomingVersion`). */\n\tmerged: FieldVersions\n}\n\n/**\n * Resolve per-field last-write-wins for an incoming operation.\n *\n * For each incoming field, the operation wins only when its version is strictly\n * greater than the field's effective stored version. Ties (identical version,\n * i.e. the same operation re-applied) are losses, keeping the resolution\n * idempotent. The comparison is a total order, so every node resolves the same\n * winner regardless of the order operations arrive.\n *\n * @param current - Parsed `_field_versions` of the target row\n * @param incomingFields - Field names carried by the incoming operation\n * @param incomingVersion - Serialized HLC version of the incoming operation\n * @param rowVersion - The row's `_version` (fallback for untracked fields)\n */\nexport function resolvePerFieldLww(\n\tcurrent: FieldVersions,\n\tincomingFields: Iterable<string>,\n\tincomingVersion: string,\n\trowVersion: string | undefined,\n): PerFieldLwwResult {\n\tconst winners: string[] = []\n\tconst merged: FieldVersions = { ...current }\n\tfor (const field of incomingFields) {\n\t\tconst stored = effectiveFieldVersion(current, field, rowVersion)\n\t\tif (incomingVersion > stored) {\n\t\t\twinners.push(field)\n\t\t\tmerged[field] = incomingVersion\n\t\t}\n\t\t// Losing fields are left untouched: their existing tracked version (or the\n\t\t// row-level `_version` fallback) already reflects the newer writer.\n\t}\n\treturn { winners, merged }\n}\n","import type { StorageAdapter, Transaction } from '../types'\n\ninterface SequenceRow {\n\tsequence_number: number\n}\n\n/**\n * Atomically increment and return the next sequence number for a node.\n * Uses SQLite UPSERT + RETURNING so concurrent tabs do not reuse sequence slots.\n */\nexport async function allocateNextSequenceNumber(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n): Promise<number> {\n\tconst rows = await adapter.query<SequenceRow>(\n\t\t`INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, 1)\n ON CONFLICT(node_id) DO UPDATE SET sequence_number = sequence_number + 1\n RETURNING sequence_number`,\n\t\t[nodeId],\n\t)\n\tconst seq = rows[0]?.sequence_number\n\tif (seq === undefined) {\n\t\tthrow new Error(`Failed to allocate sequence number for node \"${nodeId}\"`)\n\t}\n\treturn seq\n}\n\n/**\n * Allocate the next sequence inside an existing storage transaction.\n */\nexport async function allocateNextSequenceInTransaction(\n\ttx: Transaction,\n\tnodeId: string,\n): Promise<number> {\n\tconst rows = await tx.query<SequenceRow>(\n\t\t`INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, 1)\n ON CONFLICT(node_id) DO UPDATE SET sequence_number = sequence_number + 1\n RETURNING sequence_number`,\n\t\t[nodeId],\n\t)\n\tconst seq = rows[0]?.sequence_number\n\tif (seq === undefined) {\n\t\tthrow new Error(`Failed to allocate sequence number for node \"${nodeId}\" in transaction`)\n\t}\n\treturn seq\n}\n\n/**\n * Read the current sequence for a node without incrementing.\n */\nexport async function readSequenceNumber(adapter: StorageAdapter, nodeId: string): Promise<number> {\n\tconst rows = await adapter.query<SequenceRow>(\n\t\t'SELECT sequence_number FROM _kora_version_vector WHERE node_id = ?',\n\t\t[nodeId],\n\t)\n\treturn rows[0]?.sequence_number ?? 0\n}\n","import type { LocalMutationContext } from './types'\n\n/**\n * Merge explicit parent deps (e.g. referential cascade) with tracker-derived deps.\n */\nexport function resolveCausalDeps(ctx: LocalMutationContext): string[] {\n\tconst merged: string[] = []\n\tfor (const id of ctx.extraCausalDeps ?? []) {\n\t\tif (!merged.includes(id)) {\n\t\t\tmerged.push(id)\n\t\t}\n\t}\n\tfor (const id of ctx.causalTracker?.nextCausalDeps(ctx.collection, ctx.inTransaction) ?? []) {\n\t\tif (!merged.includes(id)) {\n\t\t\tmerged.push(id)\n\t\t}\n\t}\n\treturn merged\n}\n","import type { AtomicOp } from '@korajs/core'\nimport {\n\tcreateOperation,\n\tisAtomicOp,\n\tquoteIdent,\n\tresolveAtomicOp,\n\ttoAtomicOp,\n\ttransformSecretFieldsForWrite,\n\tvalidateRecord,\n} from '@korajs/core'\nimport { RecordNotFoundError } from '../errors'\nimport { parseFieldVersions, serializeFieldVersions } from '../lww/field-versions'\nimport { serializeRowVersion } from '../lww/row-version'\nimport { buildInsertQuery, buildUpdateQuery } from '../query/sql-builder'\nimport { encodeRichtextFieldsForOpData } from '../serialization/op-data-encoding'\nimport { deserializeRecord, serializeOperation, serializeRecord } from '../serialization/serializer'\nimport { validateUpdateStateMachine } from '../state-machine/state-validator'\nimport { allocateNextSequenceInTransaction } from '../store/sequence-allocator'\nimport type { CollectionRecord, RawCollectionRow } from '../types'\nimport { resolveCausalDeps } from './resolve-causal-deps'\nimport type { LocalMutationContext } from './types'\n\n/**\n * Update a record and persist the operation log entry atomically.\n */\nexport async function executeUpdate(\n\tctx: LocalMutationContext,\n\tid: string,\n\tdata: Record<string, unknown>,\n): Promise<CollectionRecord> {\n\tconst currentRows = await ctx.adapter.query<RawCollectionRow>(\n\t\t`SELECT * FROM ${quoteIdent(ctx.collection)} WHERE id = ? AND _deleted = 0`,\n\t\t[id],\n\t)\n\tconst currentRow = currentRows[0]\n\tif (!currentRow) {\n\t\tthrow new RecordNotFoundError(ctx.collection, id)\n\t}\n\n\tlet validated = validateRecord(ctx.collection, ctx.definition, data, 'update')\n\tconst currentRecord = deserializeRecord(currentRow, ctx.definition.fields)\n\tvalidated = validateUpdateStateMachine(\n\t\tctx.collection,\n\t\tid,\n\t\tctx.definition,\n\t\tcurrentRecord,\n\t\tvalidated,\n\t)\n\n\tif (Object.keys(validated).length === 0) {\n\t\treturn { ...currentRecord }\n\t}\n\n\tconst previousData: Record<string, unknown> = {}\n\tconst resolvedData: Record<string, unknown> = {}\n\tconst atomicOps: Record<string, AtomicOp> = {}\n\n\tfor (const key of Object.keys(validated)) {\n\t\tconst value = validated[key]\n\t\tpreviousData[key] = currentRecord[key]\n\n\t\tif (isAtomicOp(value)) {\n\t\t\tresolvedData[key] = resolveAtomicOp(currentRecord[key], value)\n\t\t\tatomicOps[key] = toAtomicOp(value)\n\t\t} else {\n\t\t\tresolvedData[key] = value\n\t\t}\n\t}\n\n\t// Transform secret fields in the changed set to their at-rest form before the\n\t// operation is built. previousData already holds the stored (ciphertext) value\n\t// from the current record, so it needs no transform.\n\tconst writeData = await transformSecretFieldsForWrite(\n\t\tresolvedData,\n\t\tctx.definition,\n\t\tctx.secretKeyProvider,\n\t)\n\n\tconst hasAtomicOps = Object.keys(atomicOps).length > 0\n\tconst causalDeps = resolveCausalDeps(ctx)\n\tlet operation!: Awaited<ReturnType<typeof createOperation>>\n\n\tawait ctx.adapter.transaction(async (tx) => {\n\t\tconst sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId)\n\t\toperation = await createOperation(\n\t\t\t{\n\t\t\t\tnodeId: ctx.nodeId,\n\t\t\t\ttype: 'update',\n\t\t\t\tcollection: ctx.collection,\n\t\t\t\trecordId: id,\n\t\t\t\t// Binary richtext values (new and previous) are tagged as canonical\n\t\t\t\t// JSON BEFORE the operation is content-hashed, so the hash input,\n\t\t\t\t// persisted JSON, and wire payload are the identical value.\n\t\t\t\tdata: encodeRichtextFieldsForOpData(writeData, ctx.definition.fields),\n\t\t\t\tpreviousData: encodeRichtextFieldsForOpData(previousData, ctx.definition.fields),\n\t\t\t\tsequenceNumber,\n\t\t\t\tcausalDeps,\n\t\t\t\tschemaVersion: ctx.schema.version,\n\t\t\t\t...(hasAtomicOps ? { atomicOps } : {}),\n\t\t\t},\n\t\t\tctx.clock,\n\t\t)\n\t\tctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction)\n\n\t\tconst serializedChanges = serializeRecord(writeData, ctx.definition.fields)\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\t// A local edit is always the newest writer of the fields it touches (the\n\t\t// HLC advances past every timestamp this device has seen), so stamp each\n\t\t// changed field with this operation's version.\n\t\tconst mergedFieldVersions = parseFieldVersions(currentRow._field_versions)\n\t\tfor (const changedField of Object.keys(serializedChanges)) {\n\t\t\tmergedFieldVersions[changedField] = version\n\t\t}\n\t\tconst updateQuery = buildUpdateQuery(ctx.collection, id, {\n\t\t\t...serializedChanges,\n\t\t\t_updated_at: operation.timestamp.wallTime,\n\t\t\t_version: version,\n\t\t\t_field_versions: serializeFieldVersions(mergedFieldVersions),\n\t\t})\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${ctx.collection}`,\n\t\t\tserializeOperation(operation) as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait tx.execute(updateQuery.sql, updateQuery.params)\n\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t})\n\n\tctx.onMutation(ctx.collection, operation)\n\n\tconst rows = await ctx.adapter.query<RawCollectionRow>(\n\t\t`SELECT * FROM ${quoteIdent(ctx.collection)} WHERE id = ? AND _deleted = 0`,\n\t\t[id],\n\t)\n\tconst row = rows[0]\n\tif (!row) {\n\t\tthrow new RecordNotFoundError(ctx.collection, id)\n\t}\n\treturn deserializeRecord(row, ctx.definition.fields)\n}\n","import { KoraError, validateTransition } from '@korajs/core'\nimport type {\n\tCollectionDefinition,\n\tStateMachineConstraint,\n\tStateMachineDefinition,\n} from '@korajs/core'\n\n/**\n * Error thrown when a local mutation attempts an invalid state transition.\n *\n * Contains enough context to debug without reproduction:\n * the collection, record, field, current state, attempted state, and allowed transitions.\n */\nexport class InvalidStateTransitionError extends KoraError {\n\tconstructor(\n\t\tpublic readonly collection: string,\n\t\tpublic readonly recordId: string,\n\t\tpublic readonly field: string,\n\t\tpublic readonly fromState: string,\n\t\tpublic readonly toState: string,\n\t\tpublic readonly allowedStates: string[],\n\t) {\n\t\tsuper(\n\t\t\t`Invalid state transition in collection \"${collection}\": ` +\n\t\t\t\t`cannot transition field \"${field}\" from \"${fromState}\" to \"${toState}\". ` +\n\t\t\t\t`Allowed transitions from \"${fromState}\": ${allowedStates.length > 0 ? allowedStates.join(', ') : '(none -- terminal state)'}`,\n\t\t\t'INVALID_STATE_TRANSITION',\n\t\t\t{ collection, recordId, field, fromState, toState, allowedStates },\n\t\t)\n\t\tthis.name = 'InvalidStateTransitionError'\n\t}\n}\n\n/**\n * Validates a state machine transition for a local mutation (insert or update).\n *\n * For inserts: validates that the initial state (from the data or the default value)\n * is a known state in the state machine.\n *\n * For updates: looks up the current state field value and checks whether\n * the transition to the new value is allowed.\n *\n * @param collectionName - Name of the collection\n * @param recordId - The record being mutated\n * @param stateMachine - The state machine definition\n * @param currentState - The current value of the state field (null for inserts)\n * @param newState - The new value being set for the state field\n * @returns An object indicating whether the transition is valid, and if not, the allowed states.\n * When `onInvalidTransition` is 'last-valid-state', callers should suppress the field update\n * rather than throwing.\n */\nexport function validateStateTransition(\n\tcollectionName: string,\n\trecordId: string,\n\tstateMachine: StateMachineDefinition,\n\tcurrentState: string | null,\n\tnewState: string,\n): { valid: boolean; allowedStates: string[] } {\n\t// For inserts, any valid enum value is acceptable as the initial state\n\t// (schema validation already ensures the value is a valid enum value)\n\tif (currentState === null) {\n\t\treturn { valid: true, allowedStates: [] }\n\t}\n\n\t// Same-state transitions are always valid (idempotent updates)\n\tif (currentState === newState) {\n\t\treturn { valid: true, allowedStates: stateMachine.transitions[currentState] ?? [] }\n\t}\n\n\tconst constraint: StateMachineConstraint = {\n\t\tfield: stateMachine.field,\n\t\tcollection: collectionName,\n\t\ttransitions: stateMachine.transitions,\n\t}\n\tconst transitionResult = validateTransition(constraint, currentState, newState)\n\tif (transitionResult.valid) {\n\t\treturn { valid: true, allowedStates: transitionResult.allowedTargets }\n\t}\n\n\tconst allowedStates = transitionResult.allowedTargets\n\n\tif (stateMachine.onInvalidTransition === 'reject') {\n\t\tthrow new InvalidStateTransitionError(\n\t\t\tcollectionName,\n\t\t\trecordId,\n\t\t\tstateMachine.field,\n\t\t\tcurrentState,\n\t\t\tnewState,\n\t\t\tallowedStates,\n\t\t)\n\t}\n\n\t// 'last-valid-state': return invalid so the caller can suppress the field update\n\treturn { valid: false, allowedStates }\n}\n\n/**\n * Checks whether a collection update data object contains a change to the state machine field,\n * and if so, validates the transition.\n *\n * If the state field is not in the update data, returns the data unchanged.\n * If the transition is invalid and mode is 'last-valid-state', removes the field from the data.\n * If the transition is invalid and mode is 'reject', throws InvalidStateTransitionError.\n *\n * @param collectionName - Name of the collection\n * @param recordId - The record being updated\n * @param collectionDef - The collection definition from the schema\n * @param currentRecord - The current record data (must include the state field)\n * @param updateData - The partial update data\n * @returns The update data, potentially with the state field removed if invalid and mode is 'last-valid-state'\n */\nexport function validateUpdateStateMachine(\n\tcollectionName: string,\n\trecordId: string,\n\tcollectionDef: CollectionDefinition,\n\tcurrentRecord: Record<string, unknown>,\n\tupdateData: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst stateMachine = collectionDef.stateMachine\n\tif (stateMachine === undefined) {\n\t\treturn updateData\n\t}\n\n\tconst stateField = stateMachine.field\n\tif (!(stateField in updateData)) {\n\t\t// State field not being changed -- no validation needed\n\t\treturn updateData\n\t}\n\n\tconst currentState = currentRecord[stateField]\n\tconst newState = updateData[stateField]\n\n\t// Both values must be strings for state machine validation\n\tif (typeof currentState !== 'string' || typeof newState !== 'string') {\n\t\treturn updateData\n\t}\n\n\tconst result = validateStateTransition(\n\t\tcollectionName,\n\t\trecordId,\n\t\tstateMachine,\n\t\tcurrentState,\n\t\tnewState,\n\t)\n\n\tif (result.valid) {\n\t\treturn updateData\n\t}\n\n\t// Mode is 'last-valid-state': silently remove the state field from the update\n\tconst filtered = { ...updateData }\n\tdelete filtered[stateField]\n\n\t// If no fields remain after removing the state field, the update becomes a no-op.\n\t// Return the empty object -- the caller can decide whether to proceed.\n\treturn filtered\n}\n","import { createOperation, quoteIdent } from '@korajs/core'\nimport type { Operation } from '@korajs/core'\nimport { RecordNotFoundError } from '../errors'\nimport { serializeRowVersion } from '../lww/row-version'\nimport { buildInsertQuery, buildSoftDeleteQuery } from '../query/sql-builder'\nimport { serializeOperation } from '../serialization/serializer'\nimport { allocateNextSequenceInTransaction } from '../store/sequence-allocator'\nimport type { RawCollectionRow } from '../types'\nimport { resolveCausalDeps } from './resolve-causal-deps'\nimport type { LocalMutationContext } from './types'\n\nexport interface ExecuteDeleteOptions {\n\t/** When true, skip RelationEnforcer (caller already enforced referential integrity). */\n\tskipReferentialEnforcement?: boolean\n\t/** Pre-built delete operation (avoids duplicate sequence allocation). */\n\toperation?: Operation\n}\n\n/**\n * Soft-delete a record and persist the operation log entry atomically.\n * Returns the primary delete operation and any cascaded side-effect operations.\n */\nexport async function executeDelete(\n\tctx: LocalMutationContext,\n\tid: string,\n\toptions?: ExecuteDeleteOptions,\n): Promise<Operation[]> {\n\tconst currentRows = await ctx.adapter.query<RawCollectionRow>(\n\t\t`SELECT * FROM ${quoteIdent(ctx.collection)} WHERE id = ? AND _deleted = 0`,\n\t\t[id],\n\t)\n\tif (!currentRows[0]) {\n\t\tthrow new RecordNotFoundError(ctx.collection, id)\n\t}\n\n\tconst causalDeps = resolveCausalDeps(ctx)\n\tlet operation = options?.operation\n\tconst cascadedOps: Operation[] = []\n\n\tawait ctx.adapter.transaction(async (tx) => {\n\t\tif (!operation) {\n\t\t\tconst sequenceNumber = await allocateNextSequenceInTransaction(tx, ctx.nodeId)\n\t\t\toperation = await createOperation(\n\t\t\t\t{\n\t\t\t\t\tnodeId: ctx.nodeId,\n\t\t\t\t\ttype: 'delete',\n\t\t\t\t\tcollection: ctx.collection,\n\t\t\t\t\trecordId: id,\n\t\t\t\t\tdata: null,\n\t\t\t\t\tpreviousData: null,\n\t\t\t\t\tsequenceNumber,\n\t\t\t\t\tcausalDeps,\n\t\t\t\t\tschemaVersion: ctx.schema.version,\n\t\t\t\t},\n\t\t\t\tctx.clock,\n\t\t\t)\n\t\t\tctx.causalTracker?.afterOperation(ctx.collection, operation.id, ctx.inTransaction)\n\t\t}\n\n\t\tif (ctx.relationEnforcer && !options?.skipReferentialEnforcement) {\n\t\t\tconst enforcementResult = await ctx.relationEnforcer.enforceDelete(ctx.collection, id, tx, [\n\t\t\t\toperation.id,\n\t\t\t])\n\t\t\tcascadedOps.push(...enforcementResult.operations)\n\t\t}\n\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\tconst deleteQuery = buildSoftDeleteQuery(\n\t\t\tctx.collection,\n\t\t\tid,\n\t\t\toperation.timestamp.wallTime,\n\t\t\tversion,\n\t\t)\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${ctx.collection}`,\n\t\t\tserializeOperation(operation) as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait tx.execute(deleteQuery.sql, deleteQuery.params)\n\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t})\n\n\tif (!operation) {\n\t\tthrow new Error('Delete operation was not created')\n\t}\n\n\tctx.onMutation(ctx.collection, operation)\n\tfor (const cascadedOp of cascadedOps) {\n\t\tctx.onMutation(cascadedOp.collection, cascadedOp)\n\t}\n\n\treturn [operation, ...cascadedOps]\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core"]}
1
+ {"version":3,"sources":["../src/internal.ts","../src/serialization/serializer.ts","../src/serialization/richtext-serializer.ts","../src/serialization/op-data-encoding.ts","../src/query/sql-builder.ts","../src/errors.ts","../src/lww/row-version.ts","../src/mutations/run-local-write.ts","../src/mutations/write-context.ts","../src/mutations/write-ops.ts","../src/lww/field-versions.ts","../src/state-machine/state-validator.ts","../src/mutations/secret-write.ts","../src/mutations/execute-insert.ts","../src/mutations/execute-update.ts","../src/mutations/execute-delete.ts","../src/sync/sync-state.ts","../src/sync/sync-durability.ts","../src/sync/local-sync-records.ts"],"sourcesContent":["// Internal exports — shared within @kora packages but NOT part of the public API.\n// Other @kora packages can import from '@korajs/store/internal' if needed.\n\nexport {\n\tserializeOperation,\n\tdeserializeOperation,\n\tdeserializeOperationWithCollection,\n\tserializeRecord,\n\tdeserializeRecord,\n} from './serialization/serializer'\n\nexport {\n\tbuildSelectQuery,\n\tbuildCountQuery,\n\tbuildInsertQuery,\n\tbuildUpdateQuery,\n\tbuildLwwUpdateQuery,\n\tbuildSoftDeleteQuery,\n\tbuildLwwSoftDeleteQuery,\n\tbuildWhereClause,\n} from './query/sql-builder'\nexport {\n\tserializeRowVersion,\n\trowVersionFromRecord,\n\tisIncomingNewerThanRow,\n} from './lww/row-version'\nexport type { SqlQuery } from './query/sql-builder'\n\nexport type {\n\tOperationRow,\n\tRawCollectionRow,\n\tMetaRow,\n\tVersionVectorRow,\n\tSubscription,\n} from './types'\n\nexport { executeInsert } from './mutations/execute-insert'\nexport { executeUpdate } from './mutations/execute-update'\nexport { executeDelete } from './mutations/execute-delete'\nexport type { ExecuteDeleteOptions } from './mutations/execute-delete'\nexport type { LocalMutationContext } from './mutations/types'\n\nexport { hasUnsyncedOwnOperations } from './sync/local-sync-records'\nexport type { DatabaseQuery } from './sync/local-sync-records'\n","import { HybridLogicalClock, decodeStoredText, encodeStoredText } from '@korajs/core'\nimport type { CollectionDefinition, FieldDescriptor, Operation } from '@korajs/core'\nimport type { CollectionRecord, OperationRow, RawCollectionRow } from '../types'\nimport { decodeRichtext, encodeRichtext } from './richtext-serializer'\n\n/**\n * Serialize a JS record to SQL-compatible values for INSERT/UPDATE.\n * Transforms: boolean → 0/1, array → JSON string, richtext → Yjs binary update.\n *\n * @param data - The record data with JS-native types\n * @param fields - The field descriptors from the schema\n * @returns An object with SQL-compatible values\n */\nexport function serializeRecord(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tconst descriptor = fields[key]\n\t\tif (!descriptor) {\n\t\t\tresult[key] = value\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = serializeValue(value, descriptor)\n\t}\n\treturn result\n}\n\n/**\n * Deserialize a SQL row to JS-native types for the application layer.\n * Transforms: 0/1 → boolean, JSON string → array, strips _deleted, maps _created_at/_updated_at.\n *\n * @param row - The raw SQL row\n * @param fields - The field descriptors from the schema\n * @returns A CollectionRecord with JS-native types\n */\nexport function deserializeRecord(\n\trow: RawCollectionRow,\n\tfields: Record<string, FieldDescriptor>,\n): CollectionRecord {\n\tconst result: CollectionRecord = {\n\t\tid: row.id,\n\t\tcreatedAt: row._created_at,\n\t\tupdatedAt: row._updated_at,\n\t}\n\n\tfor (const [key, descriptor] of Object.entries(fields)) {\n\t\tconst rawValue = row[key]\n\t\tif (rawValue === undefined || rawValue === null) {\n\t\t\tresult[key] = rawValue ?? null\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = deserializeValue(rawValue, descriptor)\n\t}\n\n\treturn result\n}\n\n/**\n * Internal key used to embed atomicOps metadata in the data JSON column.\n * This avoids adding a new column to the ops table (no migration needed).\n */\nconst ATOMIC_OPS_KEY = '__kora_atomic_ops__'\n\n/**\n * Internal key used to embed transactionId in the data JSON column.\n */\nconst TX_ID_KEY = '__kora_tx_id__'\n\n/**\n * Internal key used to embed mutationName in the data JSON column.\n */\nconst MUTATION_NAME_KEY = '__kora_mutation__'\n\n/**\n * Internal key used to embed a scope-entry insert's per-field versions (RT-27) in the\n * data JSON column, so a later fold of the record's log places each field correctly.\n */\nconst FIELD_VERSIONS_KEY = '__kora_field_versions__'\n\n/**\n * Internal key used to embed the operation's content-hash version (CORE-1, protocol\n * v2) in the data JSON column. Absent means version 1. Kept in the row so id\n * verification and re-hashing (rebase, rotation, resequence) use the right version.\n */\nconst HASH_VERSION_KEY = '__kora_hash_version__'\n\n/**\n * Serialize an Operation to a row for the operations log table.\n *\n * @param op - The operation to serialize\n * @returns An OperationRow suitable for SQL INSERT\n */\nexport function serializeOperation(op: Operation): OperationRow {\n\tconst hasMetadata =\n\t\top.transactionId !== undefined || op.mutationName !== undefined || op.hashVersion !== undefined\n\tlet dataPayload: Record<string, unknown> | null = null\n\tif (op.data) {\n\t\t// Embed metadata in the data JSON when present\n\t\tdataPayload = { ...op.data }\n\t\tif (op.atomicOps !== undefined && Object.keys(op.atomicOps).length > 0) {\n\t\t\tdataPayload[ATOMIC_OPS_KEY] = op.atomicOps\n\t\t}\n\t\tif (op.transactionId !== undefined) {\n\t\t\tdataPayload[TX_ID_KEY] = op.transactionId\n\t\t}\n\t\tif (op.mutationName !== undefined) {\n\t\t\tdataPayload[MUTATION_NAME_KEY] = op.mutationName\n\t\t}\n\t\tif (op.fieldVersions !== undefined) {\n\t\t\tdataPayload[FIELD_VERSIONS_KEY] = op.fieldVersions\n\t\t}\n\t\tif (op.hashVersion !== undefined) {\n\t\t\tdataPayload[HASH_VERSION_KEY] = op.hashVersion\n\t\t}\n\t} else if (hasMetadata) {\n\t\t// For delete operations (data is null), we still need to store metadata\n\t\tdataPayload = {}\n\t\tif (op.transactionId !== undefined) {\n\t\t\tdataPayload[TX_ID_KEY] = op.transactionId\n\t\t}\n\t\tif (op.mutationName !== undefined) {\n\t\t\tdataPayload[MUTATION_NAME_KEY] = op.mutationName\n\t\t}\n\t\tif (op.hashVersion !== undefined) {\n\t\t\tdataPayload[HASH_VERSION_KEY] = op.hashVersion\n\t\t}\n\t}\n\n\treturn {\n\t\tid: op.id,\n\t\tnode_id: op.nodeId,\n\t\ttype: op.type,\n\t\trecord_id: op.recordId,\n\t\tdata: dataPayload ? JSON.stringify(dataPayload) : null,\n\t\tprevious_data: op.previousData ? JSON.stringify(op.previousData) : null,\n\t\ttimestamp: HybridLogicalClock.serialize(op.timestamp),\n\t\tsequence_number: op.sequenceNumber,\n\t\tcausal_deps: JSON.stringify(op.causalDeps),\n\t\tschema_version: op.schemaVersion,\n\t}\n}\n\n/**\n * Deserialize a row from the operations log table back to an Operation.\n *\n * @param row - The raw operation row from SQL\n * @returns The deserialized Operation object\n */\nexport function deserializeOperation(row: OperationRow): Operation {\n\tlet data: Record<string, unknown> | null = null\n\tlet atomicOps: Record<string, unknown> | undefined\n\tlet transactionId: string | undefined\n\tlet mutationName: string | undefined\n\tlet fieldVersions: Operation['fieldVersions']\n\tlet hashVersion: Operation['hashVersion']\n\n\tif (row.data) {\n\t\tconst parsed = JSON.parse(row.data) as Record<string, unknown>\n\t\t// Extract embedded metadata keys\n\t\tif (ATOMIC_OPS_KEY in parsed) {\n\t\t\tatomicOps = parsed[ATOMIC_OPS_KEY] as Record<string, unknown>\n\t\t}\n\t\tif (TX_ID_KEY in parsed) {\n\t\t\ttransactionId = parsed[TX_ID_KEY] as string\n\t\t}\n\t\tif (MUTATION_NAME_KEY in parsed) {\n\t\t\tmutationName = parsed[MUTATION_NAME_KEY] as string\n\t\t}\n\t\tif (FIELD_VERSIONS_KEY in parsed) {\n\t\t\tfieldVersions = parsed[FIELD_VERSIONS_KEY] as Operation['fieldVersions']\n\t\t}\n\t\tif (HASH_VERSION_KEY in parsed) {\n\t\t\thashVersion = parsed[HASH_VERSION_KEY] as Operation['hashVersion']\n\t\t}\n\t\t// Remove metadata keys from data\n\t\tconst {\n\t\t\t[ATOMIC_OPS_KEY]: _a,\n\t\t\t[TX_ID_KEY]: _t,\n\t\t\t[MUTATION_NAME_KEY]: _m,\n\t\t\t[FIELD_VERSIONS_KEY]: _f,\n\t\t\t[HASH_VERSION_KEY]: _h,\n\t\t\t...rest\n\t\t} = parsed\n\t\t// An insert or update always carries data, possibly `{}` (an insert of only\n\t\t// optional fields): its id covers `{}`, so `{}` is read back, never `null`. Only a\n\t\t// delete's payload is metadata alone (RT-80).\n\t\tdata = row.type !== 'delete' || Object.keys(rest).length > 0 ? rest : null\n\t}\n\n\treturn {\n\t\tid: row.id,\n\t\tnodeId: row.node_id,\n\t\ttype: row.type as Operation['type'],\n\t\tcollection: '', // Collection name is derived from the table name by the caller\n\t\trecordId: row.record_id,\n\t\tdata,\n\t\tpreviousData: row.previous_data\n\t\t\t? (JSON.parse(row.previous_data) as Record<string, unknown>)\n\t\t\t: null,\n\t\ttimestamp: HybridLogicalClock.deserialize(row.timestamp),\n\t\tsequenceNumber: row.sequence_number,\n\t\tcausalDeps: JSON.parse(row.causal_deps) as string[],\n\t\tschemaVersion: row.schema_version,\n\t\t...(atomicOps !== undefined ? { atomicOps: atomicOps as Operation['atomicOps'] } : {}),\n\t\t...(transactionId !== undefined ? { transactionId } : {}),\n\t\t...(mutationName !== undefined ? { mutationName } : {}),\n\t\t...(fieldVersions !== undefined ? { fieldVersions } : {}),\n\t\t...(hashVersion !== undefined ? { hashVersion } : {}),\n\t}\n}\n\n/**\n * Deserialize an operation row with collection name already known.\n */\nexport function deserializeOperationWithCollection(\n\trow: OperationRow,\n\tcollection: string,\n): Operation {\n\tconst op = deserializeOperation(row)\n\treturn { ...op, collection }\n}\n\n/**\n * The read form of record-shaped values the developer wrote: a richtext field written\n * as a string or ArrayBuffer is returned as the Yjs bytes it is stored (and read back)\n * as, so `insert()` / `update()` (and their transaction previews) return the same\n * value shape as `findById()` and queries. Other fields are returned as given.\n *\n * @param data - Record-shaped values (validated, at rest)\n * @param fields - The collection's field descriptors\n * @returns `data`, or a copy with richtext values in their read form\n */\nexport function toRichtextReadShape(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tlet out = data\n\tfor (const [name, descriptor] of Object.entries(fields)) {\n\t\tif (descriptor.kind !== 'richtext' || !(name in data)) continue\n\t\tconst value = data[name]\n\t\tif (value === null || value === undefined || value instanceof Uint8Array) continue\n\t\tif (out === data) out = { ...data }\n\t\tout[name] = encodeRichtext(value as Parameters<typeof encodeRichtext>[0])\n\t}\n\treturn out\n}\n\nfunction serializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value ? 1 : 0\n\t\tcase 'array':\n\t\tcase 'object':\n\t\tcase 'json':\n\t\tcase 'blob':\n\t\t\treturn JSON.stringify(value)\n\t\tcase 'richtext':\n\t\t\t// May be a record-shaped value (string/bytes) or an op-data value\n\t\t\t// (tagged { $koraBytes }) — encodeRichtext accepts every form.\n\t\t\treturn encodeRichtext(value as Parameters<typeof encodeRichtext>[0])\n\t\tdefault:\n\t\t\t// Raw string columns: SQLite binds strings as UTF-8, which cannot hold a lone\n\t\t\t// surrogate (stored as U+FFFD) and SQLite WASM reads TEXT up to a NUL. The\n\t\t\t// shared stored-text codec keeps every JS string (RT-65); JSON columns above\n\t\t\t// need none (JSON.stringify escapes both).\n\t\t\treturn typeof value === 'string' && isRawTextKind(descriptor.kind)\n\t\t\t\t? encodeStoredText(value)\n\t\t\t\t: value\n\t}\n}\n\n/**\n * Field kinds whose column holds the raw string (the stored-text codec applies). Not\n * `enum`: its column holds the schema's literal values verbatim; an enum value SQL text\n * cannot store exactly (U+0000, U+FFFF, a lone surrogate) is refused by `t.enum()` itself\n * (the table no longer has a `CHECK`, RT-101).\n */\nconst RAW_TEXT_KINDS: ReadonlySet<string> = new Set(['string', 'secret'])\n\n/** True for a field kind stored as a raw string, encoded with the stored-text codec. */\nexport function isRawTextKind(kind: string): boolean {\n\treturn RAW_TEXT_KINDS.has(kind)\n}\n\n/**\n * Encode a filter value compared against a field's column: a raw-string field stores\n * the encoded form, and the codec is injective, so equality and `IN` stay exact.\n *\n * @param value - The value from the developer's `where`\n * @param descriptor - The field's descriptor, when the field is known\n */\nexport function encodeStoredFilterValue(\n\tvalue: unknown,\n\tdescriptor: FieldDescriptor | undefined,\n): unknown {\n\treturn typeof value === 'string' && descriptor !== undefined && isRawTextKind(descriptor.kind)\n\t\t? encodeStoredText(value)\n\t\t: value\n}\n\nfunction deserializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value === 1 || value === true\n\t\tcase 'array':\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn JSON.parse(value) as unknown[]\n\t\t\t}\n\t\t\treturn value\n\t\tcase 'object':\n\t\tcase 'json':\n\t\tcase 'blob':\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn JSON.parse(value) as unknown\n\t\t\t}\n\t\t\treturn value\n\t\tcase 'richtext':\n\t\t\treturn decodeRichtext(value)\n\t\tdefault:\n\t\t\treturn typeof value === 'string' && isRawTextKind(descriptor.kind)\n\t\t\t\t? decodeStoredText(value)\n\t\t\t\t: value\n\t}\n}\n","import * as Y from 'yjs'\nimport {\n\ttype KoraBytesValue,\n\tdecodeRichtextOpDataValue,\n\tisKoraBytesValue,\n\tisLegacyNumericByteObject,\n} from './op-data-encoding'\n\nconst TEXT_KEY = 'content'\n\nexport type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined\n\n/**\n * Encodes richtext values into Yjs document updates.\n *\n * Also accepts the tagged `{ $koraBytes }` form that binary richtext values\n * take inside `op.data` (and, for old dev databases, the pre-fix numeric-key\n * object shape), so every consumer that writes op-data values to columns\n * inherits the decoding. Plain strings keep their exact historical behavior:\n * encoded as a fresh Yjs document containing the string.\n */\nexport function encodeRichtext(value: RichtextInput | KoraBytesValue): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\t// Deterministic clientId so the same string encodes to the same bytes on every\n\t\t// device (a plain string is a replacement, not a collaborative actor). A random\n\t\t// clientId would make a string-derived richtext column diverge byte-wise across\n\t\t// replicas even when the text is identical.\n\t\tdoc.clientID = 0\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tif (isKoraBytesValue(value) || isLegacyNumericByteObject(value)) {\n\t\tconst decoded = decodeRichtextOpDataValue(value)\n\t\t// Both accepted shapes decode to bytes; strings were handled above.\n\t\treturn decoded instanceof Uint8Array ? decoded : null\n\t}\n\n\tthrow new Error('Richtext value must be a string, Uint8Array, ArrayBuffer, null, or undefined.')\n}\n\n/**\n * Decodes driver-provided richtext values into Uint8Array.\n */\nexport function decodeRichtext(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tthrow new Error(\n\t\t'Richtext storage value must be Uint8Array, ArrayBuffer, Buffer, null, or undefined.',\n\t)\n}\n\n/**\n * Reads plain text from a richtext Yjs state update.\n */\nexport function richtextToPlainText(value: RichtextInput): string {\n\tconst encoded = encodeRichtext(value)\n\tif (!encoded) return ''\n\n\tconst doc = new Y.Doc()\n\tY.applyUpdate(doc, encoded)\n\treturn doc.getText(TEXT_KEY).toString()\n}\n\n/**\n * Compares two richtext values by decoded plain text.\n * Yjs update bytes are not stable across separate encodes of the same string.\n */\nexport function richtextStatesEqual(a: unknown, b: unknown): boolean {\n\ttry {\n\t\treturn richtextToPlainText(a as RichtextInput) === richtextToPlainText(b as RichtextInput)\n\t} catch {\n\t\treturn false\n\t}\n}\n","import {\n\ttype KoraBytesValue,\n\tbase64ToBytes,\n\tbytesToBase64,\n\tdecodeBytesFromOpData,\n\tencodeBytesForOpData,\n\tisKoraBytesValue,\n\tisLegacyNumericByteObject,\n} from '@korajs/core'\nimport type { FieldDescriptor } from '@korajs/core'\n\n/**\n * The canonical binary op-data encoding lives in `@korajs/core` because\n * `op.data` is a core concept shared by store (creation, persistence) and merge\n * (CRDT merge). These re-exports keep existing store-internal import paths\n * working while the single source of truth is the core module.\n */\nexport type { KoraBytesValue }\nexport { base64ToBytes, bytesToBase64, isKoraBytesValue, isLegacyNumericByteObject }\n\n/** @deprecated Use {@link encodeRichtextForOpData}; kept for existing imports. */\nexport const encodeRichtextForOpData = encodeBytesForOpData\n/** @deprecated Use {@link decodeRichtextOpDataValue}; kept for existing imports. */\nexport const decodeRichtextOpDataValue = decodeBytesFromOpData\n\n/**\n * Apply {@link encodeBytesForOpData} to every richtext-typed field in an\n * operation's data payload. Called at operation creation, BEFORE the payload\n * is content-hashed, so the hash input, persisted JSON, wire payload, and the\n * value the merge engine sees are the identical canonical value. Non-richtext\n * fields are untouched.\n */\nexport function encodeRichtextFieldsForOpData(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = { ...data }\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (fields[key]?.kind !== 'richtext') {\n\t\t\tcontinue\n\t\t}\n\t\tif (typeof value === 'string' || value instanceof Uint8Array || value instanceof ArrayBuffer) {\n\t\t\tresult[key] = encodeBytesForOpData(value)\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Reverse of {@link encodeRichtextFieldsForOpData}: decode tagged (or legacy\n * numeric-key) richtext values in an op-data payload back to record-shaped\n * values (string or Uint8Array). Null/undefined and plain strings pass\n * through, preserving pre-fix behavior exactly.\n */\nexport function decodeRichtextFieldsFromOpData(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = { ...data }\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (fields[key]?.kind !== 'richtext' || value === null || value === undefined) {\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = decodeBytesFromOpData(value)\n\t}\n\treturn result\n}\n","import type { CollectionDefinition, FieldDescriptor } from '@korajs/core'\nimport { quoteIdent } from '@korajs/core'\nimport { QueryError } from '../errors'\nimport { lwwVersionWhereClause } from '../lww/row-version'\nimport { encodeStoredFilterValue } from '../serialization/serializer'\nimport type { QueryDescriptor, WhereOperators } from '../types'\n\n/**\n * Result of building a SQL query: the parameterized SQL string and its bound values.\n */\nexport interface SqlQuery {\n\tsql: string\n\tparams: unknown[]\n}\n\n/**\n * Build a SELECT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0` to exclude soft-deleted records.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query\n */\nexport function buildSelectQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT * FROM ${quoteIdent(descriptor.collection)}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\t// Always filter out soft-deleted records\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\tif (descriptor.orderBy.length > 0) {\n\t\tconst orderParts = descriptor.orderBy.map((o) => {\n\t\t\tvalidateFieldName(o.field, fields)\n\t\t\t// SEC-7: the direction is whitelisted to a fixed keyword, never spliced from\n\t\t\t// the caller's string (a runtime value cast to the type is untrusted input).\n\t\t\treturn `${quoteIdent(resolveColumnName(o.field, fields))} ${sqlSortDirection(o.direction, o.field)}`\n\t\t})\n\t\tparts.push(`ORDER BY ${orderParts.join(', ')}`)\n\t}\n\n\t// SEC-7: LIMIT / OFFSET are bound as parameters after a safe-integer check, so a\n\t// value that only claims to be a number can never become SQL text.\n\tconst limit = descriptor.limit !== undefined ? assertQueryCount(descriptor.limit, 'limit') : null\n\tconst offset =\n\t\tdescriptor.offset !== undefined ? assertQueryCount(descriptor.offset, 'offset') : null\n\tif (limit !== null) {\n\t\tparts.push('LIMIT ?')\n\t\tparams.push(limit)\n\t}\n\tif (offset !== null) {\n\t\t// SQLite accepts OFFSET only after LIMIT; LIMIT -1 means \"no limit\".\n\t\tif (limit === null) parts.push('LIMIT -1')\n\t\tparts.push('OFFSET ?')\n\t\tparams.push(offset)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build a COUNT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0`.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query that returns { count: number }\n */\nexport function buildCountQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT COUNT(*) as count FROM ${quoteIdent(descriptor.collection)}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build an INSERT query for a collection record.\n *\n * @param collection - The collection name\n * @param record - The record data (already serialized with id, _created_at, _updated_at)\n * @returns A parameterized SQL query\n */\nexport function buildInsertQuery(collection: string, record: Record<string, unknown>): SqlQuery {\n\tconst columns = Object.keys(record)\n\tconst placeholders = columns.map(() => '?')\n\tconst params = Object.values(record)\n\n\tconst quotedColumns = columns.map((col) => quoteIdent(col))\n\tconst sql = `INSERT INTO ${quoteIdent(collection)} (${quotedColumns.join(', ')}) VALUES (${placeholders.join(', ')})`\n\treturn { sql, params }\n}\n\n/**\n * Build an UPDATE query for a collection record.\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param changes - The fields to update (already serialized)\n * @returns A parameterized SQL query\n */\nexport function buildUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tchanges: Record<string, unknown>,\n): SqlQuery {\n\tconst setClauses = Object.keys(changes).map((col) => `${quoteIdent(col)} = ?`)\n\tconst params = [...Object.values(changes), id]\n\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ?`\n\treturn { sql, params }\n}\n\n/**\n * Build a field-level fast-forward UPDATE: writes the changed field values\n * unconditionally (the caller has already proven per-field that the local value\n * still equals the base the remote wrote from, so the remote is the newest known\n * writer of those fields), while advancing the row's `_version` / `_updated_at`\n * watermark MONOTONICALLY — never regressing it below its current value.\n *\n * This is what makes per-field LWW correct on a per-row `_version` column: a\n * concurrent edit to a different field may have pushed `_version` past this\n * operation's timestamp, but that must not cause this operation's own field\n * change to be dropped, nor may materializing it move the watermark backward\n * (which would let a genuinely stale later operation win).\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param fieldChanges - Serialized field values to write unconditionally (must NOT include _version / _updated_at)\n * @param remoteVersion - The operation's serialized row version (lexicographically comparable)\n * @param wallTime - The operation's wall-clock time in ms\n * @param options - Optional extras. `maxCreatedAt` advances `_created_at` to the\n * greater of its current value and the given wall time — used for insert\n * collisions so every node converges on the max insert wall time regardless\n * of arrival order.\n */\nexport function buildFieldFastForwardUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tfieldChanges: Record<string, unknown>,\n\tremoteVersion: string,\n\twallTime: number,\n\toptions?: { maxCreatedAt?: number },\n): SqlQuery {\n\tconst fieldClauses = Object.keys(fieldChanges).map((col) => `${quoteIdent(col)} = ?`)\n\t// _version is a lexicographically-sortable string; _updated_at is a number.\n\t// Keep the greater of the current and incoming value for each (monotonic).\n\tconst setClauses = [\n\t\t...fieldClauses,\n\t\t'_version = CASE WHEN _version >= ? THEN _version ELSE ? END',\n\t\t'_updated_at = CASE WHEN _updated_at >= ? THEN _updated_at ELSE ? END',\n\t]\n\tconst params: unknown[] = [\n\t\t...Object.values(fieldChanges),\n\t\tremoteVersion,\n\t\tremoteVersion,\n\t\twallTime,\n\t\twallTime,\n\t]\n\tif (options?.maxCreatedAt !== undefined) {\n\t\tsetClauses.push('_created_at = CASE WHEN _created_at >= ? THEN _created_at ELSE ? END')\n\t\tparams.push(options.maxCreatedAt, options.maxCreatedAt)\n\t}\n\tparams.push(id)\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ?`\n\treturn { sql, params }\n}\n\n/**\n * Build a soft-delete query (SET _deleted = 1).\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param updatedAt - The timestamp to set on _updated_at\n * @returns A parameterized SQL query\n */\nexport function buildSoftDeleteQuery(\n\tcollection: string,\n\tid: string,\n\tupdatedAt: number,\n\tversion?: string,\n): SqlQuery {\n\tif (version !== undefined) {\n\t\treturn {\n\t\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ?`,\n\t\t\tparams: [updatedAt, version, id],\n\t\t}\n\t}\n\treturn {\n\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ? WHERE id = ?`,\n\t\tparams: [updatedAt, id],\n\t}\n}\n\n/**\n * Build an UPDATE that applies only when the row is missing or older than `remoteVersion`\n * (serialized HLC). Prevents stale remote sync from overwriting newer local materialized state.\n */\nexport function buildLwwUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tchanges: Record<string, unknown>,\n\tremoteVersion: string,\n): SqlQuery {\n\tconst setClauses = Object.keys(changes).map((col) => `${quoteIdent(col)} = ?`)\n\tconst lww = lwwVersionWhereClause(remoteVersion)\n\tconst sql = `UPDATE ${quoteIdent(collection)} SET ${setClauses.join(', ')} WHERE id = ? AND ${lww.sql}`\n\tconst params = [...Object.values(changes), id, ...lww.params]\n\treturn { sql, params }\n}\n\n/**\n * Build a soft-delete that applies only when the row is older than `remoteVersion`.\n */\nexport function buildLwwSoftDeleteQuery(\n\tcollection: string,\n\tid: string,\n\tupdatedAt: number,\n\tversion: string,\n): SqlQuery {\n\tconst lww = lwwVersionWhereClause(version)\n\treturn {\n\t\tsql: `UPDATE ${quoteIdent(collection)} SET _deleted = 1, _updated_at = ?, _version = ? WHERE id = ? AND ${lww.sql}`,\n\t\tparams: [updatedAt, version, id, ...lww.params],\n\t}\n}\n\n/**\n * Build a WHERE clause from conditions, validating field names against the schema.\n *\n * @param where - The where conditions\n * @param fields - The field descriptors from the collection schema\n * @returns The SQL WHERE clause string and params, or null if no conditions\n */\nexport function buildWhereClause(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery | null {\n\tconst params: unknown[] = []\n\tconst result = buildWhereClauseParts(where, fields, params)\n\tif (!result) return null\n\treturn { sql: result, params }\n}\n\n// --- Internal helpers ---\n\nconst VALID_OPERATORS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in'])\n\nfunction buildWhereClauseParts(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n\tparams: unknown[],\n): string | null {\n\tconst conditions: string[] = []\n\n\tfor (const [fieldName, value] of Object.entries(where)) {\n\t\tvalidateFieldName(fieldName, fields)\n\t\tconst descriptor = fields[fieldName]\n\t\tconst column = resolveColumnName(fieldName, fields)\n\n\t\tif (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t// Operator object: { $gt: 5, $lt: 10 }\n\t\t\tconst ops = value as WhereOperators\n\t\t\tfor (const [op, opValue] of Object.entries(ops)) {\n\t\t\t\tif (!VALID_OPERATORS.has(op)) {\n\t\t\t\t\tthrow new QueryError(`Unknown operator \"${op}\" on field \"${fieldName}\"`, {\n\t\t\t\t\t\tfield: fieldName,\n\t\t\t\t\t\toperator: op,\n\t\t\t\t\t\tvalidOperators: [...VALID_OPERATORS],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconditions.push(buildOperatorCondition(fieldName, column, op, opValue, descriptor, params))\n\t\t\t}\n\t\t} else {\n\t\t\t// Shorthand: { completed: false } means { completed: { $eq: false } }\n\t\t\tconditions.push(buildOperatorCondition(fieldName, column, '$eq', value, descriptor, params))\n\t\t}\n\t}\n\n\tif (conditions.length === 0) return null\n\treturn conditions.join(' AND ')\n}\n\nfunction buildOperatorCondition(\n\tfieldName: string,\n\tcolumnName: string,\n\toperator: string,\n\tvalue: unknown,\n\tdescriptor: FieldDescriptor | undefined,\n\tparams: unknown[],\n): string {\n\t// Serialize boolean values to 0/1 and raw strings to their stored form (RT-65)\n\tconst sqlValue = toSqlFilterValue(value, descriptor)\n\n\tconst column = quoteIdent(columnName)\n\n\tswitch (operator) {\n\t\tcase '$eq':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${column} IS NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} = ?`\n\t\tcase '$ne':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${column} IS NOT NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} != ?`\n\t\tcase '$gt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} > ?`\n\t\tcase '$gte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} >= ?`\n\t\tcase '$lt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} < ?`\n\t\tcase '$lte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${column} <= ?`\n\t\tcase '$in': {\n\t\t\tif (!Array.isArray(sqlValue)) {\n\t\t\t\tthrow new QueryError(`$in operator requires an array value for field \"${fieldName}\"`, {\n\t\t\t\t\tfield: fieldName,\n\t\t\t\t\treceived: typeof sqlValue,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst placeholders = sqlValue.map(() => '?')\n\t\t\tfor (const item of sqlValue) {\n\t\t\t\tparams.push(toSqlFilterValue(item, descriptor))\n\t\t\t}\n\t\t\treturn `${column} IN (${placeholders.join(', ')})`\n\t\t}\n\t\tdefault:\n\t\t\tthrow new QueryError(`Unknown operator \"${operator}\"`, { operator })\n\t}\n}\n\nfunction toSqlFilterValue(value: unknown, descriptor: FieldDescriptor | undefined): unknown {\n\tif (descriptor?.kind === 'boolean' && typeof value === 'boolean') return value ? 1 : 0\n\treturn encodeStoredFilterValue(value, descriptor)\n}\n\n/**\n * Record metadata exposed on every record under a camelCase name and backed by a\n * system column (STORE-11): `where({ updatedAt: { $gt: t } })` and\n * `orderBy('createdAt', 'desc')` filter and sort on these columns. A schema field\n * declared with the same name wins and its own column is used instead.\n */\nexport const VIRTUAL_TIMESTAMP_FIELDS = Object.freeze({\n\tcreatedAt: '_created_at',\n\tupdatedAt: '_updated_at',\n} as const)\n\n/** Name of a virtual record-metadata field usable in `where` and `orderBy`. */\nexport type VirtualTimestampField = keyof typeof VIRTUAL_TIMESTAMP_FIELDS\n\n/** Every virtual record-metadata field name, for typing `where` / `orderBy` keys. */\nexport const VIRTUAL_TIMESTAMP_FIELD_NAMES: readonly VirtualTimestampField[] = Object.freeze([\n\t'createdAt',\n\t'updatedAt',\n] as const)\n\nconst SORT_DIRECTIONS: Readonly<Record<string, 'ASC' | 'DESC'>> = Object.freeze({\n\tasc: 'ASC',\n\tdesc: 'DESC',\n})\n\n/** Map a query field name to the column it reads (STORE-11 virtual fields). */\nfunction resolveColumnName(fieldName: string, fields: Record<string, FieldDescriptor>): string {\n\tif (Object.hasOwn(fields, fieldName)) return fieldName\n\tif (fieldName === 'createdAt' || fieldName === 'updatedAt') {\n\t\treturn VIRTUAL_TIMESTAMP_FIELDS[fieldName]\n\t}\n\treturn fieldName\n}\n\nfunction sqlSortDirection(direction: unknown, field: string): 'ASC' | 'DESC' {\n\tconst key = typeof direction === 'string' ? direction.toLowerCase() : ''\n\tconst keyword = Object.hasOwn(SORT_DIRECTIONS, key) ? SORT_DIRECTIONS[key] : undefined\n\tif (keyword === undefined) {\n\t\tthrow new QueryError(`Invalid sort direction for field \"${field}\". Use 'asc' or 'desc'.`, {\n\t\t\tfield,\n\t\t\tdirection: typeof direction === 'string' ? direction.slice(0, 64) : typeof direction,\n\t\t})\n\t}\n\treturn keyword\n}\n\nfunction assertQueryCount(value: unknown, clause: 'limit' | 'offset'): number {\n\tif (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {\n\t\tthrow new QueryError(\n\t\t\t`Invalid ${clause}: expected a non-negative safe integer. Pass a whole number, for example .${clause}(10).`,\n\t\t\t{ clause, received: typeof value === 'number' ? value : typeof value },\n\t\t)\n\t}\n\treturn value\n}\n\nfunction validateFieldName(fieldName: string, fields: Record<string, FieldDescriptor>): void {\n\t// Allow schema fields plus metadata fields that map to query-able columns\n\tconst allowedFields = new Set([\n\t\t...Object.keys(fields),\n\t\t'id',\n\t\t...VIRTUAL_TIMESTAMP_FIELD_NAMES,\n\t\t'_created_at',\n\t\t'_updated_at',\n\t])\n\tif (!allowedFields.has(fieldName)) {\n\t\tthrow new QueryError(\n\t\t\t`Unknown field \"${fieldName}\" in query. Available fields: ${[...allowedFields].join(', ')}`,\n\t\t\t{ field: fieldName },\n\t\t)\n\t}\n}\n","import { KoraError } from '@korajs/core'\n\n/**\n * Thrown when a query is invalid (bad field names, invalid operators, etc.).\n */\nexport class QueryError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'QUERY_ERROR', context)\n\t\tthis.name = 'QueryError'\n\t}\n}\n\n/**\n * Thrown when a record is not found by ID (findById, update, delete on missing record).\n */\nexport class RecordNotFoundError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(`Record \"${recordId}\" not found in collection \"${collection}\"`, 'RECORD_NOT_FOUND', {\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t})\n\t\tthis.name = 'RecordNotFoundError'\n\t}\n}\n\n/**\n * Thrown inside `applyRemoteOperation` when the row's version state changed\n * between the caller's snapshot read and the guarded write (a concurrent local\n * mutation landed in the window). The transaction is rolled back with nothing\n * written; the caller re-reads fresh state, recomputes its merge, and retries.\n * This is the optimistic-concurrency guard that keeps merge-engine results\n * (richtext, add-wins arrays, constraint resolutions) from clobbering newer\n * local edits they never saw.\n */\nexport class OptimisticLockError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(\n\t\t\t`Row version changed while merging record \"${recordId}\" in \"${collection}\"; retry with fresh state`,\n\t\t\t'OPTIMISTIC_LOCK',\n\t\t\t{ collection, recordId },\n\t\t)\n\t\tthis.name = 'OptimisticLockError'\n\t}\n}\n\n/**\n * Thrown when a storage adapter operation fails.\n */\nexport class AdapterError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'ADAPTER_ERROR', context)\n\t\tthis.name = 'AdapterError'\n\t}\n}\n\n/**\n * Thrown when a write reaches a storage adapter that could not obtain durable\n * storage (it opened, or was promoted to leader, on a non-persistent in-memory\n * database). Kora refuses such writes instead of accepting data that would be\n * lost on reload. Reads still work.\n *\n * Fix: close other tabs or apps using the same origin's OPFS storage and reopen,\n * or opt in explicitly with `allowNonDurable: true` if in-memory storage is\n * acceptable for this app.\n */\nexport class StorageDurabilityError extends KoraError {\n\tconstructor(dbName: string, phase: 'open' | 'promotion', reason: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" has no durable storage (${reason} during ${phase}); writes are refused so they are not silently lost on reload. Close other tabs or apps using this origin's storage and reload, or set allowNonDurable: true to accept in-memory storage.`,\n\t\t\t'STORAGE_DURABILITY_LOST',\n\t\t\t{ dbName, phase, reason },\n\t\t)\n\t\tthis.name = 'StorageDurabilityError'\n\t}\n}\n\n/**\n * Thrown when the store opens a database a NEWER build of the app already migrated: its\n * stored schema version is above this code's `schema.version` (RT-109). Running older\n * code against it would write operations of the old schema into the new one, or fail\n * part-way, so the store refuses before changing anything and emits `store:schema-ahead`.\n *\n * Fix: run the newer build (reload while online, or accept the app's update prompt).\n * Never lower `schema.version` in a deployed app.\n */\nexport class SchemaVersionAheadError extends KoraError {\n\tconstructor(\n\t\tpublic readonly dbName: string,\n\t\tpublic readonly storedVersion: number,\n\t\tpublic readonly codeVersion: number,\n\t) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is at schema version ${storedVersion}, newer than this build's schema version ${codeVersion}: a newer build of the app already migrated it. It was not opened and nothing in it changed. Run the newer build (reload while online, or accept the update); never lower schema.version in a deployed app.`,\n\t\t\t'SCHEMA_VERSION_AHEAD',\n\t\t\t{ dbName, storedVersion, codeVersion },\n\t\t)\n\t\tthis.name = 'SchemaVersionAheadError'\n\t}\n}\n\n/**\n * Thrown when an operation is attempted on a store that has not been opened.\n */\nexport class StoreNotOpenError extends KoraError {\n\tconstructor() {\n\t\tsuper('Store is not open. Call store.open() before performing operations.', 'STORE_NOT_OPEN')\n\t\tthis.name = 'StoreNotOpenError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).\n */\nexport class WorkerInitError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Worker initialization failed: ${message}`, 'WORKER_INIT_ERROR', context)\n\t\tthis.name = 'WorkerInitError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker does not respond within the configured timeout.\n */\nexport class WorkerTimeoutError extends KoraError {\n\tconstructor(operation: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\t`Worker did not respond within ${timeoutMs}ms for operation \"${operation}\"`,\n\t\t\t'WORKER_TIMEOUT',\n\t\t\t{ operation, timeoutMs },\n\t\t)\n\t\tthis.name = 'WorkerTimeoutError'\n\t}\n}\n\n/**\n * Thrown when a follower tab cannot reach a live leader tab over the multi-tab\n * broadcast channel (the leader tab was closed, crashed, or is unresponsive).\n * Distinct from {@link WorkerTimeoutError}: this fails fast on a confirmed-absent\n * leader instead of waiting out the full RPC timeout.\n */\nexport class NoLeaderError extends KoraError {\n\tconstructor(\n\t\toperation: string,\n\t\tmessage = `No live leader tab is answering multi-tab storage RPC for operation \"${operation}\"`,\n\t\tcode = 'NO_LEADER',\n\t\tcontext: Record<string, unknown> = {},\n\t) {\n\t\tsuper(message, code, { operation, ...context })\n\t\tthis.name = 'NoLeaderError'\n\t}\n}\n\n/**\n * Thrown to a follower tab's pending storage requests when the leader tab that\n * was answering stopped sending heartbeats (hung, frozen, or suspended). The\n * request may or may not have been applied: retry it with the same `requestId`\n * (in the error context) and the leader de-duplicates it. Retriable.\n */\nexport class LeaderUnresponsiveError extends NoLeaderError {\n\tconstructor(operation: string, silentMs: number, requestId: string) {\n\t\tsuper(\n\t\t\toperation,\n\t\t\t`The leader tab stopped responding (no heartbeat for ${silentMs}ms) during \"${operation}\". The request may or may not have been applied; retry it with the same requestId and the leader de-duplicates it.`,\n\t\t\t'LEADER_UNRESPONSIVE',\n\t\t\t{ silentMs, requestId, retriable: true },\n\t\t)\n\t\tthis.name = 'LeaderUnresponsiveError'\n\t}\n}\n\n/**\n * Thrown to pending storage requests when their bridge was torn down, typically\n * because this tab was just promoted to leader or the leader handed over.\n * Retriable: the next attempt goes to the new leader.\n */\nexport class BridgeTerminatedError extends KoraError {\n\tconstructor(operation: string, reason: string) {\n\t\tsuper(\n\t\t\t`Storage request \"${operation}\" was interrupted (${reason}); retry it against the new storage leader.`,\n\t\t\t'BRIDGE_TERMINATED',\n\t\t\t{ operation, reason, retriable: true },\n\t\t)\n\t\tthis.name = 'BridgeTerminatedError'\n\t}\n}\n\n/** Thrown when a storage request is cancelled through its `AbortSignal`. */\nexport class RequestAbortedError extends KoraError {\n\tconstructor(operation: string, requestId: string) {\n\t\tsuper(\n\t\t\t`Storage request \"${operation}\" was aborted by the caller. A write may already have reached the leader; retry with the same requestId to stay idempotent.`,\n\t\t\t'REQUEST_ABORTED',\n\t\t\t{ operation, requestId },\n\t\t)\n\t\tthis.name = 'RequestAbortedError'\n\t}\n}\n\n/**\n * Thrown by `deleteDatabase()` when the database is open in some tab or worker\n * on this origin. Close it everywhere (or call `app.close()`) and retry.\n */\nexport class StorageInUseError extends KoraError {\n\tconstructor(dbName: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is open in a tab on this origin; close it before deleting it.`,\n\t\t\t'STORAGE_IN_USE',\n\t\t\t{ dbName },\n\t\t)\n\t\tthis.name = 'StorageInUseError'\n\t}\n}\n\n/**\n * Thrown by `deleteDatabase()` when the database still holds operations that\n * never reached the server. Sync first, or pass `{ force: true }` to discard them.\n */\nexport class UnsyncedDataError extends KoraError {\n\tconstructor(dbName: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" has unsynced operations; deleting it would lose them. Sync first, or pass { force: true } to discard them deliberately.`,\n\t\t\t'UNSYNCED_DATA',\n\t\t\t{ dbName },\n\t\t)\n\t\tthis.name = 'UnsyncedDataError'\n\t}\n}\n\n/**\n * Thrown when a database's data lives in a storage backend this runtime cannot\n * read (for example it was written to OPFS, and OPFS is unavailable now). Kora\n * refuses to start an empty, disjoint copy rather than hide the existing data.\n */\nexport class StorageBackendMismatchError extends KoraError {\n\tconstructor(dbName: string, recorded: string, requested: string, reason: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is stored in ${recorded}, but this session can only use ${requested} (${reason}). Kora will not start a separate empty copy; reload in a browser profile where ${recorded} is available.`,\n\t\t\t'STORAGE_BACKEND_MISMATCH',\n\t\t\t{ dbName, recorded, requested, reason },\n\t\t)\n\t\tthis.name = 'StorageBackendMismatchError'\n\t}\n}\n\n/**\n * Thrown when IndexedDB persistence operations fail (serialize/deserialize).\n */\nexport class PersistenceError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Persistence error: ${message}`, 'PERSISTENCE_ERROR', context)\n\t\tthis.name = 'PersistenceError'\n\t}\n}\n","import { type HLCTimestamp, HybridLogicalClock } from '@korajs/core'\nimport type { RawCollectionRow } from '../types'\n\n/** Empty _version means legacy rows (compare by wallTime only via fallback). */\nexport const EMPTY_ROW_VERSION = ''\n\n/**\n * Serialize an HLC timestamp for storage in the `_version` column.\n * Lexicographic order matches {@link HybridLogicalClock.compare}.\n */\nexport function serializeRowVersion(timestamp: HLCTimestamp): string {\n\treturn HybridLogicalClock.serialize(timestamp)\n}\n\n/**\n * Read the version stamp stored on a materialized row.\n * Legacy rows without `_version` fall back to wallTime with logical 0.\n */\nexport function rowVersionFromRecord(\n\trow: Pick<RawCollectionRow, '_updated_at' | '_version'>,\n): HLCTimestamp {\n\tconst version = row._version\n\tif (typeof version === 'string' && version.length > 0) {\n\t\treturn HybridLogicalClock.deserialize(version)\n\t}\n\treturn { wallTime: row._updated_at, logical: 0, nodeId: '' }\n}\n\n/**\n * Returns true when `incoming` is strictly newer than the version on `row`.\n */\nexport function isIncomingNewerThanRow(\n\tincoming: HLCTimestamp,\n\trow: Pick<RawCollectionRow, '_updated_at' | '_version'>,\n): boolean {\n\treturn HybridLogicalClock.compare(incoming, rowVersionFromRecord(row)) > 0\n}\n\n/**\n * SQL fragment and params for LWW guards: apply only when row is missing or older.\n * Uses serialized `_version` for total order (matches HLC compare).\n */\nexport function lwwVersionWhereClause(remoteVersion: string): { sql: string; params: string[] } {\n\treturn {\n\t\tsql: '(_version = ? OR _version < ?)',\n\t\tparams: [EMPTY_ROW_VERSION, remoteVersion],\n\t}\n}\n","import type { Operation } from '@korajs/core'\nimport { KoraError } from '@korajs/core'\nimport type { LocalMutationContext } from './types'\nimport { CausalScope, type WriteScope, withWriteScope } from './write-context'\nimport type { WriteResult } from './write-ops'\n\n/**\n * Run one local write in its own storage transaction, then publish it.\n *\n * Post-commit work (causal heads, in-memory version vector, subscriptions,\n * `operation:created`) runs only after the transaction commits, so nothing\n * observable ever points at an operation that was rolled back (STORE-15).\n */\nexport async function runLocalWrite(\n\tctx: LocalMutationContext,\n\twrite: (scope: WriteScope) => Promise<WriteResult>,\n): Promise<WriteResult> {\n\tconst causal = new CausalScope(ctx.causalTracker, false)\n\tlet result: WriteResult | undefined\n\ttry {\n\t\tawait ctx.adapter.transaction(async (tx) => {\n\t\t\tresult = await withWriteScope(tx, ctx.nodeId, causal, write)\n\t\t})\n\t} catch (error) {\n\t\tctx.onStorageError?.(error)\n\t\tthrow error\n\t}\n\tif (!result) {\n\t\tthrow new KoraError('Local write transaction completed without a result', 'WRITE_INCOMPLETE', {\n\t\t\tcollection: ctx.collection,\n\t\t})\n\t}\n\tcausal.publish()\n\tpublishOperations(ctx, result)\n\treturn result\n}\n\n/** Notify the store about every operation of a committed write, in creation order. */\nfunction publishOperations(ctx: LocalMutationContext, result: WriteResult): void {\n\tconst ops: Operation[] = []\n\tif (result.operation) ops.push(result.operation)\n\tops.push(...result.sideEffects)\n\tfor (const op of ops) {\n\t\tctx.onMutation(op.collection, op)\n\t}\n}\n","import type {\n\tCausalTracker,\n\tHybridLogicalClock,\n\tOperation,\n\tSchemaDefinition,\n\tSecretKeyProvider,\n} from '@korajs/core'\nimport { KoraError } from '@korajs/core'\nimport type { RecordFolder } from '../fold/record-folder'\nimport type { RelationEnforcer } from '../relations/relation-enforcer'\nimport type { Transaction } from '../types'\n\n/**\n * Called inside the write transaction for every local delete, after the delete\n * operation is built and before referential side effects run. Lets the unified\n * apply pipeline run its referential checks (and emit their traces) against the\n * rows the transaction sees. Throwing aborts the whole write.\n */\nexport type LocalDeleteHook = (operation: Operation, tx: Transaction) => Promise<void>\n\n/**\n * Everything the single local write path needs to build and persist an\n * operation inside a storage transaction. One environment serves single-record\n * writes, `app.transaction` commits and referential cascades, so every local\n * operation is built the same way (W6).\n */\nexport interface WriteEnv {\n\treadonly schema: SchemaDefinition\n\treadonly clock: HybridLogicalClock\n\treadonly nodeId: string\n\treadonly relationEnforcer: RelationEnforcer | null\n\treadonly secretKeyProvider?: SecretKeyProvider\n\t/** Shared by every operation of an `app.transaction` / `app.mutation` commit. */\n\treadonly transactionId?: string\n\t/** Human-readable mutation name propagated to every operation (DevTools). */\n\treadonly mutationName?: string\n\t/** See {@link LocalDeleteHook}. */\n\treadonly beforeLocalDelete?: LocalDeleteHook\n\t/**\n\t * The W7 record fold. When set, a local write appends its operation and then\n\t * merges it into the record's fold state, which re-materializes the row (the\n\t * same path remote operations take). Absent only under\n\t * `experimental.legacyMerge` (and while a pre-W7 database is migrated, before its\n\t * re-materialization): the row is then written directly, as in beta.12.\n\t */\n\treadonly fold?: RecordFolder\n\t/**\n\t * Largest operation a local write may produce (RT-86), the server's\n\t * `maxOperationBytes`. Default `DEFAULT_MAX_OPERATION_BYTES` (256 KiB).\n\t */\n\treadonly maxOperationBytes?: number\n}\n\n/**\n * Causal bookkeeping for the operations built inside ONE storage transaction.\n *\n * Dependencies are computed from the committed causal heads (the shared\n * {@link CausalTracker}) overlaid with the operations already built in this\n * transaction. Heads are published to the shared tracker only after the\n * transaction commits, so a rolled-back write never leaves a dependency on an\n * operation that does not exist.\n */\nexport class CausalScope {\n\tprivate readonly lastByCollection = new Map<string, string>()\n\tprivate lastInScope: string | null = null\n\tprivate readonly recorded: Array<{ collection: string; id: string }> = []\n\n\tconstructor(\n\t\tprivate readonly tracker: CausalTracker | null,\n\t\t/** When true, every operation also depends on the previous one in this scope. */\n\t\tprivate readonly chainOperations: boolean,\n\t) {}\n\n\t/**\n\t * Direct causal parents for the next operation in `collection`.\n\t *\n\t * @param collection - Collection of the operation being built\n\t * @param extra - Explicit parents (e.g. the delete that caused a cascade)\n\t */\n\tdepsFor(collection: string, extra: readonly string[] = []): string[] {\n\t\tconst deps: string[] = []\n\t\tconst add = (id: string | undefined | null): void => {\n\t\t\tif (id && !deps.includes(id)) deps.push(id)\n\t\t}\n\t\tfor (const id of extra) add(id)\n\t\tconst local = this.lastByCollection.get(collection)\n\t\tif (local !== undefined) {\n\t\t\tadd(local)\n\t\t} else {\n\t\t\tfor (const id of this.tracker?.nextCausalDeps(collection, false) ?? []) add(id)\n\t\t}\n\t\tif (this.chainOperations) add(this.lastInScope)\n\t\treturn deps\n\t}\n\n\t/** Record an operation built in this scope. */\n\trecord(collection: string, operationId: string): void {\n\t\tthis.lastByCollection.set(collection, operationId)\n\t\tthis.lastInScope = operationId\n\t\tthis.recorded.push({ collection, id: operationId })\n\t}\n\n\t/** Publish this scope's heads to the shared tracker. Call only after commit. */\n\tpublish(): void {\n\t\tfor (const { collection, id } of this.recorded) {\n\t\t\tthis.tracker?.afterOperation(collection, id, false)\n\t\t}\n\t}\n}\n\n/**\n * The sequence-number block of ONE write transaction.\n *\n * The first number is reserved with one `UPSERT ... RETURNING` on\n * `_kora_version_vector`, inside the transaction; later numbers continue the block\n * in memory, and {@link SequenceBlock.close} persists the block's end with one\n * write before the transaction commits. Write transactions are serialized and no\n * code path allocates numbers outside one, so the block is contiguous and never\n * shared; the UNIQUE `(node_id, sequence_number)` index on every op table is the\n * backstop. A rollback discards the reservation with everything else.\n */\nexport class SequenceBlock {\n\tprivate last: number | null = null\n\tprivate persisted = 0\n\n\tconstructor(\n\t\tprivate readonly tx: Transaction,\n\t\tprivate readonly nodeId: string,\n\t) {}\n\n\t/** The next number of the block. */\n\tasync take(): Promise<number> {\n\t\tif (this.last === null) {\n\t\t\tconst rows = await this.tx.query<{ sequence_number: number }>(\n\t\t\t\t`INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, 1)\n ON CONFLICT(node_id) DO UPDATE SET sequence_number = sequence_number + 1\n RETURNING sequence_number`,\n\t\t\t\t[this.nodeId],\n\t\t\t)\n\t\t\tconst first = rows[0]?.sequence_number\n\t\t\tif (first === undefined) {\n\t\t\t\tthrow new KoraError(\n\t\t\t\t\t`Failed to reserve a sequence number for node \"${this.nodeId}\"`,\n\t\t\t\t\t'SEQUENCE_RESERVATION_FAILED',\n\t\t\t\t\t{ nodeId: this.nodeId },\n\t\t\t\t)\n\t\t\t}\n\t\t\tthis.last = first\n\t\t\tthis.persisted = first\n\t\t\treturn first\n\t\t}\n\t\tthis.last += 1\n\t\treturn this.last\n\t}\n\n\t/** Persist the end of the block. Must run inside the transaction, before commit. */\n\tasync close(): Promise<void> {\n\t\tif (this.last === null || this.last === this.persisted) return\n\t\tawait this.tx.execute(\n\t\t\t'UPDATE _kora_version_vector SET sequence_number = MAX(sequence_number, ?) WHERE node_id = ?',\n\t\t\t[this.last, this.nodeId],\n\t\t)\n\t\tthis.persisted = this.last\n\t}\n}\n\n/**\n * The per-transaction state of a local write: the transaction handle, its\n * sequence block and its causal scope. Every operation built in one transaction\n * takes the next number of the same contiguous block.\n */\nexport interface WriteScope {\n\treadonly tx: Transaction\n\treadonly sequence: SequenceBlock\n\treadonly causal: CausalScope\n}\n\n/**\n * Run `write` with a fresh {@link WriteScope} on `tx`, then close the sequence\n * block (still inside the transaction).\n */\nexport async function withWriteScope<T>(\n\ttx: Transaction,\n\tnodeId: string,\n\tcausal: CausalScope,\n\twrite: (scope: WriteScope) => Promise<T>,\n): Promise<T> {\n\tconst scope: WriteScope = { tx, sequence: new SequenceBlock(tx, nodeId), causal }\n\tconst result = await write(scope)\n\tawait scope.sequence.close()\n\treturn result\n}\n\n/**\n * Whether a storage error means the device ran out of space: SQLite's\n * `SQLITE_FULL` (\"database or disk is full\") or a browser quota error from OPFS /\n * IndexedDB. The store maps these to a `store:quota-exceeded` event (STORE-15).\n */\nexport function isStorageFullError(error: unknown): boolean {\n\tif (error === null || typeof error !== 'object') return false\n\tconst candidate = error as { name?: unknown; code?: unknown; message?: unknown; cause?: unknown }\n\tif (candidate.name === 'QuotaExceededError') return true\n\tif (candidate.code === 'SQLITE_FULL' || candidate.code === 'QUOTA_EXCEEDED') return true\n\tconst message = typeof candidate.message === 'string' ? candidate.message : ''\n\tif (/SQLITE_FULL|database or disk is full|quota ?exceeded/i.test(message)) return true\n\treturn candidate.cause !== undefined && candidate.cause !== error\n\t\t? isStorageFullError(candidate.cause)\n\t\t: false\n}\n","import type { AtomicOp, CollectionDefinition, Operation } from '@korajs/core'\nimport {\n\tDEFAULT_MAX_OPERATION_BYTES,\n\tKoraError,\n\tOperationTooLargeError,\n\tassertResolvedFieldValue,\n\tcreateOperation,\n\tgenerateUUIDv7,\n\tisAtomicOp,\n\tmeasureOperationBytes,\n\tquoteIdent,\n\tresolveAtomicOp,\n\ttoAtomicOp,\n\tvalidateRecord,\n} from '@korajs/core'\nimport { RecordNotFoundError } from '../errors'\nimport { stampFieldVersions } from '../lww/field-versions'\nimport { serializeRowVersion } from '../lww/row-version'\nimport { buildInsertQuery, buildSoftDeleteQuery, buildUpdateQuery } from '../query/sql-builder'\nimport { encodeRichtextFieldsForOpData } from '../serialization/op-data-encoding'\nimport {\n\tdeserializeRecord,\n\tserializeOperation,\n\tserializeRecord,\n\ttoRichtextReadShape,\n} from '../serialization/serializer'\nimport { validateUpdateStateMachine } from '../state-machine/state-validator'\nimport type { CollectionRecord, RawCollectionRow } from '../types'\nimport { toAtRestWriteData } from './secret-write'\nimport type { WriteEnv, WriteScope } from './write-context'\n\n/*\n * THE local write path (W6). Every local operation — a single-record write, each\n * entry of an `app.transaction` / `app.mutation` commit, and every referential\n * cascade — is built and persisted by the functions in this file, inside the\n * storage transaction that commits it:\n *\n * 1. the current row is read through the transaction handle, so validation\n * (existence, state machine) and atomic ops resolve against committed state;\n * 2. the sequence number comes from the transaction's SequenceBlock (reserved\n * with UPSERT ... RETURNING on `_kora_version_vector` inside the transaction,\n * end persisted before commit); transactions are serialized, so the numbers of\n * one commit form one contiguous block and two commits can never share one;\n * 3. the operation is built only AFTER its sequence number is reserved, and is\n * never re-stamped (protocol v2 hashes the sequence number into the id);\n * 4. the row is written with per-field version stamps and the operation row is\n * appended, in the same transaction.\n */\n\n/** An insert whose values are validated and in their at-rest form. */\nexport interface PreparedInsert {\n\treadonly collection: string\n\treadonly recordId: string\n\t/** Record-shaped, at-rest values (secret fields hashed / encrypted). */\n\treadonly data: Record<string, unknown>\n}\n\n/**\n * Validate insert data, fill auto fields and transform secrets. Pure with\n * respect to storage, so `app.transaction` can do it when the developer calls\n * `insert` (returning the new id immediately) and write it at commit.\n */\nexport async function prepareInsert(\n\tenv: WriteEnv,\n\tcollection: string,\n\tdata: Record<string, unknown>,\n): Promise<PreparedInsert> {\n\tconst definition = requireDefinition(env, collection)\n\tconst validated = validateRecord(collection, definition, data, 'insert')\n\tconst now = Date.now()\n\tfor (const [fieldName, descriptor] of Object.entries(definition.fields)) {\n\t\tif (descriptor.auto && descriptor.kind === 'timestamp') {\n\t\t\tvalidated[fieldName] = now\n\t\t}\n\t}\n\t// Secret fields reach their at-rest form BEFORE the operation is built, so\n\t// plaintext never enters the op log, the row, or the wire (STORE-4).\n\tconst writeData = await toAtRestWriteData(validated, definition, env.secretKeyProvider)\n\treturn { collection, recordId: generateUUIDv7(), data: writeData }\n}\n\n/** Result of writing one local operation. */\nexport interface WriteResult {\n\t/** The operation, or null when the write turned out to be a no-op. */\n\treadonly operation: Operation | null\n\t/** The record as stored after the write. */\n\treadonly record: CollectionRecord\n\t/** Operations created as side effects (referential cascades), in creation order. */\n\treadonly sideEffects: Operation[]\n}\n\n/**\n * Write a prepared insert inside `scope.tx`.\n */\nexport async function writeInsertInTx(\n\tenv: WriteEnv,\n\tscope: WriteScope,\n\tinsert: PreparedInsert,\n\textraCausalDeps: readonly string[] = [],\n): Promise<WriteResult> {\n\tconst definition = requireDefinition(env, insert.collection)\n\tconst operation = await buildLocalOperation(env, scope, {\n\t\ttype: 'insert',\n\t\tcollection: insert.collection,\n\t\trecordId: insert.recordId,\n\t\t// Binary richtext values are tagged as canonical JSON BEFORE hashing, so the\n\t\t// hash input, persisted JSON and wire payload are the identical value.\n\t\tdata: encodeRichtextFieldsForOpData(insert.data, definition.fields),\n\t\tpreviousData: null,\n\t\textraCausalDeps,\n\t})\n\n\tif (env.fold) {\n\t\t// Append, then merge: the row is the materialization of the record's state.\n\t\tawait appendOperationRow(scope, operation)\n\t\tawait env.fold.applyInTx(scope.tx, operation, 'local')\n\t} else {\n\t\tconst serialized = serializeRecord(insert.data, definition.fields)\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\tconst row: Record<string, unknown> = {\n\t\t\tid: insert.recordId,\n\t\t\t...serialized,\n\t\t\t_created_at: operation.timestamp.wallTime,\n\t\t\t_updated_at: operation.timestamp.wallTime,\n\t\t\t_version: version,\n\t\t\t// Every inserted field is stamped with this operation's version, so later\n\t\t\t// per-field LWW compares against a real writer, not the row fallback.\n\t\t\t_field_versions: stampFieldVersions(null, Object.keys(serialized), version),\n\t\t}\n\t\tconst rowInsert = buildInsertQuery(insert.collection, row)\n\t\tawait scope.tx.execute(rowInsert.sql, rowInsert.params)\n\t\tawait appendOperationRow(scope, operation)\n\t}\n\n\treturn {\n\t\toperation,\n\t\trecord: {\n\t\t\tid: insert.recordId,\n\t\t\t// A richtext value written as a string returns as the bytes reads return.\n\t\t\t...toRichtextReadShape(insert.data, definition.fields),\n\t\t\tcreatedAt: operation.timestamp.wallTime,\n\t\t\tupdatedAt: operation.timestamp.wallTime,\n\t\t},\n\t\tsideEffects: [],\n\t}\n}\n\n/**\n * Write a local update inside `scope.tx`.\n *\n * `data` must already be schema-validated (it may contain atomic op descriptors).\n * The state machine, atomic ops and `previousData` are all resolved against the\n * row read through the transaction, so concurrent writers serialize correctly\n * (STORE-9, NEW-STORE-2).\n */\nexport async function writeUpdateInTx(\n\tenv: WriteEnv,\n\tscope: WriteScope,\n\tcollection: string,\n\tid: string,\n\tdata: Record<string, unknown>,\n\textraCausalDeps: readonly string[] = [],\n): Promise<WriteResult> {\n\tconst definition = requireDefinition(env, collection)\n\tconst currentRow = await readLiveRow(scope, collection, id)\n\tif (!currentRow) {\n\t\tthrow new RecordNotFoundError(collection, id)\n\t}\n\tconst currentRecord = deserializeRecord(currentRow, definition.fields)\n\tconst allowed = validateUpdateStateMachine(collection, id, definition, currentRecord, data)\n\tif (Object.keys(allowed).length === 0) {\n\t\treturn { operation: null, record: currentRecord, sideEffects: [] }\n\t}\n\n\tconst previousData: Record<string, unknown> = {}\n\tconst resolved: Record<string, unknown> = {}\n\tconst atomicOps: Record<string, AtomicOp> = {}\n\tfor (const [key, value] of Object.entries(allowed)) {\n\t\tpreviousData[key] = currentRecord[key]\n\t\tif (isAtomicOp(value)) {\n\t\t\tresolved[key] = resolveAtomicOp(currentRecord[key], value)\n\t\t\t// The resolved value must be in the field's domain like any written value\n\t\t\t// (RT-87): `op.increment(0.5)` on a timestamp, an increment past the largest\n\t\t\t// finite number.\n\t\t\tconst descriptor = definition.fields[key]\n\t\t\tif (descriptor) assertResolvedFieldValue(collection, key, descriptor, resolved[key])\n\t\t\tatomicOps[key] = toAtomicOp(value)\n\t\t} else {\n\t\t\tresolved[key] = value\n\t\t}\n\t}\n\t// previousData already holds the stored (at-rest) values, so only the new\n\t// values need the secret transform.\n\tconst writeData = await toAtRestWriteData(resolved, definition, env.secretKeyProvider)\n\n\tconst operation = await buildLocalOperation(env, scope, {\n\t\ttype: 'update',\n\t\tcollection,\n\t\trecordId: id,\n\t\tdata: encodeRichtextFieldsForOpData(writeData, definition.fields),\n\t\tpreviousData: encodeRichtextFieldsForOpData(previousData, definition.fields),\n\t\textraCausalDeps,\n\t\t...(Object.keys(atomicOps).length > 0 ? { atomicOps } : {}),\n\t})\n\n\tif (env.fold) {\n\t\tawait appendOperationRow(scope, operation)\n\t\tawait env.fold.applyInTx(scope.tx, operation, 'local')\n\t} else {\n\t\tconst serializedChanges = serializeRecord(writeData, definition.fields)\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\t// A local edit is the newest writer of every field it touches (the HLC is past\n\t\t// every timestamp this device has seen), so each changed field gets its stamp.\n\t\tconst rowUpdate = buildUpdateQuery(collection, id, {\n\t\t\t...serializedChanges,\n\t\t\t_updated_at: operation.timestamp.wallTime,\n\t\t\t_version: version,\n\t\t\t_field_versions: stampFieldVersions(\n\t\t\t\tcurrentRow._field_versions,\n\t\t\t\tObject.keys(serializedChanges),\n\t\t\t\tversion,\n\t\t\t),\n\t\t})\n\t\tawait scope.tx.execute(rowUpdate.sql, rowUpdate.params)\n\t\tawait appendOperationRow(scope, operation)\n\t}\n\n\tconst updatedRow = await readLiveRow(scope, collection, id)\n\tif (!updatedRow) {\n\t\tthrow new RecordNotFoundError(collection, id)\n\t}\n\treturn {\n\t\toperation,\n\t\trecord: deserializeRecord(updatedRow, definition.fields),\n\t\tsideEffects: [],\n\t}\n}\n\n/** Options for {@link writeDeleteInTx}. */\nexport interface WriteDeleteOptions {\n\t/** Skip referential checks and cascades (the caller already applied them). */\n\treadonly skipReferentialEnforcement?: boolean\n\t/** Explicit causal parents (e.g. the delete that caused this cascade). */\n\treadonly extraCausalDeps?: readonly string[]\n}\n\n/**\n * Soft-delete a record inside `scope.tx`, then apply the schema's referential\n * policies (cascade, set-null, restrict) in the same transaction. Cascaded\n * operations are built through this same write path, after the parent, so their\n * sequence numbers follow the parent's in the commit's block (STORE-2).\n */\nexport async function writeDeleteInTx(\n\tenv: WriteEnv,\n\tscope: WriteScope,\n\tcollection: string,\n\tid: string,\n\toptions: WriteDeleteOptions = {},\n): Promise<WriteResult> {\n\tconst definition = requireDefinition(env, collection)\n\tconst currentRow = await readLiveRow(scope, collection, id)\n\tif (!currentRow) {\n\t\tthrow new RecordNotFoundError(collection, id)\n\t}\n\tconst operation = await buildLocalOperation(env, scope, {\n\t\ttype: 'delete',\n\t\tcollection,\n\t\trecordId: id,\n\t\tdata: null,\n\t\tpreviousData: null,\n\t\textraCausalDeps: options.extraCausalDeps ?? [],\n\t})\n\n\tif (!options.skipReferentialEnforcement && env.beforeLocalDelete) {\n\t\tawait env.beforeLocalDelete(operation, scope.tx)\n\t}\n\n\t// The row is tombstoned BEFORE side effects run, so a reference cycle\n\t// (self-referencing or mutually-referencing records) cannot cascade back\n\t// into this record.\n\tif (env.fold) {\n\t\tawait appendOperationRow(scope, operation)\n\t\tawait env.fold.applyInTx(scope.tx, operation, 'local')\n\t} else {\n\t\tconst version = serializeRowVersion(operation.timestamp)\n\t\tconst softDelete = buildSoftDeleteQuery(collection, id, operation.timestamp.wallTime, version)\n\t\tawait scope.tx.execute(softDelete.sql, softDelete.params)\n\t\tawait appendOperationRow(scope, operation)\n\t}\n\n\tconst sideEffects: Operation[] = []\n\tif (!options.skipReferentialEnforcement && env.relationEnforcer) {\n\t\tconst result = await env.relationEnforcer.enforceDelete(\n\t\t\tcollection,\n\t\t\tid,\n\t\t\tscope,\n\t\t\tenv,\n\t\t\toperation.id,\n\t\t)\n\t\tsideEffects.push(...result.operations)\n\t}\n\n\treturn {\n\t\toperation,\n\t\trecord: deserializeRecord(currentRow, definition.fields),\n\t\tsideEffects,\n\t}\n}\n\ninterface LocalOperationInput {\n\ttype: Operation['type']\n\tcollection: string\n\trecordId: string\n\tdata: Record<string, unknown> | null\n\tpreviousData: Record<string, unknown> | null\n\textraCausalDeps: readonly string[]\n\tatomicOps?: Record<string, AtomicOp>\n}\n\n/**\n * Reserve the next sequence number through the transaction, THEN build the\n * operation. The order matters: the operation is final the moment it exists.\n */\nasync function buildLocalOperation(\n\tenv: WriteEnv,\n\tscope: WriteScope,\n\tinput: LocalOperationInput,\n): Promise<Operation> {\n\tconst sequenceNumber = await scope.sequence.take()\n\tconst causalDeps = scope.causal.depsFor(input.collection, input.extraCausalDeps)\n\tconst operation = await createOperation(\n\t\t{\n\t\t\tnodeId: env.nodeId,\n\t\t\ttype: input.type,\n\t\t\tcollection: input.collection,\n\t\t\trecordId: input.recordId,\n\t\t\tdata: input.data,\n\t\t\tpreviousData: input.previousData,\n\t\t\tsequenceNumber,\n\t\t\tcausalDeps,\n\t\t\tschemaVersion: env.schema.version,\n\t\t\t...(input.atomicOps !== undefined ? { atomicOps: input.atomicOps } : {}),\n\t\t\t...(env.transactionId !== undefined ? { transactionId: env.transactionId } : {}),\n\t\t\t...(env.mutationName !== undefined ? { mutationName: env.mutationName } : {}),\n\t\t},\n\t\tenv.clock,\n\t)\n\t// Every operation must fit the server's maxOperationBytes (RT-86): refused here, in\n\t// the write's transaction (nothing is written), rather than accepted and then refused\n\t// by the server.\n\tconst maxBytes = env.maxOperationBytes ?? DEFAULT_MAX_OPERATION_BYTES\n\tconst bytes = measureOperationBytes(operation)\n\tif (bytes > maxBytes) {\n\t\tthrow new OperationTooLargeError(input.collection, input.recordId, bytes, maxBytes)\n\t}\n\tscope.causal.record(input.collection, operation.id)\n\treturn operation\n}\n\nasync function appendOperationRow(scope: WriteScope, operation: Operation): Promise<void> {\n\tconst opInsert = buildInsertQuery(\n\t\t`_kora_ops_${operation.collection}`,\n\t\tserializeOperation(operation) as unknown as Record<string, unknown>,\n\t)\n\tawait scope.tx.execute(opInsert.sql, opInsert.params)\n}\n\nasync function readLiveRow(\n\tscope: WriteScope,\n\tcollection: string,\n\tid: string,\n): Promise<RawCollectionRow | undefined> {\n\tconst rows = await scope.tx.query<RawCollectionRow>(\n\t\t`SELECT * FROM ${quoteIdent(collection)} WHERE id = ? AND _deleted = 0`,\n\t\t[id],\n\t)\n\treturn rows[0]\n}\n\nfunction requireDefinition(env: WriteEnv, collection: string): CollectionDefinition {\n\tconst definition = env.schema.collections[collection]\n\tif (!definition) {\n\t\tthrow new KoraError(\n\t\t\t`Unknown collection \"${collection}\". Available: ${Object.keys(env.schema.collections).join(', ')}`,\n\t\t\t'UNKNOWN_COLLECTION',\n\t\t\t{ collection },\n\t\t)\n\t}\n\treturn definition\n}\n","import { EMPTY_ROW_VERSION } from './row-version'\n\n/**\n * Per-field last-writer versions for a materialized row.\n *\n * Each entry maps a field name to a serialized HLC timestamp (the same\n * lexicographically-sortable form used by the row `_version` column). Because\n * the serialized form sorts identically to {@link HybridLogicalClock.compare},\n * per-field last-write-wins is a plain string comparison: the greater string is\n * the newer writer. This makes field-level convergence deterministic and\n * independent of the order operations happen to arrive in.\n */\nexport type FieldVersions = Record<string, string>\n\n/**\n * Parse the `_field_versions` JSON column into a map. Tolerates null, empty,\n * and malformed values by returning an empty map — a row that predates\n * per-field tracking simply has no field versions and falls back to `_version`.\n */\nexport function parseFieldVersions(raw: unknown): FieldVersions {\n\tif (typeof raw !== 'string' || raw.length === 0) {\n\t\treturn {}\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as unknown\n\t\tif (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\treturn {}\n\t\t}\n\t\tconst result: FieldVersions = {}\n\t\tfor (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n\t\t\tif (typeof value === 'string') {\n\t\t\t\tresult[key] = value\n\t\t\t}\n\t\t}\n\t\treturn result\n\t} catch {\n\t\treturn {}\n\t}\n}\n\n/**\n * Serialize a field-version map for storage in the `_field_versions` column.\n */\nexport function serializeFieldVersions(versions: FieldVersions): string {\n\treturn JSON.stringify(versions)\n}\n\n/**\n * Build the initial field-version map for an insert: every supplied field is\n * stamped with the operation's version.\n */\nexport function fieldVersionsForFields(\n\tfieldNames: Iterable<string>,\n\tversion: string,\n): FieldVersions {\n\tconst versions: FieldVersions = {}\n\tfor (const field of fieldNames) {\n\t\tversions[field] = version\n\t}\n\treturn versions\n}\n\n/**\n * The effective stored version for a field: its own tracked version if present,\n * otherwise the row-level `_version` (so rows written before per-field tracking\n * still compare correctly), otherwise the empty sentinel (older than anything).\n */\nexport function effectiveFieldVersion(\n\tcurrent: FieldVersions,\n\tfield: string,\n\trowVersion: string | undefined,\n): string {\n\tconst own = current[field]\n\tif (own !== undefined) {\n\t\treturn own\n\t}\n\treturn rowVersion ?? EMPTY_ROW_VERSION\n}\n\n/**\n * Result of resolving an incoming operation against the current per-field\n * versions of a row.\n */\nexport interface PerFieldLwwResult {\n\t/** Fields the incoming operation wins (strictly newer than the stored version). */\n\twinners: string[]\n\t/** The merged field-version map to persist (winners advanced to `incomingVersion`). */\n\tmerged: FieldVersions\n}\n\n/**\n * Resolve per-field last-write-wins for an incoming operation.\n *\n * For each incoming field, the operation wins only when its version is strictly\n * greater than the field's effective stored version. Ties (identical version,\n * i.e. the same operation re-applied) are losses, keeping the resolution\n * idempotent. The comparison is a total order, so every node resolves the same\n * winner regardless of the order operations arrive.\n *\n * @param current - Parsed `_field_versions` of the target row\n * @param incomingFields - Field names carried by the incoming operation\n * @param incomingVersion - Serialized HLC version of the incoming operation\n * @param rowVersion - The row's `_version` (fallback for untracked fields)\n */\nexport function resolvePerFieldLww(\n\tcurrent: FieldVersions,\n\tincomingFields: Iterable<string>,\n\tincomingVersion: string,\n\trowVersion: string | undefined,\n): PerFieldLwwResult {\n\tconst winners: string[] = []\n\tconst merged: FieldVersions = { ...current }\n\tfor (const field of incomingFields) {\n\t\tconst stored = effectiveFieldVersion(current, field, rowVersion)\n\t\tif (incomingVersion > stored) {\n\t\t\twinners.push(field)\n\t\t\tmerged[field] = incomingVersion\n\t\t}\n\t\t// Losing fields are left untouched: their existing tracked version (or the\n\t\t// row-level `_version` fallback) already reflects the newer writer.\n\t}\n\treturn { winners, merged }\n}\n\n/**\n * Stamp `fields` with `version` on top of a stored `_field_versions` value and\n * return the serialized column. This is the one per-field stamp every local\n * write uses (insert, update, transaction entries, cascades), so every field a\n * device writes carries the HLC of the operation that wrote it.\n *\n * Stamps only move forward: a field already holding a newer version keeps it.\n * The map is keyed by field name with one HLC register per field, the shape the\n * per-field fold (W7) builds on.\n *\n * @param currentRaw - The stored `_field_versions` column (or null for a new row)\n * @param fields - Field names this write sets\n * @param version - The writing operation's serialized HLC version\n */\nexport function stampFieldVersions(\n\tcurrentRaw: unknown,\n\tfields: Iterable<string>,\n\tversion: string,\n): string {\n\tconst versions = parseFieldVersions(currentRaw)\n\tfor (const field of fields) {\n\t\tconst existing = versions[field]\n\t\tif (existing === undefined || existing < version) {\n\t\t\tversions[field] = version\n\t\t}\n\t}\n\treturn serializeFieldVersions(versions)\n}\n","import { KoraError, validateTransition } from '@korajs/core'\nimport type {\n\tCollectionDefinition,\n\tStateMachineConstraint,\n\tStateMachineDefinition,\n} from '@korajs/core'\n\n/**\n * Error thrown when a local mutation attempts an invalid state transition.\n *\n * Contains enough context to debug without reproduction:\n * the collection, record, field, current state, attempted state, and allowed transitions.\n */\nexport class InvalidStateTransitionError extends KoraError {\n\tconstructor(\n\t\tpublic readonly collection: string,\n\t\tpublic readonly recordId: string,\n\t\tpublic readonly field: string,\n\t\tpublic readonly fromState: string,\n\t\tpublic readonly toState: string,\n\t\tpublic readonly allowedStates: string[],\n\t) {\n\t\tsuper(\n\t\t\t`Invalid state transition in collection \"${collection}\": ` +\n\t\t\t\t`cannot transition field \"${field}\" from \"${fromState}\" to \"${toState}\". ` +\n\t\t\t\t`Allowed transitions from \"${fromState}\": ${allowedStates.length > 0 ? allowedStates.join(', ') : '(none -- terminal state)'}`,\n\t\t\t'INVALID_STATE_TRANSITION',\n\t\t\t{ collection, recordId, field, fromState, toState, allowedStates },\n\t\t)\n\t\tthis.name = 'InvalidStateTransitionError'\n\t}\n}\n\n/**\n * Validates a state machine transition for a local mutation (insert or update).\n *\n * For inserts: validates that the initial state (from the data or the default value)\n * is a known state in the state machine.\n *\n * For updates: looks up the current state field value and checks whether\n * the transition to the new value is allowed.\n *\n * @param collectionName - Name of the collection\n * @param recordId - The record being mutated\n * @param stateMachine - The state machine definition\n * @param currentState - The current value of the state field (null for inserts)\n * @param newState - The new value being set for the state field\n * @returns An object indicating whether the transition is valid, and if not, the allowed states.\n * When `onInvalidTransition` is 'last-valid-state', callers should suppress the field update\n * rather than throwing.\n */\nexport function validateStateTransition(\n\tcollectionName: string,\n\trecordId: string,\n\tstateMachine: StateMachineDefinition,\n\tcurrentState: string | null,\n\tnewState: string,\n): { valid: boolean; allowedStates: string[] } {\n\t// For inserts, any valid enum value is acceptable as the initial state\n\t// (schema validation already ensures the value is a valid enum value)\n\tif (currentState === null) {\n\t\treturn { valid: true, allowedStates: [] }\n\t}\n\n\t// Same-state transitions are always valid (idempotent updates)\n\tif (currentState === newState) {\n\t\treturn { valid: true, allowedStates: stateMachine.transitions[currentState] ?? [] }\n\t}\n\n\tconst constraint: StateMachineConstraint = {\n\t\tfield: stateMachine.field,\n\t\tcollection: collectionName,\n\t\ttransitions: stateMachine.transitions,\n\t}\n\tconst transitionResult = validateTransition(constraint, currentState, newState)\n\tif (transitionResult.valid) {\n\t\treturn { valid: true, allowedStates: transitionResult.allowedTargets }\n\t}\n\n\tconst allowedStates = transitionResult.allowedTargets\n\n\tif (stateMachine.onInvalidTransition === 'reject') {\n\t\tthrow new InvalidStateTransitionError(\n\t\t\tcollectionName,\n\t\t\trecordId,\n\t\t\tstateMachine.field,\n\t\t\tcurrentState,\n\t\t\tnewState,\n\t\t\tallowedStates,\n\t\t)\n\t}\n\n\t// 'last-valid-state': return invalid so the caller can suppress the field update\n\treturn { valid: false, allowedStates }\n}\n\n/**\n * Checks whether a collection update data object contains a change to the state machine field,\n * and if so, validates the transition.\n *\n * If the state field is not in the update data, returns the data unchanged.\n * If the transition is invalid and mode is 'last-valid-state', removes the field from the data.\n * If the transition is invalid and mode is 'reject', throws InvalidStateTransitionError.\n *\n * @param collectionName - Name of the collection\n * @param recordId - The record being updated\n * @param collectionDef - The collection definition from the schema\n * @param currentRecord - The current record data (must include the state field)\n * @param updateData - The partial update data\n * @returns The update data, potentially with the state field removed if invalid and mode is 'last-valid-state'\n */\nexport function validateUpdateStateMachine(\n\tcollectionName: string,\n\trecordId: string,\n\tcollectionDef: CollectionDefinition,\n\tcurrentRecord: Record<string, unknown>,\n\tupdateData: Record<string, unknown>,\n): Record<string, unknown> {\n\tlet result = updateData\n\tconst stateMachine = collectionDef.stateMachine\n\tif (stateMachine !== undefined) {\n\t\tresult = applyStateMachine(collectionName, recordId, stateMachine, currentRecord, result)\n\t}\n\t// Field-level `t.enum(...).transitions()` maps are state machines too, in the\n\t// default 'reject' mode (NEW-STORE-3). A collection-level machine on the same\n\t// field takes precedence.\n\tfor (const [field, descriptor] of Object.entries(collectionDef.fields)) {\n\t\tif (descriptor.kind !== 'enum' || !descriptor.transitions) continue\n\t\tif (stateMachine?.field === field) continue\n\t\tresult = applyStateMachine(\n\t\t\tcollectionName,\n\t\t\trecordId,\n\t\t\t{ field, transitions: descriptor.transitions, onInvalidTransition: 'reject' },\n\t\t\tcurrentRecord,\n\t\t\tresult,\n\t\t)\n\t}\n\treturn result\n}\n\nfunction applyStateMachine(\n\tcollectionName: string,\n\trecordId: string,\n\tstateMachine: StateMachineDefinition,\n\tcurrentRecord: Record<string, unknown>,\n\tupdateData: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst stateField = stateMachine.field\n\tif (!(stateField in updateData)) {\n\t\t// State field not being changed -- no validation needed\n\t\treturn updateData\n\t}\n\n\tconst currentState = currentRecord[stateField]\n\tconst newState = updateData[stateField]\n\n\t// Both values must be strings for state machine validation\n\tif (typeof currentState !== 'string' || typeof newState !== 'string') {\n\t\treturn updateData\n\t}\n\n\tconst result = validateStateTransition(\n\t\tcollectionName,\n\t\trecordId,\n\t\tstateMachine,\n\t\tcurrentState,\n\t\tnewState,\n\t)\n\n\tif (result.valid) {\n\t\treturn updateData\n\t}\n\n\t// Mode is 'last-valid-state': silently remove the state field from the update\n\tconst filtered = { ...updateData }\n\tdelete filtered[stateField]\n\n\t// If no fields remain after removing the state field, the update becomes a no-op.\n\t// Return the empty object -- the caller can decide whether to proceed.\n\treturn filtered\n}\n","import type { CollectionDefinition, SecretKeyProvider } from '@korajs/core'\nimport { transformSecretFieldsForWrite } from '@korajs/core'\n\n/**\n * Convert the values a local write is about to persist into their at-rest form.\n *\n * Every local write path (single-record insert/update and the buffered\n * `TransactionContext` used by `app.transaction` / `app.mutation`) MUST route\n * its data through this helper BEFORE the operation is built, so plaintext\n * secrets never reach the operation log, the materialized row, or the wire.\n * Keeping one helper means a new write path cannot forget the transform\n * without also bypassing this function (STORE-4).\n *\n * @param data - Validated (and atomic-op-resolved) field values for the write\n * @param definition - The collection definition, used to find secret fields\n * @param secretKeyProvider - Key provider for `t.secret()` encrypted fields\n * @returns A copy of `data` with secret fields hashed or encrypted\n */\nexport function toAtRestWriteData(\n\tdata: Record<string, unknown>,\n\tdefinition: CollectionDefinition,\n\tsecretKeyProvider: SecretKeyProvider | undefined,\n): Promise<Record<string, unknown>> {\n\treturn transformSecretFieldsForWrite(data, definition, secretKeyProvider)\n}\n","import type { CollectionRecord } from '../types'\nimport { runLocalWrite } from './run-local-write'\nimport type { LocalMutationContext } from './types'\nimport { prepareInsert, writeInsertInTx } from './write-ops'\n\n/**\n * Insert a record and persist its operation, atomically, through the single\n * local write path (sequence reserved and operation built inside the write\n * transaction).\n */\nexport async function executeInsert(\n\tctx: LocalMutationContext,\n\tdata: Record<string, unknown>,\n): Promise<CollectionRecord> {\n\tconst prepared = await prepareInsert(ctx, ctx.collection, data)\n\tconst result = await runLocalWrite(ctx, (scope) =>\n\t\twriteInsertInTx(ctx, scope, prepared, ctx.extraCausalDeps ?? []),\n\t)\n\treturn result.record\n}\n","import { validateRecord } from '@korajs/core'\nimport type { CollectionRecord } from '../types'\nimport { runLocalWrite } from './run-local-write'\nimport type { LocalMutationContext } from './types'\nimport { writeUpdateInTx } from './write-ops'\n\n/**\n * Update a record and persist its operation, atomically. The row read, the\n * state-machine check and atomic-op resolution all happen inside the write\n * transaction, so concurrent increments compose (STORE-9).\n */\nexport async function executeUpdate(\n\tctx: LocalMutationContext,\n\tid: string,\n\tdata: Record<string, unknown>,\n): Promise<CollectionRecord> {\n\tconst validated = validateRecord(ctx.collection, ctx.definition, data, 'update')\n\tconst result = await runLocalWrite(ctx, (scope) =>\n\t\twriteUpdateInTx(ctx, scope, ctx.collection, id, validated, ctx.extraCausalDeps ?? []),\n\t)\n\treturn result.record\n}\n","import type { Operation } from '@korajs/core'\nimport { runLocalWrite } from './run-local-write'\nimport type { LocalMutationContext } from './types'\nimport { writeDeleteInTx } from './write-ops'\n\nexport interface ExecuteDeleteOptions {\n\t/** When true, skip referential checks and cascades (caller already enforced them). */\n\tskipReferentialEnforcement?: boolean\n}\n\n/**\n * Soft-delete a record and persist the operation log entry atomically, together\n * with any referential side effects (cascades / set-null), in one transaction.\n * Returns the primary delete operation followed by the side-effect operations.\n */\nexport async function executeDelete(\n\tctx: LocalMutationContext,\n\tid: string,\n\toptions?: ExecuteDeleteOptions,\n): Promise<Operation[]> {\n\tconst result = await runLocalWrite(ctx, (scope) =>\n\t\twriteDeleteInTx(ctx, scope, ctx.collection, id, {\n\t\t\tskipReferentialEnforcement: options?.skipReferentialEnforcement ?? false,\n\t\t\textraCausalDeps: ctx.extraCausalDeps ?? [],\n\t\t}),\n\t)\n\treturn result.operation ? [result.operation, ...result.sideEffects] : result.sideEffects\n}\n","import type { Operation, VersionVector } from '@korajs/core'\nimport { createVersionVector } from '@korajs/core'\nimport type { MetaRow, StorageAdapter } from '../types'\n\nexport const LAST_ACKED_SERVER_VECTOR_META_KEY = 'last_acked_server_vector'\nexport const DELTA_CURSOR_META_KEY = 'delta_cursor'\nexport const DELIVERY_WATERMARK_META_KEY = 'delivery_watermark'\nexport const NODE_TOKEN_META_KEY = 'sync_node_token'\nexport const AUTHORITATIVE_NODE_IDS_META_KEY = 'sync_authoritative_node_ids'\n\n/**\n * Serialize a version vector for `_kora_meta` storage.\n */\nexport function serializeVersionVectorToMeta(vector: VersionVector): string {\n\tconst record: Record<string, number> = {}\n\tfor (const [nodeId, seq] of vector) {\n\t\trecord[nodeId] = seq\n\t}\n\treturn JSON.stringify(record)\n}\n\n/**\n * Deserialize a version vector from `_kora_meta`.\n */\nexport function deserializeVersionVectorFromMeta(value: string): VersionVector {\n\tconst parsed = JSON.parse(value) as Record<string, number>\n\tconst vector = createVersionVector()\n\tfor (const [nodeId, seq] of Object.entries(parsed)) {\n\t\tif (typeof seq === 'number' && seq >= 0) {\n\t\t\tvector.set(nodeId, seq)\n\t\t}\n\t}\n\treturn vector\n}\n\n/**\n * Merge two version vectors, keeping the maximum sequence per node.\n */\nexport function mergeVersionVectors(a: VersionVector, b: VersionVector): VersionVector {\n\tconst merged = new Map(a)\n\tfor (const [nodeId, seq] of b) {\n\t\tmerged.set(nodeId, Math.max(merged.get(nodeId) ?? 0, seq))\n\t}\n\treturn merged\n}\n\n/**\n * Operations present locally but not yet on the server (per server version vector).\n */\nexport async function collectOperationsAheadOfServer(\n\tlocalVector: VersionVector,\n\tserverVector: VersionVector,\n\tfetchRange: (nodeId: string, fromSeq: number, toSeq: number) => Promise<Operation[]>,\n): Promise<Operation[]> {\n\tconst missing: Operation[] = []\n\tfor (const [nodeId, localSeq] of localVector) {\n\t\tconst serverSeq = serverVector.get(nodeId) ?? 0\n\t\tif (localSeq > serverSeq) {\n\t\t\tconst ops = await fetchRange(nodeId, serverSeq + 1, localSeq)\n\t\t\tmissing.push(...ops)\n\t\t}\n\t}\n\tmissing.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\treturn missing\n}\n\n/**\n * Load persisted last-acked server vector from `_kora_meta`.\n */\nexport async function loadLastAckedServerVector(adapter: StorageAdapter): Promise<VersionVector> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tLAST_ACKED_SERVER_VECTOR_META_KEY,\n\t])\n\tconst row = rows[0]\n\tif (!row?.value) {\n\t\treturn createVersionVector()\n\t}\n\ttry {\n\t\treturn deserializeVersionVectorFromMeta(row.value)\n\t} catch {\n\t\treturn createVersionVector()\n\t}\n}\n\n/**\n * Persist the last-acked server version vector to `_kora_meta`.\n */\nexport async function saveLastAckedServerVector(\n\tadapter: StorageAdapter,\n\tvector: VersionVector,\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tLAST_ACKED_SERVER_VECTOR_META_KEY,\n\t\tserializeVersionVectorToMeta(vector),\n\t])\n}\n\n/**\n * Load the per-device node token the sync server issued for this node id (RT-12).\n */\nexport async function loadNodeToken(\n\tadapter: StorageAdapter,\n\tnodeId?: string,\n): Promise<string | null> {\n\tif (nodeId !== undefined) {\n\t\tconst own = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\t\tnodeTokenKey(nodeId),\n\t\t])\n\t\tif (own[0]) return own[0].value\n\t\t// The legacy single key belongs to the database's node id (`_kora_meta.node_id`).\n\t\tconst meta = await adapter.query<MetaRow>(\"SELECT value FROM _kora_meta WHERE key = 'node_id'\")\n\t\tif (meta[0]?.value !== nodeId) return null\n\t}\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tNODE_TOKEN_META_KEY,\n\t])\n\treturn rows[0]?.value ?? null\n}\n\n/** `_kora_meta` key of one node id's token (RT-38/RT-40: a database may use several). */\nexport function nodeTokenKey(nodeId: string): string {\n\treturn `${NODE_TOKEN_META_KEY}:${nodeId}`\n}\n\n/**\n * Persist the per-device node token next to the node id in `_kora_meta`. With a node\n * id, the token is stored under that node's own key: a database that uses several node\n * ids (a rotated identity, per-tab isolation) keeps each node's token.\n */\nexport async function saveNodeToken(\n\tadapter: StorageAdapter,\n\ttoken: string,\n\tnodeId?: string,\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tnodeId === undefined ? NODE_TOKEN_META_KEY : nodeTokenKey(nodeId),\n\t\ttoken,\n\t])\n}\n\n/**\n * Load a persisted delta cursor for resuming paginated initial sync.\n */\nexport async function loadDeltaCursor(adapter: StorageAdapter): Promise<string | null> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tDELTA_CURSOR_META_KEY,\n\t])\n\treturn rows[0]?.value ?? null\n}\n\n/**\n * Persist or clear the delta cursor in `_kora_meta`.\n */\nexport async function saveDeltaCursor(\n\tadapter: StorageAdapter,\n\tcursor: string | null,\n): Promise<void> {\n\tif (cursor === null) {\n\t\tawait adapter.execute('DELETE FROM _kora_meta WHERE key = ?', [DELTA_CURSOR_META_KEY])\n\t\treturn\n\t}\n\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tDELTA_CURSOR_META_KEY,\n\t\tcursor,\n\t])\n}\n\n/**\n * The `_kora_meta` key for a view's delivery watermark. The default (empty) signature\n * uses the bare key for backward compatibility; other views suffix the signature.\n */\nexport function deliveryWatermarkKey(signature: string): string {\n\treturn signature === ''\n\t\t? DELIVERY_WATERMARK_META_KEY\n\t\t: `${DELIVERY_WATERMARK_META_KEY}:${signature}`\n}\n\n/**\n * Load the persisted delivery watermark for a view signature (0 when none recorded).\n */\nexport async function loadDeliveryWatermark(\n\tadapter: StorageAdapter,\n\tsignature: string,\n): Promise<number> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tdeliveryWatermarkKey(signature),\n\t])\n\tconst value = rows[0]?.value\n\tif (value === undefined || value === null) {\n\t\treturn 0\n\t}\n\tconst parsed = Number(value)\n\treturn Number.isFinite(parsed) && parsed >= 0 ? parsed : 0\n}\n\n/**\n * Persist the delivery watermark for a view signature in `_kora_meta`.\n */\nexport async function saveDeliveryWatermark(\n\tadapter: StorageAdapter,\n\tsignature: string,\n\twatermark: number,\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tdeliveryWatermarkKey(signature),\n\t\tString(watermark),\n\t])\n}\n\n/**\n * Delete a persisted view watermark. Dropping a watermark is always safe: the view simply\n * back-fills from 0 (deduplicated) the next time it is visited. This bounds the number of\n * `_kora_meta` rows an app that churns through many distinct views can accumulate.\n */\nexport async function deleteDeliveryWatermark(\n\tadapter: StorageAdapter,\n\tsignature: string,\n): Promise<void> {\n\tawait adapter.execute('DELETE FROM _kora_meta WHERE key = ?', [deliveryWatermarkKey(signature)])\n}\n\n/**\n * Load every persisted view watermark, keyed by signature (the default view is '').\n */\nexport async function loadAllDeliveryWatermarks(\n\tadapter: StorageAdapter,\n): Promise<Record<string, number>> {\n\tconst rows = await adapter.query<MetaRow & { key: string }>(\n\t\t'SELECT key, value FROM _kora_meta WHERE key = ? OR key LIKE ?',\n\t\t[DELIVERY_WATERMARK_META_KEY, `${DELIVERY_WATERMARK_META_KEY}:%`],\n\t)\n\tconst result: Record<string, number> = {}\n\tconst prefix = `${DELIVERY_WATERMARK_META_KEY}:`\n\tfor (const row of rows) {\n\t\tconst signature = row.key === DELIVERY_WATERMARK_META_KEY ? '' : row.key.slice(prefix.length)\n\t\tconst parsed = Number(row.value)\n\t\tif (Number.isFinite(parsed) && parsed >= 0) {\n\t\t\tresult[signature] = parsed\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Persist the node ids the sync server named authoritative (protocol v2): their\n * operations are server-authored. Stored as a JSON array in `_kora_meta`.\n */\nexport async function saveAuthoritativeNodeIds(\n\tadapter: StorageAdapter,\n\tnodeIds: string[],\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tAUTHORITATIVE_NODE_IDS_META_KEY,\n\t\tJSON.stringify(nodeIds),\n\t])\n}\n\n/** `_kora_meta` key of the explicit authoritative ids a server revoked (RT-81). */\nconst REVOKED_AUTHORITATIVE_NODE_IDS_META_KEY = 'sync_revoked_authoritative_node_ids'\n\n/**\n * Persist the explicit authoritative node ids a sync server revoked (RT-81). A revoked\n * id is never authoritative again on this device, whatever a later handshake lists.\n */\nexport async function saveRevokedAuthoritativeNodeIds(\n\tadapter: StorageAdapter,\n\tnodeIds: string[],\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tREVOKED_AUTHORITATIVE_NODE_IDS_META_KEY,\n\t\tJSON.stringify(nodeIds),\n\t])\n}\n\n/** Load the persisted revoked authoritative node ids (empty when none, or unreadable). */\nexport async function loadRevokedAuthoritativeNodeIds(adapter: StorageAdapter): Promise<string[]> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tREVOKED_AUTHORITATIVE_NODE_IDS_META_KEY,\n\t])\n\tconst raw = rows[0]?.value\n\tif (raw === undefined) return []\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(raw)\n\t\tif (Array.isArray(parsed)) return parsed.filter((id): id is string => typeof id === 'string')\n\t} catch {\n\t\t// Unreadable: no revocation known; the next handshake that carries one rewrites it.\n\t}\n\treturn []\n}\n\n/**\n * Load the persisted authoritative node ids, or null when no protocol-2 server ever\n * answered (or the stored value is unreadable).\n */\nexport async function loadAuthoritativeNodeIds(adapter: StorageAdapter): Promise<string[] | null> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tAUTHORITATIVE_NODE_IDS_META_KEY,\n\t])\n\tconst raw = rows[0]?.value\n\tif (raw === undefined) return null\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(raw)\n\t\tif (Array.isArray(parsed) && parsed.every((id) => typeof id === 'string')) {\n\t\t\treturn parsed as string[]\n\t\t}\n\t} catch {\n\t\t// Unreadable: treated as never received; the next handshake rewrites it.\n\t}\n\treturn null\n}\n","import type { Operation } from '@korajs/core'\nimport type { MetaRow, StorageAdapter } from '../types'\nimport { deliveryWatermarkKey } from './sync-state'\n\n/** `_kora_meta` key of this device's contiguous acknowledged own-operation prefix (W3). */\nexport const OWN_ACKED_THROUGH_META_KEY = 'own_acked_through'\n/** `_kora_meta` key of the downlink scope the sync server last accepted (SYNC-11). */\nexport const ACCEPTED_DOWNLINK_SCOPE_META_KEY = 'accepted_downlink_scope'\n\n/**\n * Durable inbound quarantine (W4): delivered operations the client deliberately did not\n * apply, kept so they are never silently lost and can be replayed. Created on first use\n * so existing databases need no migration.\n */\nconst UNAPPLIED_OPS_DDL =\n\t'CREATE TABLE IF NOT EXISTS _kora_unapplied_ops (\\n' +\n\t' op_id TEXT PRIMARY KEY NOT NULL,\\n' +\n\t' collection TEXT NOT NULL,\\n' +\n\t' delivery_seq INTEGER,\\n' +\n\t' code TEXT NOT NULL,\\n' +\n\t' message TEXT NOT NULL,\\n' +\n\t' payload TEXT NOT NULL,\\n' +\n\t' quarantined_at INTEGER NOT NULL\\n' +\n\t')'\n\n/** A delivered operation the client did not apply, as stored in `_kora_unapplied_ops`. */\nexport interface UnappliedOperation {\n\t/** The operation as delivered (still encrypted when decryption failed). */\n\toperation: Operation\n\t/** Delivery sequence of the batch that carried it, or null for a legacy batch. */\n\tdeliverySequence: number | null\n\t/** Machine-readable reason. */\n\tcode: string\n\t/** Human-readable explanation. */\n\tmessage: string\n\t/** Wall-clock time (ms) it was quarantined. Display only. */\n\tquarantinedAt: number\n}\n\ninterface UnappliedRow {\n\top_id: string\n\tdelivery_seq: number | null\n\tcode: string\n\tmessage: string\n\tpayload: string\n\tquarantined_at: number\n}\n\nconst ensuredAdapters = new WeakSet<StorageAdapter>()\n\nasync function ensureUnappliedTable(adapter: StorageAdapter): Promise<void> {\n\tif (ensuredAdapters.has(adapter)) return\n\tawait adapter.execute(UNAPPLIED_OPS_DDL)\n\tensuredAdapters.add(adapter)\n}\n\n/**\n * Record quarantined operations and, when given, advance a view's delivery watermark in\n * the SAME transaction, so the watermark can never pass an operation that is neither\n * applied nor recorded here (the W4 invariant).\n */\nexport async function saveUnappliedOperations(\n\tadapter: StorageAdapter,\n\tentries: UnappliedOperation[],\n\twatermark?: { signature: string; watermark: number },\n): Promise<void> {\n\tawait ensureUnappliedTable(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tfor (const entry of entries) {\n\t\t\tawait tx.execute(\n\t\t\t\t'INSERT OR REPLACE INTO _kora_unapplied_ops (op_id, collection, delivery_seq, code, message, payload, quarantined_at) VALUES (?, ?, ?, ?, ?, ?, ?)',\n\t\t\t\t[\n\t\t\t\t\tentry.operation.id,\n\t\t\t\t\tentry.operation.collection,\n\t\t\t\t\tentry.deliverySequence,\n\t\t\t\t\tentry.code,\n\t\t\t\t\tentry.message,\n\t\t\t\t\tJSON.stringify(entry.operation),\n\t\t\t\t\tentry.quarantinedAt,\n\t\t\t\t],\n\t\t\t)\n\t\t}\n\t\tif (watermark) {\n\t\t\tawait tx.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\t\t\tdeliveryWatermarkKey(watermark.signature),\n\t\t\t\tString(watermark.watermark),\n\t\t\t])\n\t\t}\n\t})\n}\n\n/** Every quarantined operation, oldest delivery first. */\nexport async function loadUnappliedOperations(\n\tadapter: StorageAdapter,\n): Promise<UnappliedOperation[]> {\n\tawait ensureUnappliedTable(adapter)\n\tconst rows = await adapter.query<UnappliedRow>(\n\t\t'SELECT op_id, delivery_seq, code, message, payload, quarantined_at FROM _kora_unapplied_ops ORDER BY delivery_seq ASC, quarantined_at ASC',\n\t)\n\treturn rows.map((row) => ({\n\t\toperation: JSON.parse(row.payload) as Operation,\n\t\tdeliverySequence: row.delivery_seq === null ? null : Number(row.delivery_seq),\n\t\tcode: row.code,\n\t\tmessage: row.message,\n\t\tquarantinedAt: Number(row.quarantined_at),\n\t}))\n}\n\n/** Remove quarantined operations by operation id (applied on replay, or reconciled). */\nexport async function removeUnappliedOperations(\n\tadapter: StorageAdapter,\n\toperationIds: string[],\n): Promise<void> {\n\tif (operationIds.length === 0) return\n\tawait ensureUnappliedTable(adapter)\n\tconst placeholders = operationIds.map(() => '?').join(', ')\n\tawait adapter.execute(\n\t\t`DELETE FROM _kora_unapplied_ops WHERE op_id IN (${placeholders})`,\n\t\toperationIds,\n\t)\n}\n\n/** `_kora_meta` key of a node's prefix when the legacy single key holds another node's. */\nexport function ownAckedThroughKey(nodeId: string): string {\n\treturn `${OWN_ACKED_THROUGH_META_KEY}:${nodeId}`\n}\n\nfunction parseLegacyPrefix(value: string | undefined): { nodeId: string; sequence: number } | null {\n\tif (value === undefined || value === null) return null\n\ttry {\n\t\tconst parsed = JSON.parse(value) as { nodeId?: unknown; sequence?: unknown }\n\t\tif (typeof parsed.nodeId !== 'string') return null\n\t\tconst sequence =\n\t\t\ttypeof parsed.sequence === 'number' && parsed.sequence >= 0 ? parsed.sequence : null\n\t\treturn sequence === null ? null : { nodeId: parsed.nodeId, sequence }\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Load the acknowledged own-operation prefix for a node id. Null when none was ever\n * recorded for this node (an upgrade from a release that persisted a max instead).\n *\n * The first node a database tracks keeps the single legacy key (`own_acked_through`, as\n * earlier releases wrote it); any other local node (a rotated node, another tab's node\n * under per-tab isolation, RT-40) has its own key, so tracking one node never erases\n * another node's prefix.\n */\nexport async function loadOwnAckedThrough(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n): Promise<number | null> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tOWN_ACKED_THROUGH_META_KEY,\n\t])\n\tconst legacy = parseLegacyPrefix(rows[0]?.value)\n\tif (legacy && legacy.nodeId === nodeId) return legacy.sequence\n\tconst own = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\townAckedThroughKey(nodeId),\n\t])\n\tconst value = own[0]?.value\n\tif (value === undefined || value === null) return null\n\tconst sequence = Number(value)\n\treturn Number.isInteger(sequence) && sequence >= 0 ? sequence : null\n}\n\n/** Persist the acknowledged own-operation prefix for a node id. */\nexport async function saveOwnAckedThrough(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\tsequence: number,\n): Promise<void> {\n\tawait adapter.transaction(async (tx) => {\n\t\tconst rows = await tx.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\t\tOWN_ACKED_THROUGH_META_KEY,\n\t\t])\n\t\tconst legacy = parseLegacyPrefix(rows[0]?.value)\n\t\tif (legacy === null || legacy.nodeId === nodeId) {\n\t\t\tawait tx.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\t\t\tOWN_ACKED_THROUGH_META_KEY,\n\t\t\t\tJSON.stringify({ nodeId, sequence }),\n\t\t\t])\n\t\t\treturn\n\t\t}\n\t\tawait tx.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\t\townAckedThroughKey(nodeId),\n\t\t\tString(sequence),\n\t\t])\n\t})\n}\n\n/** Load the downlink scope the sync server last accepted (null when none). */\nexport async function loadAcceptedDownlinkScope(\n\tadapter: StorageAdapter,\n): Promise<Record<string, Record<string, unknown>> | null> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tACCEPTED_DOWNLINK_SCOPE_META_KEY,\n\t])\n\tconst value = rows[0]?.value\n\tif (value === undefined || value === null) return null\n\ttry {\n\t\tconst parsed = JSON.parse(value) as unknown\n\t\treturn parsed !== null && typeof parsed === 'object'\n\t\t\t? (parsed as Record<string, Record<string, unknown>>)\n\t\t\t: null\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/** Persist (or clear) the downlink scope the sync server last accepted. */\nexport async function saveAcceptedDownlinkScope(\n\tadapter: StorageAdapter,\n\tscope: Record<string, Record<string, unknown>> | null,\n): Promise<void> {\n\tif (scope === null) {\n\t\tawait adapter.execute('DELETE FROM _kora_meta WHERE key = ?', [\n\t\t\tACCEPTED_DOWNLINK_SCOPE_META_KEY,\n\t\t])\n\t\treturn\n\t}\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tACCEPTED_DOWNLINK_SCOPE_META_KEY,\n\t\tJSON.stringify(scope),\n\t])\n}\n","import type { MetaRow, StorageAdapter, Transaction } from '../types'\nimport { OWN_ACKED_THROUGH_META_KEY } from './sync-durability'\nimport { NODE_TOKEN_META_KEY, nodeTokenKey } from './sync-state'\n\n/**\n * Durable sync bookkeeping that belongs to the device, not to the app (Phase 2 RT-36,\n * RT-38, RT-40): the operations the server refused for good, and the node ids this\n * database authored operations under.\n *\n * Both tables are created on first use, so existing databases need no migration.\n */\n\n/**\n * Operations the server refused with a non-retriable rejection (RT-36). Unlike\n * `_kora_sync_rejected` (the app's reconciliation list, which `clearRejectedOperations`\n * empties), a row here is never removed: a refused operation stays refused, so a later\n * rescan of the device's own history never submits it again.\n */\nexport const TERMINAL_REJECTIONS_TABLE = '_kora_terminal_rejections'\n\n/**\n * Node ids this database authored operations under (RT-38, RT-40): the node in use,\n * nodes it rotated away from, and other tabs' nodes under per-tab isolation.\n */\nexport const LOCAL_NODES_TABLE = '_kora_local_nodes'\n\n/** Meta key: count of accepted sync handshakes on this database (refusal cycles, RT-38). */\nexport const ACCEPTED_CYCLE_META_KEY = 'sync_accepted_cycle'\n\n/** Meta key: terminal rejections were seeded from the pre-Phase-2 rejected list once. */\nconst TERMINAL_SEED_META_KEY = 'terminal_rejections_seeded_v1'\n\n/**\n * Rejection codes that never become a terminal marker: the operation was not judged on\n * its content, so a later upload of it may be accepted.\n *\n * - `SEQUENCE_CONFLICT`: the device's sequence number collided with an operation the\n * server holds (a lost local tail, RT-35, or a beta.12 duplicate pair, RT-37); the\n * operation is renumbered and uploaded again.\n * - `NODE_ID_MISMATCH`: uploaded on another node's session.\n * - `OUT_OF_UPLINK_SCOPE`: judged by the client against the scope it had then.\n */\nexport const NON_TERMINAL_REJECTION_CODES: ReadonlySet<string> = new Set([\n\t'SEQUENCE_CONFLICT',\n\t'NODE_ID_MISMATCH',\n\t'OUT_OF_UPLINK_SCOPE',\n])\n\nconst TERMINAL_REJECTIONS_DDL = `CREATE TABLE IF NOT EXISTS ${TERMINAL_REJECTIONS_TABLE} (\n operation_id TEXT PRIMARY KEY NOT NULL,\n node_id TEXT,\n sequence_number INTEGER,\n code TEXT NOT NULL,\n rejected_at INTEGER NOT NULL\n)`\n\nconst LOCAL_NODES_DDL = `CREATE TABLE IF NOT EXISTS ${LOCAL_NODES_TABLE} (\n node_id TEXT PRIMARY KEY NOT NULL,\n created_at INTEGER NOT NULL,\n accepted INTEGER NOT NULL DEFAULT 0,\n held INTEGER NOT NULL DEFAULT 0,\n refused_cycle INTEGER,\n principal TEXT,\n binding TEXT,\n refused_principals TEXT\n)`\n\n/**\n * How a local node's {@link LocalNodeRecord.principal} was learned (RT-50):\n *\n * - `fresh`: the node had no writes when it was bound to the signed-in user, so every\n * write under it was made by that user.\n * - `server`: a sync handshake as the node was accepted for that user (the server's node\n * claims are authoritative).\n * - `app`: the app assigned a held node to the signed-in user\n * (`app.sync.assignHeld`). A guess: cleared if the server refuses the node for them.\n */\nexport type LocalNodeBinding = 'fresh' | 'server' | 'app'\n\nconst LOCAL_NODE_BINDINGS: readonly LocalNodeBinding[] = ['fresh', 'server', 'app']\n\n/** At most this many principals are remembered as refused for one node (RT-50). */\nconst MAX_REFUSED_PRINCIPALS = 32\n\n/** Meta key: per-node adoption schedule (parked adoptions, upload progress; RT-46). */\nexport const ADOPTION_SCHEDULE_META_KEY = 'sync_adoption_schedule_v1'\n\n/** Adapters whose `_kora_local_nodes` table is known to have every column (this process). */\nconst migratedAdapters = new WeakSet<StorageAdapter>()\n\n/** A terminal rejection to record (RT-36). */\nexport interface TerminalRejection {\n\toperationId: string\n\t/** Authoring node and sequence, when known (null for rows seeded from the old list). */\n\tnodeId: string | null\n\tsequenceNumber: number | null\n\tcode: string\n\trejectedAt: number\n}\n\n/** A node id this database authored operations under (RT-38, RT-40). */\nexport interface LocalNodeRecord {\n\tnodeId: string\n\t/** Wall-clock time (ms) the node was registered. Display only. */\n\tcreatedAt: number\n\t/** A sync handshake as this node was accepted at least once. */\n\taccepted: boolean\n\t/**\n\t * The server refused this node (`NODE_ID_CLAIMED`) after it had been accepted: it\n\t * belongs to a principal that is not signed in. Its unsynced operations are held for\n\t * that principal, never uploaded under another one.\n\t */\n\theld: boolean\n\t/** {@link ACCEPTED_CYCLE_META_KEY} value when the server last refused it, or null. */\n\trefusedCycle: number | null\n\t/**\n\t * The signed-in user the node's writes belong to (RT-42), or null when the app never\n\t * told the store who was signed in while it was in use. A node bound to one user is\n\t * never used for, uploaded under, or adopted by another user's session.\n\t */\n\tprincipal: string | null\n\t/**\n\t * How {@link principal} was learned (RT-50), or null when unbound, or bound by a release\n\t * that did not record it (treated as a guess).\n\t */\n\tbinding: LocalNodeBinding | null\n\t/**\n\t * Users the server refused this UNBOUND node for (`NODE_ID_CLAIMED`, RT-50): it is\n\t * not theirs, so it is never tried again on their sessions. An unbound node that was\n\t * accepted before belongs to whichever user the server accepts it for.\n\t */\n\trefusedPrincipals: string[]\n}\n\ninterface LocalNodeRow {\n\tnode_id: string\n\tcreated_at: number\n\taccepted: number\n\theld: number\n\trefused_cycle: number | null\n\tprincipal: string | null\n\tbinding: string | null\n\trefused_principals: string | null\n}\n\n/** A parked adoption (RT-46): the node made no upload progress in its last session. */\nexport interface ParkedAdoption {\n\t/** {@link AdoptionSchedule.progress} when it was parked: retried once it moves. */\n\tprogressMark: number\n\t/** Wall-clock time (ms) after which it is retried anyway. Scheduling only. */\n\tuntilMs: number\n\t/** Consecutive parks (backoff exponent). */\n\tcount: number\n\t/** Wall-clock time (ms) it was parked. Scheduling only. */\n\tparkedAtMs: number\n}\n\n/**\n * Which local nodes' adoptions are parked (RT-46), and a counter of upload progress on\n * this database: a parked node is retried once anything else uploaded (its deferral may\n * have depended on it) or after its backoff.\n */\nexport interface AdoptionSchedule {\n\tprogress: number\n\tparked: Record<string, ParkedAdoption>\n}\n\n/**\n * Create both tables. `Store.open` runs it (through {@link registerLocalNode}), so the\n * read paths, which run often, assume the tables exist and never issue DDL (on the\n * IndexedDB adapter every statement schedules a snapshot).\n */\nexport async function ensureLocalSyncRecordTables(adapter: StorageAdapter): Promise<void> {\n\tawait adapter.execute(TERMINAL_REJECTIONS_DDL)\n\tawait adapter.execute(LOCAL_NODES_DDL)\n\tif (migratedAdapters.has(adapter)) return\n\t// A table created by an earlier release lacks the principal column (RT-42).\n\t// ... and the binding columns (RT-50).\n\tconst columns = await adapter.query<{ name: string }>(`PRAGMA table_info(${LOCAL_NODES_TABLE})`)\n\tfor (const column of ['principal', 'binding', 'refused_principals']) {\n\t\tif (!columns.some((existing) => existing.name === column)) {\n\t\t\tawait adapter.execute(`ALTER TABLE ${LOCAL_NODES_TABLE} ADD COLUMN ${column} TEXT`)\n\t\t}\n\t}\n\tmigratedAdapters.add(adapter)\n}\n\n/** Record terminal rejections (idempotent by operation id; the first record wins). */\nexport async function recordTerminalRejections(\n\tadapter: StorageAdapter,\n\tentries: TerminalRejection[],\n): Promise<void> {\n\tconst recordable = entries.filter((entry) => !NON_TERMINAL_REJECTION_CODES.has(entry.code))\n\tif (recordable.length === 0) return\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tfor (const entry of recordable) {\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at) VALUES (?, ?, ?, ?, ?)`,\n\t\t\t\t[entry.operationId, entry.nodeId, entry.sequenceNumber, entry.code, entry.rejectedAt],\n\t\t\t)\n\t\t}\n\t})\n}\n\n/** Which of `operationIds` carry a terminal rejection marker. */\nexport async function findTerminalRejections(\n\tadapter: StorageAdapter,\n\toperationIds: string[],\n): Promise<Set<string>> {\n\tconst found = new Set<string>()\n\tif (operationIds.length === 0) return found\n\tconst CHUNK = 500\n\tfor (let i = 0; i < operationIds.length; i += CHUNK) {\n\t\tconst chunk = operationIds.slice(i, i + CHUNK)\n\t\tconst rows = await adapter.query<{ operation_id: string }>(\n\t\t\t`SELECT operation_id FROM ${TERMINAL_REJECTIONS_TABLE} WHERE operation_id IN (${chunk.map(() => '?').join(', ')})`,\n\t\t\tchunk,\n\t\t)\n\t\tfor (const row of rows) found.add(row.operation_id)\n\t}\n\treturn found\n}\n\n/**\n * Seed terminal markers once from `_kora_sync_rejected`, the list earlier releases kept\n * (and the app may have cleared since). Rows whose code is not terminal are skipped:\n * in particular a pre-release device's `SEQUENCE_CONFLICT` rejections (RT-37) stay eligible\n * for the one-time re-upload, which is how those writes reach the server.\n */\nexport async function seedTerminalRejectionsOnce(adapter: StorageAdapter): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tconst done = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tTERMINAL_SEED_META_KEY,\n\t])\n\tif (done.length > 0) return\n\tconst tables = await adapter.query<{ name: string }>(\n\t\t\"SELECT name FROM sqlite_master WHERE type = 'table' AND name = '_kora_sync_rejected'\",\n\t)\n\tawait adapter.transaction(async (tx) => {\n\t\tif (tables.length > 0) {\n\t\t\tconst placeholders = [...NON_TERMINAL_REJECTION_CODES].map(() => '?').join(', ')\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at)\n SELECT operation_id, NULL, NULL, code, rejected_at FROM _kora_sync_rejected\n WHERE retriable = 0 AND code NOT IN (${placeholders})`,\n\t\t\t\t[...NON_TERMINAL_REJECTION_CODES],\n\t\t\t)\n\t\t}\n\t\tawait tx.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\t\tTERMINAL_SEED_META_KEY,\n\t\t\tString(Date.now()),\n\t\t])\n\t})\n}\n\nfunction rowToRecord(row: LocalNodeRow): LocalNodeRecord {\n\treturn {\n\t\tnodeId: row.node_id,\n\t\tcreatedAt: Number(row.created_at),\n\t\taccepted: Number(row.accepted) === 1,\n\t\theld: Number(row.held) === 1,\n\t\trefusedCycle: row.refused_cycle === null ? null : Number(row.refused_cycle),\n\t\tprincipal: typeof row.principal === 'string' ? row.principal : null,\n\t\tbinding:\n\t\t\ttypeof row.principal === 'string' &&\n\t\t\tLOCAL_NODE_BINDINGS.includes(row.binding as LocalNodeBinding)\n\t\t\t\t? (row.binding as LocalNodeBinding)\n\t\t\t\t: null,\n\t\trefusedPrincipals: parsePrincipalList(row.refused_principals),\n\t}\n}\n\nfunction parsePrincipalList(value: string | null): string[] {\n\tif (typeof value !== 'string' || value.length === 0) return []\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(value)\n\t\treturn Array.isArray(parsed)\n\t\t\t? parsed.filter((entry): entry is string => typeof entry === 'string')\n\t\t\t: []\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Register a node id this database authors operations under. A node registered for the\n * first time that a claims-aware server already accepted here (a database from a build\n * with node claims) is recorded as accepted, so a later refusal holds its writes\n * instead of re-authoring them under another principal (RT-38).\n *\n * Sync history alone is not that evidence (RT-92): beta.12 and older servers recorded\n * no node claims, so a node only they accepted belongs to nobody on an upgraded server.\n * It registers as not accepted, and a refusal re-authors its never-sent writes under a\n * fresh node (RT-21) instead of holding them for an owner that does not exist.\n */\nexport async function registerLocalNode(adapter: StorageAdapter, nodeId: string): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tconst existing = await adapter.query<{ node_id: string }>(\n\t\t`SELECT node_id FROM ${LOCAL_NODES_TABLE} WHERE node_id = ?`,\n\t\t[nodeId],\n\t)\n\tif (existing.length > 0) return\n\tconst accepted = await hasSyncHistory(adapter, nodeId)\n\tawait adapter.execute(\n\t\t`INSERT OR IGNORE INTO ${LOCAL_NODES_TABLE} (node_id, created_at, accepted, held, refused_cycle) VALUES (?, ?, ?, 0, NULL)`,\n\t\t[nodeId, Date.now(), accepted ? 1 : 0],\n\t)\n}\n\n/**\n * Whether a claims-aware server accepted `nodeId` on this database before the registry\n * existed: a node token it issued (anonymous claims), or the acknowledged-prefix record\n * builds with node claims keep. beta.12's `last_acked_server_vector` does not count: its\n * server recorded no claims (RT-92).\n */\nasync function hasSyncHistory(adapter: StorageAdapter, nodeId: string): Promise<boolean> {\n\tconst rows = await adapter.query<MetaRow & { key: string }>(\n\t\t'SELECT key, value FROM _kora_meta WHERE key IN (?, ?, ?)',\n\t\t[NODE_TOKEN_META_KEY, nodeTokenKey(nodeId), OWN_ACKED_THROUGH_META_KEY],\n\t)\n\tconst nodeIdRows = await adapter.query<MetaRow>(\n\t\t\"SELECT value FROM _kora_meta WHERE key = 'node_id'\",\n\t)\n\tconst isMetaNode = nodeIdRows[0]?.value === nodeId\n\tfor (const row of rows) {\n\t\tif (row.key === nodeTokenKey(nodeId)) return true\n\t\tif (row.key === NODE_TOKEN_META_KEY) {\n\t\t\tif (isMetaNode) return true\n\t\t\tcontinue\n\t\t}\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(row.value) as Record<string, unknown>\n\t\t\tif (row.key === OWN_ACKED_THROUGH_META_KEY) {\n\t\t\t\tif (parsed.nodeId === nodeId && typeof parsed.sequence === 'number' && parsed.sequence > 0)\n\t\t\t\t\treturn true\n\t\t\t}\n\t\t} catch {\n\t\t\t// An unreadable row is no evidence either way.\n\t\t}\n\t}\n\treturn false\n}\n\n/** Every registered local node, oldest first. */\nexport async function listLocalNodes(adapter: StorageAdapter): Promise<LocalNodeRecord[]> {\n\tconst rows = await adapter.query<LocalNodeRow>(\n\t\t`SELECT node_id, created_at, accepted, held, refused_cycle, principal, binding, refused_principals FROM ${LOCAL_NODES_TABLE} ORDER BY created_at ASC, node_id ASC`,\n\t)\n\treturn rows.map(rowToRecord)\n}\n\nasync function readAcceptedCycle(tx: Transaction): Promise<number> {\n\tconst rows = await tx.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tACCEPTED_CYCLE_META_KEY,\n\t])\n\tconst value = Number(rows[0]?.value ?? 0)\n\treturn Number.isFinite(value) ? value : 0\n}\n\n/**\n * Record an accepted handshake as `nodeId`: the node is accepted and no longer held,\n * and a new refusal cycle starts (every node may be tried again after a refusal).\n */\nexport async function markLocalNodeAccepted(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tconst cycle = await readAcceptedCycle(tx)\n\t\tawait tx.execute(\n\t\t\t`INSERT INTO ${LOCAL_NODES_TABLE} (node_id, created_at, accepted, held, refused_cycle) VALUES (?, ?, 1, 0, NULL)\n ON CONFLICT(node_id) DO UPDATE SET accepted = 1, held = 0, refused_cycle = NULL`,\n\t\t\t[nodeId, Date.now()],\n\t\t)\n\t\tawait tx.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\t\tACCEPTED_CYCLE_META_KEY,\n\t\t\tString(cycle + 1),\n\t\t])\n\t})\n}\n\n/**\n * Record that the server refused `nodeId` (`NODE_ID_CLAIMED`) in the current refusal\n * cycle. `held` marks its unsynced operations as held for the principal owning it.\n */\nexport async function markLocalNodeRefused(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\theld: boolean,\n): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tconst cycle = await readAcceptedCycle(tx)\n\t\tawait tx.execute(\n\t\t\t`INSERT INTO ${LOCAL_NODES_TABLE} (node_id, created_at, accepted, held, refused_cycle) VALUES (?, ?, 0, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET held = MAX(held, excluded.held), refused_cycle = excluded.refused_cycle`,\n\t\t\t[nodeId, Date.now(), held ? 1 : 0, cycle],\n\t\t)\n\t})\n}\n\n/**\n * Forget a local node that has nothing left to upload (bounds the registry under per-tab\n * isolation, where every tab adds a node). A node that writes again is registered again\n * when its store opens. Never the database's own node id.\n */\nexport async function forgetLocalNode(adapter: StorageAdapter, nodeId: string): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.execute(\n\t\t`DELETE FROM ${LOCAL_NODES_TABLE} WHERE node_id = ? AND held = 0 AND node_id NOT IN (SELECT value FROM _kora_meta WHERE key = 'node_id')`,\n\t\t[nodeId],\n\t)\n}\n\n/**\n * Bind a local node to the signed-in user whose writes it carries (RT-42), recording how\n * that was learned (RT-50). The node is registered if it is not yet.\n */\nexport async function setLocalNodePrincipal(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\tprincipal: string,\n\tbinding: LocalNodeBinding,\n): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.execute(\n\t\t`INSERT INTO ${LOCAL_NODES_TABLE} (node_id, created_at, accepted, held, refused_cycle, principal, binding) VALUES (?, ?, 0, 0, NULL, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET principal = excluded.principal, binding = excluded.binding`,\n\t\t[nodeId, Date.now(), principal, binding],\n\t)\n}\n\n/** A binding that is only a guess: cleared when the server refuses the node (RT-50). */\nfunction isGuessedBinding(node: LocalNodeRecord): boolean {\n\treturn node.principal !== null && (node.binding === null || node.binding === 'app')\n}\n\nasync function findLocalNode(\n\tadapter: StorageAdapter | Transaction,\n\tnodeId: string,\n): Promise<LocalNodeRecord | null> {\n\tconst rows = await adapter.query<LocalNodeRow>(\n\t\t`SELECT node_id, created_at, accepted, held, refused_cycle, principal, binding, refused_principals FROM ${LOCAL_NODES_TABLE} WHERE node_id = ?`,\n\t\t[nodeId],\n\t)\n\tconst row = rows[0]\n\treturn row ? rowToRecord(row) : null\n}\n\n/**\n * A sync handshake as `nodeId` was accepted for `principal` (RT-50): the server's node\n * claims say the node is theirs. Binds an unbound node, or replaces a guessed binding,\n * as `server`. A node bound from evidence (`fresh` or `server`) to another user keeps\n * its binding: the engine never runs such a session.\n */\nexport async function confirmLocalNodePrincipal(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\tprincipal: string,\n): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tconst node = await findLocalNode(tx, nodeId)\n\t\tif (node && node.principal !== null && node.principal !== principal && !isGuessedBinding(node))\n\t\t\treturn\n\t\tawait tx.execute(\n\t\t\t`INSERT INTO ${LOCAL_NODES_TABLE} (node_id, created_at, accepted, held, refused_cycle, principal, binding) VALUES (?, ?, 0, 0, NULL, ?, 'server')\n ON CONFLICT(node_id) DO UPDATE SET principal = excluded.principal, binding = 'server', refused_principals = NULL`,\n\t\t\t[nodeId, Date.now(), principal],\n\t\t)\n\t})\n}\n\n/**\n * The server refused `nodeId` for `principal` (`NODE_ID_CLAIMED`, RT-50): another user\n * owns it. A guessed binding is cleared (it was wrong), and an unbound node remembers\n * that it is not `principal`'s, so it is tried on another user's session instead.\n */\nexport async function recordLocalNodeRefusedFor(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\tprincipal: string,\n): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tconst node = await findLocalNode(tx, nodeId)\n\t\tif (!node) return\n\t\tif (node.principal !== null && !isGuessedBinding(node)) return\n\t\tconst refused = node.refusedPrincipals.filter((entry) => entry !== principal)\n\t\trefused.push(principal)\n\t\tawait tx.execute(\n\t\t\t`UPDATE ${LOCAL_NODES_TABLE} SET principal = NULL, binding = NULL, refused_principals = ? WHERE node_id = ?`,\n\t\t\t[JSON.stringify(refused.slice(-MAX_REFUSED_PRINCIPALS)), nodeId],\n\t\t)\n\t})\n}\n\n/**\n * The app assigned a held, unbound local node to `principal` (RT-50,\n * `app.sync.assignHeld`): its writes upload on their sessions from now on. A guess, so\n * a later refusal by the server clears it again.\n *\n * @returns false when the node is unknown, already bound, or was refused for `principal`\n */\nexport async function assignLocalNodePrincipal(\n\tadapter: StorageAdapter,\n\tnodeId: string,\n\tprincipal: string,\n): Promise<boolean> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tlet assigned = false\n\tawait adapter.transaction(async (tx) => {\n\t\tconst node = await findLocalNode(tx, nodeId)\n\t\tif (!node || node.principal !== null || node.refusedPrincipals.includes(principal)) return\n\t\tawait tx.execute(\n\t\t\t`UPDATE ${LOCAL_NODES_TABLE} SET principal = ?, binding = 'app', held = 0, refused_cycle = NULL WHERE node_id = ?`,\n\t\t\t[principal, nodeId],\n\t\t)\n\t\tassigned = true\n\t})\n\treturn assigned\n}\n\n/** Forget a local node whatever its state (its writes were discarded by the app, RT-50). */\nexport async function dropLocalNode(adapter: StorageAdapter, nodeId: string): Promise<void> {\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.execute(\n\t\t`DELETE FROM ${LOCAL_NODES_TABLE} WHERE node_id = ? AND node_id NOT IN (SELECT value FROM _kora_meta WHERE key = 'node_id')`,\n\t\t[nodeId],\n\t)\n}\n\n/** The adoption schedule (RT-46); empty when none was saved. */\nexport async function loadAdoptionSchedule(adapter: StorageAdapter): Promise<AdoptionSchedule> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tADOPTION_SCHEDULE_META_KEY,\n\t])\n\tconst empty: AdoptionSchedule = { progress: 0, parked: {} }\n\tconst raw = rows[0]?.value\n\tif (!raw) return empty\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as Partial<AdoptionSchedule>\n\t\treturn {\n\t\t\tprogress: typeof parsed.progress === 'number' ? parsed.progress : 0,\n\t\t\tparked: parsed.parked && typeof parsed.parked === 'object' ? parsed.parked : {},\n\t\t}\n\t} catch {\n\t\t// An unreadable schedule only costs an earlier retry of a parked adoption.\n\t\treturn empty\n\t}\n}\n\n/** Persist the adoption schedule (RT-46). */\nexport async function saveAdoptionSchedule(\n\tadapter: StorageAdapter,\n\tschedule: AdoptionSchedule,\n): Promise<void> {\n\tawait adapter.execute('INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)', [\n\t\tADOPTION_SCHEDULE_META_KEY,\n\t\tJSON.stringify(schedule),\n\t])\n}\n\n/** The current refusal cycle (count of accepted handshakes on this database). */\nexport async function loadAcceptedCycle(adapter: StorageAdapter): Promise<number> {\n\tconst rows = await adapter.query<MetaRow>('SELECT value FROM _kora_meta WHERE key = ?', [\n\t\tACCEPTED_CYCLE_META_KEY,\n\t])\n\tconst value = Number(rows[0]?.value ?? 0)\n\treturn Number.isFinite(value) ? value : 0\n}\n\n/** Read access to a database (an open adapter, or a reader handed to a delete check). */\nexport interface DatabaseQuery {\n\tquery<T>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Whether any local node has operations above its contiguous acknowledged prefix that\n * the server did not refuse for good (RT-41): the authoritative \"unsynced\" (W3), which\n * the outbound queue only approximates. A local node is one in the registry, the\n * database's node id, or the node of the legacy prefix key; a node with no recorded\n * prefix counts from 0. Works on a database no store has opened in this release.\n */\nexport async function hasUnsyncedOwnOperations(db: DatabaseQuery): Promise<boolean> {\n\tconst tables = new Set(\n\t\t(await db.query<{ name: string }>(\"SELECT name FROM sqlite_master WHERE type = 'table'\")).map(\n\t\t\t(row) => row.name,\n\t\t),\n\t)\n\tconst opsTables = [...tables].filter((name) => name.startsWith('_kora_ops_'))\n\tif (opsTables.length === 0 || !tables.has('_kora_meta')) return false\n\n\tconst nodes = new Set<string>()\n\tif (tables.has(LOCAL_NODES_TABLE)) {\n\t\tfor (const row of await db.query<{ node_id: string }>(\n\t\t\t`SELECT node_id FROM ${LOCAL_NODES_TABLE}`,\n\t\t)) {\n\t\t\tnodes.add(row.node_id)\n\t\t}\n\t}\n\tconst meta = await db.query<{ key: string; value: string }>(\n\t\t\"SELECT key, value FROM _kora_meta WHERE key = 'node_id' OR key = ? OR key LIKE ?\",\n\t\t[OWN_ACKED_THROUGH_META_KEY, `${OWN_ACKED_THROUGH_META_KEY}:%`],\n\t)\n\tconst prefixes = new Map<string, number>()\n\tfor (const row of meta) {\n\t\tif (row.key === 'node_id') {\n\t\t\tnodes.add(row.value)\n\t\t} else if (row.key === OWN_ACKED_THROUGH_META_KEY) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(row.value) as { nodeId?: unknown; sequence?: unknown }\n\t\t\t\tif (typeof parsed.nodeId === 'string') {\n\t\t\t\t\tnodes.add(parsed.nodeId)\n\t\t\t\t\tif (typeof parsed.sequence === 'number') prefixes.set(parsed.nodeId, parsed.sequence)\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Unreadable: the node (if known otherwise) counts from 0.\n\t\t\t}\n\t\t} else {\n\t\t\tconst nodeId = row.key.slice(OWN_ACKED_THROUGH_META_KEY.length + 1)\n\t\t\t// The legacy key wins for its node (loadOwnAckedThrough reads it first).\n\t\t\tconst sequence = Number(row.value)\n\t\t\tif (!prefixes.has(nodeId) && Number.isInteger(sequence)) prefixes.set(nodeId, sequence)\n\t\t}\n\t}\n\tconst exclude = tables.has(TERMINAL_REJECTIONS_TABLE)\n\t\t? ` AND id NOT IN (SELECT operation_id FROM ${TERMINAL_REJECTIONS_TABLE})`\n\t\t: ''\n\tfor (const nodeId of nodes) {\n\t\tconst prefix = prefixes.get(nodeId) ?? 0\n\t\tfor (const table of opsTables) {\n\t\t\tconst rows = await db.query<{ n: number }>(\n\t\t\t\t`SELECT COUNT(*) AS n FROM \"${table.replace(/\"/g, '\"\"')}\" WHERE node_id = ? AND sequence_number > ?${exclude}`,\n\t\t\t\t[nodeId, prefix],\n\t\t\t)\n\t\t\tif (Number(rows[0]?.n ?? 0) > 0) return true\n\t\t}\n\t}\n\treturn false\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core","import_core"]}