@sqlite-sync/core 0.12.2 → 0.13.0

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 (44) hide show
  1. package/dist/{reset-state-t0tTGqoh.js → crdt-sync-producer-BY-WobgO.js} +55 -241
  2. package/dist/crdt-sync-producer-BY-WobgO.js.map +1 -0
  3. package/dist/{crdt-sync-remote-source-CPXzmSIS.js → crdt-sync-remote-source-yeTEkOjH.js} +344 -12
  4. package/dist/crdt-sync-remote-source-yeTEkOjH.js.map +1 -0
  5. package/dist/{crdt-table-schema-BRm_WdcW.js → crdt-table-schema-D6Yf61Ex.js} +37 -13
  6. package/dist/crdt-table-schema-D6Yf61Ex.js.map +1 -0
  7. package/dist/{index-BHsKLqcK.d.ts → index-CH4lf3iX.d.ts} +166 -72
  8. package/dist/index.common-C8W9VKSY.js +276 -0
  9. package/dist/index.common-C8W9VKSY.js.map +1 -0
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +20 -677
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.native.d.ts +2 -0
  14. package/dist/index.native.js +9 -0
  15. package/dist/internal.d.ts +1 -1
  16. package/dist/internal.js +2 -2
  17. package/dist/local.d.ts +48 -0
  18. package/dist/local.js +255 -0
  19. package/dist/local.js.map +1 -0
  20. package/dist/{make-crdt-table-C6l0F-o_.js → make-crdt-table-DYCXOYB7.js} +13 -14
  21. package/dist/make-crdt-table-DYCXOYB7.js.map +1 -0
  22. package/dist/reset-state-CPOi_45B.js +220 -0
  23. package/dist/reset-state-CPOi_45B.js.map +1 -0
  24. package/dist/server.d.ts +8 -3
  25. package/dist/server.js +2 -2
  26. package/dist/server.js.map +1 -1
  27. package/dist/sqlite-reactive-db-DLS1xRZp.js +296 -0
  28. package/dist/sqlite-reactive-db-DLS1xRZp.js.map +1 -0
  29. package/dist/synced-db-database-B5M7MrBD.js +81 -0
  30. package/dist/synced-db-database-B5M7MrBD.js.map +1 -0
  31. package/dist/{system-schema-Dvl_BLId.js → system-schema-CXf3FmxN.js} +142 -58
  32. package/dist/system-schema-CXf3FmxN.js.map +1 -0
  33. package/dist/worker.d.ts +2 -10
  34. package/dist/worker.js +11 -177
  35. package/dist/worker.js.map +1 -1
  36. package/dist/ws-remote-source-BbPWrHB6.js +161 -0
  37. package/dist/ws-remote-source-BbPWrHB6.js.map +1 -0
  38. package/dist/ws-remote-source-DFBhDc6Z.d.ts +12 -0
  39. package/package.json +9 -1
  40. package/dist/crdt-sync-remote-source-CPXzmSIS.js.map +0 -1
  41. package/dist/crdt-table-schema-BRm_WdcW.js.map +0 -1
  42. package/dist/make-crdt-table-C6l0F-o_.js.map +0 -1
  43. package/dist/reset-state-t0tTGqoh.js.map +0 -1
  44. package/dist/system-schema-Dvl_BLId.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt-sync-producer-BY-WobgO.js","names":["_exhaustive"],"sources":["../src/export-import.ts","../src/migrations/migrator.ts","../src/schema/verify-sync-schema.ts","../src/sqlite-crdt/apply-crdt-event.ts","../src/sqlite-crdt/crdt-storage.ts","../src/sqlite-crdt/crdt-sync-producer.ts"],"sourcesContent":["import type { MigratableEvent, SyncDbMigrator } from \"./migrations/migrator\";\nimport type { CrdtTableConfig } from \"./sqlite-crdt/crdt-schema\";\nimport type { OwnCrdtEvent } from \"./sqlite-crdt/crdt-storage\";\nimport type { ExecuteResult } from \"./sqlite-db-wrapper\";\nimport { quoteId } from \"./utils\";\n\n/**\n * A portable, schema-version-aware dump of every active entity in a synced DB.\n * Unlike a raw SQLite snapshot, it carries no event history — just the current\n * `tombstone = 0` rows, ready to be replayed as fresh create events on import.\n */\nexport type SyncedDbExport = {\n schemaVersion: number;\n exportedAt: string;\n /** base table name -> active rows (each includes `id`, excludes `tombstone`). */\n tables: Record<string, Array<Record<string, unknown>>>;\n};\n\nexport type ImportDataOptions = { validate?: boolean };\nexport type ImportDataResult = { imported: number };\n\ntype ExportImportReactiveDb = {\n db: { execute<T>(sql: string, meta: { loggerLevel: \"system\" }): ExecuteResult<T> };\n};\n\ntype ApplyImportEvents = (events: OwnCrdtEvent[]) => void | Promise<void>;\n\ntype ImportMigrator = Pick<SyncDbMigrator, \"currentSchemaVersion\" | \"migrateEvents\">;\n\ntype CreateExportDataOptions = {\n reactiveDb: ExportImportReactiveDb;\n tablesConfig: CrdtTableConfig[];\n schemaVersion: number;\n};\n\ntype CreateImportDataOptions = {\n migrator: ImportMigrator;\n tablesConfig?: CrdtTableConfig[];\n applyEvents: ApplyImportEvents;\n};\n\nfunction createTableNameNormalizer(tablesConfig: CrdtTableConfig[] | undefined) {\n const tablesByAlias = new Map<string, CrdtTableConfig>();\n for (const table of tablesConfig ?? []) {\n const { baseTableName, crdtTableName } = table;\n tablesByAlias.set(baseTableName, table);\n tablesByAlias.set(crdtTableName, table);\n }\n return (tableName: string) => {\n const table = tablesByAlias.get(tableName);\n if (table?.exportImport === \"ignore\") {\n return null;\n }\n return table?.baseTableName ?? tableName;\n };\n}\n\nexport function createImportData({ migrator, tablesConfig, applyEvents }: CreateImportDataOptions) {\n const normalizeTableName = createTableNameNormalizer(tablesConfig);\n\n /**\n * Replay an export as a sequence of `item-created` CRDT events, seeding the\n * rows through the provided CRDT event applicator.\n *\n * Schema migration: a dump exported at an older schema version is forward-\n * migrated up to the DB's current version (running the same event transformers\n * remote events go through) before being authored — so an old backup can be\n * restored into an upgraded app. Rows in tables removed by a later migration\n * are dropped. The reverse (a dump from a *newer* version than this DB) cannot\n * be down-migrated and is rejected.\n *\n * Overwrite-by-default: because the generated events carry fresh (newest) HLC\n * timestamps, importing a row whose `id` already exists overwrites every field\n * under last-write-wins, while new ids are inserted. Existing rows absent from\n * the dump are never touched. This is a restore/seed, not a CRDT merge —\n * original timestamps are not preserved.\n *\n * @param opts.validate When `false`, skip the too-new guard and author the rows\n * as-is at the current version (footgun — payloads are not down-migrated).\n * Forward migration and per-row payload validation always run regardless.\n */\n return (data: SyncedDbExport, opts?: ImportDataOptions): ImportDataResult | Promise<ImportDataResult> => {\n const validate = opts?.validate ?? true;\n const currentVersion = migrator.currentSchemaVersion;\n\n if (validate && data.schemaVersion > currentVersion) {\n throw new Error(\n `Cannot import data from schema version ${data.schemaVersion} into a database at older schema version ${currentVersion}. Exports migrate forward to newer schema versions, not backward.`,\n );\n }\n\n const sourceEvents: MigratableEvent[] = [];\n for (const [tableName, rows] of Object.entries(data.tables)) {\n const normalizedTableName = normalizeTableName(tableName);\n if (normalizedTableName === null) {\n continue;\n }\n for (const row of rows) {\n sourceEvents.push({\n schema_version: data.schemaVersion,\n type: \"item-created\",\n dataset: normalizedTableName,\n item_id: row.id as string,\n payload: JSON.stringify(row),\n });\n }\n }\n\n // Forward-migrate the historical export up to the current schema version\n // before authoring it: own events are always written at the current version.\n const events: OwnCrdtEvent[] = migrator.migrateEvents(sourceEvents, currentVersion).map((event) => ({\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n }));\n\n const applied = applyEvents(events);\n if (applied instanceof Promise) {\n return applied.then(() => ({ imported: events.length }));\n }\n\n return { imported: events.length };\n };\n}\n\nexport function createExportData({ reactiveDb, tablesConfig, schemaVersion }: CreateExportDataOptions) {\n const tableNames = tablesConfig.flatMap((config) => [config.crdtTableName, config.baseTableName]);\n\n const resolveTables = (requested: string[] | undefined): CrdtTableConfig[] => {\n if (!requested) {\n return tablesConfig;\n }\n\n const seenBaseTableNames = new Set<string>();\n const resolved: CrdtTableConfig[] = [];\n for (const name of requested) {\n const config = tablesConfig.find(\n (tableConfig) => tableConfig.crdtTableName === name || tableConfig.baseTableName === name,\n );\n if (!config) {\n throw new Error(`Unknown table \"${name}\". Known synced tables: ${tableNames.join(\", \")}`);\n }\n if (!seenBaseTableNames.has(config.baseTableName)) {\n seenBaseTableNames.add(config.baseTableName);\n resolved.push(config);\n }\n }\n return resolved;\n };\n\n /**\n * Dump every active entity (the `tombstone = 0` rows) from the synced tables.\n *\n * Reads this tab's reactive (in-memory) DB — the state the user currently sees,\n * which may differ slightly from the worker's authoritative DB while local\n * writes are in flight.\n */\n return (opts?: { tables?: string[] }): SyncedDbExport => {\n const tables: Record<string, Array<Record<string, unknown>>> = {};\n\n for (const { baseTableName, crdtTableName, exportImport } of resolveTables(opts?.tables)) {\n if (exportImport === \"ignore\") {\n continue;\n }\n const { rows } = reactiveDb.db.execute<Record<string, unknown>>(`select * from ${quoteId(crdtTableName)}`, {\n loggerLevel: \"system\",\n });\n tables[baseTableName] = rows.map(({ tombstone: _tombstone, ...rest }) => rest);\n }\n\n return {\n schemaVersion,\n exportedAt: new Date().toISOString(),\n tables,\n };\n };\n}\n","import type {\n ColumnDataType,\n ColumnDefinitionBuilderCallback,\n Compilable,\n CreateIndexBuilder,\n CreateTableBuilder,\n Expression,\n Kysely,\n} from \"kysely\";\nimport { dummyKysely } from \"../dummy-kysely\";\nimport { type CrdtEventType, isNoOpCrdtEventPayload, type NewCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { StoredValue } from \"../sqlite-crdt/stored-value\";\n\nexport type MigratableEvent = {\n schema_version: number;\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: string;\n};\n\ntype CrdtEventTransformer = (event: NewCrdtEvent) => NewCrdtEvent | null;\n\ntype MigrationStepSql =\n | {\n sql: string;\n parameters?: readonly unknown[];\n }\n | Compilable\n | ((db: Kysely<unknown>) => Compilable);\n\ntype UpdateLogMutation =\n | { type: \"rename-table\"; oldTable: string; newTable: string }\n | { type: \"drop-table\"; table: string }\n | { type: \"rename-column\"; table: string; oldColumn: string; newColumn: string }\n | { type: \"add-column\"; table: string; column: string }\n | { type: \"drop-column\"; table: string; column: string };\n\ntype MigrationStep = {\n sql: MigrationStepSql | MigrationStepSql[];\n eventTransformer?: MigrationEventTransformers;\n updateLogMutations?: UpdateLogMutation[];\n};\n\ntype RawMigrationStep = {\n sql: MigrationSql[];\n eventTransformer?: CompiledMigrationEventTransformer;\n updateLogMutations?: UpdateLogMutation[];\n};\n\ntype MigrationSql = { sql: string; parameters: readonly unknown[] };\n\ntype DataTypeExpression = ColumnDataType | Expression<any>;\n\nconst protectedColumns = [\"id\", \"tombstone\"];\n\nfunction assertColumnNotProtected(column: string, operation: string) {\n if (protectedColumns.includes(column)) {\n throw new Error(`Cannot ${operation} protected column \"${column}\"`);\n }\n}\n\nconst migrationSteps = {\n createTable: (table: string, build: (table: CreateTableBuilder<string, never>) => Compilable): MigrationStep => ({\n sql: (db) => build(db.schema.createTable(table)),\n }),\n\n dropTable: (table: string): MigrationStep => ({\n sql: (db) => db.schema.dropTable(table),\n eventTransformer: {\n [table]: () => null,\n },\n updateLogMutations: [{ type: \"drop-table\", table }],\n }),\n\n createIndex: (indexName: string, build: (index: CreateIndexBuilder) => Compilable): MigrationStep => ({\n sql: (db) => build(db.schema.createIndex(indexName)),\n }),\n\n dropIndex: (indexName: string): MigrationStep => ({\n sql: (db) => db.schema.dropIndex(indexName),\n }),\n\n renameTable: ({ oldTable, newTable }: { oldTable: string; newTable: string }): MigrationStep => ({\n sql: (db) => db.schema.alterTable(oldTable).renameTo(newTable),\n eventTransformer: {\n [oldTable]: (event) => {\n event.dataset = newTable;\n return event;\n },\n },\n updateLogMutations: [{ type: \"rename-table\", oldTable, newTable }],\n }),\n\n renameColumn: ({\n table,\n oldColumn,\n newColumn,\n }: {\n table: string;\n oldColumn: string;\n newColumn: string;\n }): MigrationStep => {\n assertColumnNotProtected(oldColumn, \"rename\");\n assertColumnNotProtected(newColumn, \"rename to\");\n return {\n sql: (db) => db.schema.alterTable(table).renameColumn(oldColumn, newColumn),\n eventTransformer: {\n [table]: (event) => {\n if ((event.type !== \"item-updated\" && event.type !== \"item-created\") || !(oldColumn in event.payload)) {\n return event;\n }\n\n const oldVal = event.payload[oldColumn];\n delete event.payload[oldColumn];\n event.payload[newColumn] = oldVal;\n\n return event;\n },\n },\n updateLogMutations: [{ type: \"rename-column\", table, oldColumn, newColumn }],\n };\n },\n\n addColumn: ({\n table,\n column,\n type,\n defaultValue,\n build = (e) => e,\n }: {\n table: string;\n column: string;\n type: DataTypeExpression;\n defaultValue: number | boolean | string | null;\n build?: ColumnDefinitionBuilderCallback;\n }): MigrationStep => ({\n // Older creates are not given the default: the column default fills it on insert, and a\n // replayed create must not write a cell that replicas which applied it earlier never had.\n sql: (db) => db.schema.alterTable(table).addColumn(column, type, (x) => build(x).defaultTo(defaultValue)),\n updateLogMutations: [{ type: \"add-column\", table, column }],\n }),\n\n dropColumn: ({ table, column }: { table: string; column: string }): MigrationStep => {\n assertColumnNotProtected(column, \"drop\");\n return {\n sql: (db) => db.schema.alterTable(table).dropColumn(column),\n eventTransformer: {\n [table]: (event) => {\n if (!(column in event.payload)) {\n return event;\n }\n\n delete event.payload[column];\n\n if (event.type === \"item-updated\" && Object.keys(event.payload).length === 0) {\n return null;\n }\n\n return event;\n },\n },\n updateLogMutations: [{ type: \"drop-column\", table, column }],\n };\n },\n};\n\ntype MigrationEventTransformers = Record<string, CrdtEventTransformer>;\ntype CompiledMigrationEventTransformer = (event: NewCrdtEvent) => NewCrdtEvent | null;\n\nfunction buildMigrationSql(steps: MigrationStep[]): MigrationSql[] {\n return steps\n .flatMap((step) => (Array.isArray(step.sql) ? step.sql : [step.sql]))\n .map((sql): MigrationSql => {\n if (typeof sql === \"string\") {\n return { sql, parameters: [] };\n }\n\n if (typeof sql === \"function\") {\n const query = sql(dummyKysely).compile();\n return { sql: query.sql, parameters: query.parameters };\n }\n\n if (\"compile\" in sql) {\n const query = sql.compile();\n return { sql: query.sql, parameters: query.parameters };\n }\n\n return {\n sql: sql.sql,\n parameters: sql.parameters ?? [],\n };\n });\n}\n\nfunction buildMigrationEventTransformer(steps: MigrationStep[]): CompiledMigrationEventTransformer | undefined {\n const transformers: Array<[string, CrdtEventTransformer]> = [];\n\n for (const step of steps) {\n if (step.eventTransformer) {\n transformers.push(...Object.entries(step.eventTransformer));\n }\n }\n\n if (transformers.length === 0) {\n return undefined;\n }\n\n return (event: NewCrdtEvent) => {\n let transformedEvent: NewCrdtEvent | null = event;\n\n for (const [table, transformer] of transformers) {\n if (transformedEvent === null) {\n return null;\n }\n if (transformedEvent.dataset !== table) {\n continue;\n }\n transformedEvent = transformer(transformedEvent);\n if (transformedEvent === null) {\n return null;\n }\n }\n\n return transformedEvent;\n };\n}\n\nexport function createMigrations(buildMigrations: (builder: typeof migrationSteps) => Record<number, MigrationStep[]>) {\n const migrations: Record<number, RawMigrationStep> = Object.fromEntries(\n Object.entries(buildMigrations(migrationSteps)).map(([version, steps]) => {\n const versionNumber = Number(version);\n\n if (Number.isNaN(versionNumber)) {\n throw new Error(`Invalid migration version: ${version}`);\n }\n\n if (versionNumber < 0) {\n throw new Error(`Migration version cannot be negative: ${version}`);\n }\n\n const updateLogMutations = steps.flatMap((step) => step.updateLogMutations ?? []);\n\n return [\n version,\n {\n sql: buildMigrationSql(steps),\n eventTransformer: buildMigrationEventTransformer(steps),\n ...(updateLogMutations.length > 0 && { updateLogMutations }),\n },\n ];\n }),\n );\n\n return migrations;\n}\n\nexport type Migrations = ReturnType<typeof createMigrations>;\n\nexport type MigrationsDb = {\n startTransaction: (callback: (tx: MigrationsTransaction) => void) => void;\n};\n\ntype MigrationsTransaction = {\n execute: (sql: string, parameters: readonly unknown[], meta?: { loggerLevel?: \"system\" }) => void;\n};\n\nexport function createMigrator({\n migrations,\n schemaVersion,\n updateLogTableName,\n}: {\n migrations: Migrations;\n schemaVersion: StoredValue<number>;\n updateLogTableName?: string;\n}) {\n const latestSchemaVersion = Math.max(...Object.keys(migrations).map(Number));\n\n // Pre-sort migrations once for efficient range lookups\n const sortedMigrations = Object.entries(migrations)\n .map(([v, m]) => [Number(v), m] as const)\n .sort((a, b) => a[0] - b[0]);\n\n const applyMigration = (db: MigrationsDb, version: number, migration: RawMigrationStep) => {\n if (version <= schemaVersion.current) {\n throw new Error(`Cannot apply migration ${version} to schema version ${schemaVersion.current}`);\n }\n\n db.startTransaction((tx) => {\n for (const statement of migration.sql) {\n tx.execute(statement.sql, statement.parameters);\n }\n if (updateLogTableName && migration.updateLogMutations) {\n for (const mutation of migration.updateLogMutations) {\n switch (mutation.type) {\n case \"rename-table\": {\n tx.execute(`DELETE FROM ${updateLogTableName} WHERE \"dataset\" = ?`, [mutation.newTable], {\n loggerLevel: \"system\",\n });\n tx.execute(\n `UPDATE ${updateLogTableName} SET \"dataset\" = ? WHERE \"dataset\" = ?`,\n [mutation.newTable, mutation.oldTable],\n { loggerLevel: \"system\" },\n );\n break;\n }\n case \"drop-table\": {\n tx.execute(`DELETE FROM ${updateLogTableName} WHERE \"dataset\" = ?`, [mutation.table], {\n loggerLevel: \"system\",\n });\n break;\n }\n case \"rename-column\": {\n tx.execute(\n `UPDATE ${updateLogTableName}\nSET \"payload\" = COALESCE(\n (\n SELECT json_group_object(CASE WHEN \"key\" = ? THEN ? ELSE \"key\" END, \"value\")\n FROM json_each(\"payload\")\n WHERE \"key\" != ?\n ),\n '{}'\n)\nWHERE \"dataset\" = ?\n AND EXISTS (SELECT 1 FROM json_each(\"payload\") WHERE \"key\" = ? OR \"key\" = ?)`,\n [\n mutation.oldColumn,\n mutation.newColumn,\n mutation.newColumn,\n mutation.table,\n mutation.oldColumn,\n mutation.newColumn,\n ],\n { loggerLevel: \"system\" },\n );\n break;\n }\n case \"add-column\":\n case \"drop-column\": {\n tx.execute(\n `UPDATE ${updateLogTableName}\nSET \"payload\" = COALESCE(\n (SELECT json_group_object(\"key\", \"value\") FROM json_each(\"payload\") WHERE \"key\" != ?),\n '{}'\n)\nWHERE \"dataset\" = ?\n AND EXISTS (SELECT 1 FROM json_each(\"payload\") WHERE \"key\" = ?)`,\n [mutation.column, mutation.table, mutation.column],\n { loggerLevel: \"system\" },\n );\n break;\n }\n default: {\n const _exhaustive: never = mutation;\n throw new Error(`Unsupported update-log mutation: ${JSON.stringify(_exhaustive)}`);\n }\n }\n }\n }\n schemaVersion.current = version;\n });\n };\n\n const migrateEvent = <Event extends MigratableEvent>(event: Event, targetVersion?: number): Event | null => {\n targetVersion ??= latestSchemaVersion;\n if (targetVersion > schemaVersion.current) {\n throw new Error(\n `Target schema version ${targetVersion} is greater than current schema version ${schemaVersion.current}`,\n );\n }\n\n if (isNoOpCrdtEventPayload(event.payload)) {\n if (event.schema_version < targetVersion) {\n event.schema_version = targetVersion;\n }\n return event;\n }\n\n if (event.schema_version >= targetVersion) {\n return event;\n }\n\n const fromVersion = event.schema_version;\n\n let crdtEvent: NewCrdtEvent | null = {\n dataset: event.dataset,\n item_id: event.item_id,\n type: event.type,\n payload: JSON.parse(event.payload),\n };\n\n for (let i = 0; i < sortedMigrations.length; i++) {\n const [version, migration] = sortedMigrations[i];\n if (version <= fromVersion) continue;\n if (version > targetVersion) break;\n\n const transformer = migration.eventTransformer;\n if (transformer) {\n crdtEvent = transformer(crdtEvent);\n if (crdtEvent === null) return null;\n }\n }\n\n if (crdtEvent === null) {\n return null;\n }\n\n event.schema_version = targetVersion;\n event.dataset = crdtEvent.dataset;\n event.item_id = crdtEvent.item_id;\n event.type = crdtEvent.type;\n event.payload = JSON.stringify(crdtEvent.payload);\n\n return event;\n };\n\n const migrateEvents = <Event extends MigratableEvent>(events: Event[], targetVersion?: number): Event[] => {\n return events\n .map((event) => migrateEvent(event, targetVersion ?? latestSchemaVersion))\n .filter((event): event is NonNullable<typeof event> => event !== null);\n };\n\n return {\n latestSchemaVersion,\n get currentSchemaVersion() {\n return schemaVersion.current;\n },\n migrateDbToLatest: (db: MigrationsDb) => {\n const currentSchemaVersion = schemaVersion.current;\n\n if (currentSchemaVersion >= latestSchemaVersion) {\n return;\n }\n\n for (let i = 0; i < sortedMigrations.length; i++) {\n const [version, migration] = sortedMigrations[i];\n if (version <= currentSchemaVersion) continue;\n applyMigration(db, version, migration);\n }\n },\n migrateEvent,\n migrateEvents,\n };\n}\n\nexport type SyncDbMigrator = ReturnType<typeof createMigrator>;\n","import { introspectDb, type TableMetadata } from \"../introspection\";\nimport { createMigrator } from \"../migrations/migrator\";\nimport type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport { createStoredValue } from \"../sqlite-crdt/stored-value\";\nimport { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport type { SqliteDriver } from \"../sqlite-driver\";\nimport type { ColumnKind, ColumnMeta } from \"./table-builder\";\n\nexport type SchemaVerificationIssue = {\n table: string;\n column?: string;\n message: string;\n};\n\nconst acceptedDataTypes: Record<ColumnKind, string[]> = {\n text: [\"TEXT\"],\n integer: [\"INTEGER\", \"INT\"],\n real: [\"REAL\", \"DOUBLE\", \"FLOAT\"],\n boolean: [\"BOOLEAN\", \"INTEGER\", \"INT\"],\n enum: [\"TEXT\"],\n};\n\n/**\n * Verify that replaying the full migration history on a fresh database produces\n * exactly the declared table schema. Applies all migrations to `driver`, which\n * must be an empty throwaway database (it is closed afterwards), introspects the\n * result, and diffs it against the `t.table()` declarations.\n *\n * Returns an empty array when migrations and schema agree. Intended for dev-time\n * checks (`startDbWorker({ verifySchema: true })`) and tests:\n * `expect(await verifySyncSchema(syncDbSchema)).toEqual([])`.\n */\nexport function verifySyncSchemaWithDriver(schema: SyncDbSchema, driver: SqliteDriver): SchemaVerificationIssue[] {\n const db = new SQLiteDbWrapper({\n driver,\n logger: () => {},\n loggerPrefix: \"schema-verify\",\n });\n\n try {\n const migrator = createMigrator({\n migrations: schema.migrations,\n schemaVersion: createStoredValue({ initialValue: -1 }),\n });\n\n try {\n migrator.migrateDbToLatest({\n startTransaction: (callback) => {\n db.executeTransaction((tx) =>\n callback({ execute: (sql, parameters, meta) => tx.execute({ sql, parameters }, meta) }),\n );\n },\n });\n } catch (error) {\n return [\n {\n table: \"(migrations)\",\n message: `migration replay failed on an empty database: ${error instanceof Error ? error.message : String(error)}`,\n },\n ];\n }\n\n const introspection = introspectDb(db);\n\n const issues: SchemaVerificationIssue[] = [];\n for (const { crdtTableName, baseTableName } of schema.tablesConfig) {\n const declared = schema.tables[crdtTableName];\n if (!declared) continue;\n verifyTable(db, baseTableName, declared.columns, introspection[baseTableName], issues);\n }\n return issues;\n } finally {\n db.close();\n }\n}\n\nexport function formatSchemaVerificationIssues(issues: SchemaVerificationIssue[]): string {\n const lines = issues.map(\n (issue) => ` - table \"${issue.table}\"${issue.column ? ` column \"${issue.column}\"` : \"\"}: ${issue.message}`,\n );\n return `sync schema verification failed (${issues.length} issue${issues.length === 1 ? \"\" : \"s\"}):\\n${lines.join(\"\\n\")}`;\n}\n\nfunction verifyTable(\n db: SQLiteDbWrapper,\n table: string,\n declaredColumns: Record<string, ColumnMeta>,\n actual: TableMetadata | undefined,\n issues: SchemaVerificationIssue[],\n) {\n if (!actual) {\n issues.push({\n table,\n message: \"declared in the schema but missing after migrations — add a createTable step in a new version\",\n });\n return;\n }\n if (actual.isView) {\n issues.push({ table, message: \"expected a base table, but migrations created a view with this name\" });\n return;\n }\n\n const actualColumns = new Map(actual.columns.map((column) => [column.name, column]));\n\n for (const [name, meta] of Object.entries(declaredColumns)) {\n const column = actualColumns.get(name);\n if (!column) {\n issues.push({\n table,\n column: name,\n message: \"declared in the schema but missing after migrations — add an addColumn step in a new version\",\n });\n continue;\n }\n\n const accepted = acceptedDataTypes[meta.kind];\n if (!accepted.includes(column.dataType.toUpperCase())) {\n issues.push({\n table,\n column: name,\n message: `type mismatch — schema declares ${meta.kind} (${accepted.join(\"/\")}), migrated column is ${column.dataType.toUpperCase()}`,\n });\n }\n\n if (column.isNullable !== meta.nullable) {\n issues.push({\n table,\n column: name,\n message: meta.nullable\n ? \"schema declares the column nullable, but the migrated column is NOT NULL\"\n : \"schema declares the column NOT NULL, but the migrated column is nullable\",\n });\n }\n\n if (name === \"id\" && !column.isPrimaryKey) {\n issues.push({ table, column: name, message: \"the id column must be the PRIMARY KEY\" });\n }\n\n verifyDefault(db, table, name, meta, column.defaultValueSql, issues);\n }\n\n for (const column of actual.columns) {\n if (!(column.name in declaredColumns)) {\n issues.push({\n table,\n column: column.name,\n message:\n \"created by migrations but not declared in the schema — sync event payloads include every column, so declare it (or drop it in a new version)\",\n });\n }\n }\n}\n\nfunction verifyDefault(\n db: SQLiteDbWrapper,\n table: string,\n column: string,\n meta: ColumnMeta,\n defaultValueSql: string | null,\n issues: SchemaVerificationIssue[],\n) {\n // A declared `null` default and an absent DEFAULT clause are equivalent in SQLite\n // (a column with no DEFAULT defaults to NULL), so don't flag that pairing.\n if (meta.hasDefault && meta.defaultValue !== null && defaultValueSql === null) {\n issues.push({\n table,\n column,\n message: `schema declares default ${formatExpected(meta)}, but the migrated column has no default`,\n });\n return;\n }\n if (!meta.hasDefault && defaultValueSql !== null) {\n issues.push({\n table,\n column,\n message: `migrated column has default ${defaultValueSql}, but the schema declares none`,\n });\n return;\n }\n if (!meta.hasDefault || defaultValueSql === null) {\n return;\n }\n\n // Declared defaults are always constants (ColumnBuilder.default takes a JS value),\n // so a non-constant migrated default (e.g. strftime(...)) is always a real mismatch.\n let actual: unknown;\n try {\n actual = db.execute<{ value: unknown }>({\n sql: `SELECT (${defaultValueSql}) AS value`,\n parameters: [],\n }).rows[0]?.value;\n } catch {\n actual = undefined;\n }\n\n if (actual !== expectedDefaultValue(meta)) {\n issues.push({\n table,\n column,\n message: `default mismatch — schema declares ${formatExpected(meta)}, migrated column default is ${defaultValueSql}`,\n });\n }\n}\n\n/** The declared default normalized to how SQLite stores it (booleans as 0/1). */\nfunction expectedDefaultValue(meta: ColumnMeta): string | number | null {\n if (meta.defaultValue === null) return null;\n switch (meta.kind) {\n case \"boolean\":\n return meta.defaultValue ? 1 : 0;\n case \"integer\":\n case \"real\":\n return Number(meta.defaultValue);\n case \"text\":\n case \"enum\":\n return String(meta.defaultValue);\n }\n}\n\nfunction formatExpected(meta: ColumnMeta): string {\n const expected = expectedDefaultValue(meta);\n return typeof expected === \"string\" ? `'${expected}'` : String(expected);\n}\n","import type { Kysely } from \"kysely\";\nimport type { SystemDbConfig } from \"../migrations/system-schema\";\nimport type { InternalSQLiteWrapper } from \"../sqlite-db-wrapper\";\nimport type { SqliteDriverValue } from \"../sqlite-driver\";\nimport { quoteId } from \"../utils\";\nimport {\n type CrdtEventType,\n type CrdtUpdateLogItem,\n type CrdtUpdateLogPayload,\n isNoOpCrdtEventPayload,\n} from \"./crdt-table-schema\";\nimport type { StateHashAccumulator } from \"./state-hash\";\n\nexport type PendingCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n timestamp: string;\n payload: string;\n};\n\nexport const createSQLiteCrdtApplyFunction = ({\n db,\n dbConfig,\n getStateHash,\n}: {\n db: InternalSQLiteWrapper<any>;\n dbConfig: SystemDbConfig;\n getStateHash?: () => StateHashAccumulator | null;\n}) => {\n const applyCrdtEvent = createCrdtApplyFunction({\n getStateHash,\n getCrdtUpdateLog(opts) {\n const [metaRow] = db.executePrepared(\n \"get-item-crdt-meta\",\n {\n item_id: opts.itemId,\n dataset: opts.dataset,\n },\n (db, params) => {\n return (db as unknown as Kysely<{ table: CrdtUpdateLogItem }>)\n .selectFrom(dbConfig.updateLogTable.fullIdentifier as \"table\")\n .select(\"payload\")\n .where(\"item_id\", \"=\", params(\"item_id\"))\n .where(\"dataset\", \"=\", params(\"dataset\"));\n },\n { loggerLevel: \"system\" },\n );\n const meta = metaRow ? (JSON.parse(metaRow.payload) as CrdtUpdateLogPayload) : null;\n return meta;\n },\n insertCrdtUpdateLog(opts) {\n db.executePrepared(\n \"insert-crdt-update-log\",\n {\n item_id: opts.itemId,\n dataset: opts.dataset,\n payload: opts.payload,\n },\n (db, params) =>\n (db as unknown as Kysely<{ table: CrdtUpdateLogItem }>)\n .insertInto(dbConfig.updateLogTable.fullIdentifier as \"table\")\n .values({\n item_id: params(\"item_id\"),\n dataset: params(\"dataset\"),\n payload: params(\"payload\"),\n }),\n { loggerLevel: \"system\" },\n );\n },\n updateCrdtUpdateLog(opts) {\n db.executePrepared(\n \"update-crdt-update-log\",\n {\n item_id: opts.itemId,\n dataset: opts.dataset,\n payload: opts.payload,\n },\n (db, params) =>\n (db as unknown as Kysely<{ table: CrdtUpdateLogItem }>)\n .updateTable(dbConfig.updateLogTable.fullIdentifier as \"table\")\n .set({\n payload: params(\"payload\"),\n })\n .where(\"item_id\", \"=\", params(\"item_id\"))\n .where(\"dataset\", \"=\", params(\"dataset\")),\n { loggerLevel: \"system\" },\n );\n },\n insertItem(opts) {\n // Key by the sorted column set, not just the dataset: executePrepared caches\n // the compiled INSERT forever, freezing its column list. A later create event\n // omitting a column would otherwise bind undefined (-> NULL) against the stale\n // statement, bypassing the table DEFAULT. Sorting makes the key canonical so\n // events with the same columns in any order share one statement.\n const keys = Object.keys(opts.payload).sort();\n db.executePrepared(\n `crdt-insert-item-${opts.dataset}-${keys.join(\"-\")}`,\n opts.payload,\n (db, params) => db.insertInto(opts.dataset).values(Object.fromEntries(keys.map((key) => [key, params(key)]))),\n { loggerLevel: \"system\" },\n );\n },\n updateItem(opts) {\n const keys = Array.from(Object.keys(opts.payload));\n keys.sort();\n db.executePreparedRaw({\n key: `update-item-${opts.dataset}-${keys.join(\"-\")}`,\n sql: `update ${quoteId(opts.dataset)} set ${keys.map((key) => `${quoteId(key)} = ?`).join(\",\")} where id = ?`,\n params: [...keys.map((key) => opts.payload[key]), opts.itemId] as SqliteDriverValue[],\n meta: { loggerLevel: \"system\" },\n });\n },\n });\n\n return applyCrdtEvent;\n};\n\ntype CreateCrdtApplyOpts = {\n getStateHash?: () => StateHashAccumulator | null;\n getCrdtUpdateLog: (opts: { itemId: string; dataset: string }) => CrdtUpdateLogPayload | null;\n insertItem: (opts: { dataset: string; payload: Record<string, unknown> }) => void;\n insertCrdtUpdateLog: (opts: { dataset: string; itemId: string; payload: string }) => void;\n updateItem: (opts: { dataset: string; itemId: string; payload: Record<string, unknown> }) => void;\n updateCrdtUpdateLog: (opts: { dataset: string; itemId: string; payload: string }) => void;\n};\n\nexport function createCrdtApplyFunction({\n getStateHash,\n getCrdtUpdateLog,\n insertItem,\n insertCrdtUpdateLog,\n updateItem,\n updateCrdtUpdateLog,\n}: CreateCrdtApplyOpts) {\n type ItemCreatedOpts = {\n event: PendingCrdtEvent;\n };\n const applyItemCreated = ({ event }: ItemCreatedOpts) => {\n const eventPayload = JSON.parse(event.payload);\n\n eventPayload.tombstone = 0;\n insertItem({ dataset: event.dataset, payload: eventPayload });\n\n const newUpdateLog = {} as Record<string, string>;\n for (const key of Object.keys(eventPayload)) {\n newUpdateLog[key] = event.timestamp;\n }\n\n insertCrdtUpdateLog({\n dataset: event.dataset,\n itemId: event.item_id,\n payload: JSON.stringify(newUpdateLog),\n });\n getStateHash?.()?.addItem(event.dataset, event.item_id, newUpdateLog);\n };\n\n type ItemUpdatedOpts = {\n event: PendingCrdtEvent;\n meta: CrdtUpdateLogPayload;\n };\n const applyItemUpdated = ({ event, meta }: ItemUpdatedOpts) => {\n if (!meta) {\n throw new Error(`Item ${event.item_id} in dataset ${event.dataset} not found`);\n }\n // A delete carries no field data on the wire — the tombstone is a local\n // materialization of the soft-delete, stamped here with the event's HLC so\n // it competes with concurrent field edits under the same last-write-wins rule.\n const eventPayload = event.type === \"item-deleted\" ? { tombstone: 1 } : JSON.parse(event.payload);\n if (event.type === \"item-created\") {\n eventPayload.tombstone = 0;\n }\n\n const updatePayload = {} as Record<string, unknown>;\n const replacedCells: [column: string, previousHlc: string | undefined][] = [];\n const previousCreateHlc = meta.id;\n\n for (const [key, value] of Object.entries(eventPayload)) {\n if (key === \"id\") {\n continue;\n }\n\n const lastUpdateTimestamp = meta[key];\n const currentUpdateTimestamp = event.timestamp;\n\n if (!lastUpdateTimestamp || !currentUpdateTimestamp || currentUpdateTimestamp > lastUpdateTimestamp) {\n updatePayload[key] = value;\n replacedCells.push([key, lastUpdateTimestamp]);\n meta[key] = currentUpdateTimestamp;\n }\n }\n\n const isLatestCreate = event.type === \"item-created\" && (!previousCreateHlc || event.timestamp > previousCreateHlc);\n if (isLatestCreate) {\n meta.id = event.timestamp;\n }\n\n if (replacedCells.length === 0 && !isLatestCreate) {\n return;\n }\n\n if (replacedCells.length > 0) {\n updateItem({\n dataset: event.dataset,\n itemId: event.item_id,\n payload: updatePayload,\n });\n }\n updateCrdtUpdateLog({\n dataset: event.dataset,\n itemId: event.item_id,\n payload: JSON.stringify(meta),\n });\n getStateHash?.()?.updateItem(event.dataset, event.item_id, meta, previousCreateHlc, replacedCells);\n };\n\n return (event: PendingCrdtEvent) => {\n if (isNoOpCrdtEventPayload(event.payload)) {\n return;\n }\n\n const meta = getCrdtUpdateLog({\n itemId: event.item_id,\n dataset: event.dataset,\n });\n\n // TODO Check primary key / unique constraints\n\n if (event.type !== \"item-created\" && event.type !== \"item-updated\" && event.type !== \"item-deleted\") {\n throw new Error(`Unknown event type: ${event.type}`);\n }\n\n if (meta) {\n applyItemUpdated({ event, meta });\n return;\n }\n\n if (event.type === \"item-created\") {\n applyItemCreated({ event });\n return;\n }\n\n throw new Error(`Item ${event.item_id} in dataset ${event.dataset} not found`);\n };\n}\n","import { sql } from \"kysely\";\nimport { deserializeHLC, type HLCCounter, serializeHLC } from \"../hlc\";\nimport type { SyncDbMigrator } from \"../migrations/migrator\";\nimport type { SystemDbConfig } from \"../migrations/system-schema\";\nimport { CrdtEventValidationError, validateNewCrdtEvent } from \"../schema/validate-crdt-event\";\nimport type { InternalSQLiteTransactionWrapper, InternalSQLiteWrapper } from \"../sqlite-db-wrapper\";\nimport { createTypedEventTarget, ensureSingletonExecution, quoteId } from \"../utils\";\nimport { createSQLiteCrdtApplyFunction } from \"./apply-crdt-event\";\nimport type { SyncDbSchema } from \"./crdt-schema\";\nimport {\n CRDT_EVENT_NO_OP_PAYLOAD,\n type CrdtEventOrigin,\n type CrdtEventStatus,\n type CrdtEventType,\n type CrdtUpdateLogItem,\n isNoOpCrdtEventPayload,\n type NewCrdtEvent,\n type PersistedCrdtEvent,\n} from \"./crdt-table-schema\";\nimport { createStateHashTracker, type StateHashStore } from \"./state-hash\";\n\ntype LocalCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: string;\n timestamp: string;\n schema_version: number;\n};\n\nexport type OwnCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: string;\n timestamp?: undefined;\n schema_version?: undefined;\n};\n\nexport type OwnCrdtSnapshot = {\n dataset: string;\n item_id: string;\n patch: Record<string, unknown>;\n};\n\nexport type CrdtChangeIntent = {\n seq: number;\n dataset: string;\n type: CrdtEventType;\n item_id: string;\n new_item_id: string | null;\n payload_json: string;\n};\n\ntype RemoteCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: string;\n timestamp: string;\n schema_version: number;\n};\n\ntype EnqueuedCrdtEvent = LocalCrdtEvent | OwnCrdtEvent | RemoteCrdtEvent;\n\nexport type GetEventsOptions = {\n afterSyncId?: number;\n status?: CrdtEventStatus;\n excludeOrigin?: string;\n excludeNodeId?: string;\n limit?: number;\n};\n\nexport type GetEventsBatchQuery = {\n afterSyncId: number;\n status: CrdtEventStatus;\n limit: number;\n} & (\n | { excludeOrigin: CrdtEventOrigin; excludeNodeId?: undefined }\n | { excludeOrigin?: undefined; excludeNodeId: string }\n);\n\nexport type GetEventsBatch = {\n events: PersistedCrdtEvent[];\n hasMore: boolean;\n nextSyncId: number;\n};\n\nexport type EnqueueEventsResult = {\n beforeSyncId: number;\n afterSyncId: number;\n /** Resolves when the enqueued events have been processed (applied/deduped/failed). */\n processed: Promise<void>;\n};\n\nexport type EventUpdate = {\n status: CrdtEventStatus;\n schema_version: number;\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: string;\n};\n\ntype StorageHLC = Pick<HLCCounter, \"getNextHLC\" | \"mergeHLC\" | \"restoreHLC\">;\n\ntype DbSyncerStorage = {\n nodeId: string;\n initialLocalSyncId: number;\n migrator: SyncDbMigrator;\n db: InternalSQLiteWrapper<any>;\n dbConfig: SystemDbConfig;\n hlc: StorageHLC;\n stateHash?: StateHashStore;\n onEventApplied?: (event: PersistedCrdtEvent) => void;\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">;\n};\n\nexport type CrdtStorage = Omit<ReturnType<typeof createCrdtStorage>, \"internal\">;\n\nexport type InternalCrdtStorage = ReturnType<typeof createCrdtStorage>;\n\ntype EventsAppliedPayload = {\n syncId: number;\n stateHash: string | null;\n};\n\ntype InternalDbSchema = {\n _crdt_events: PersistedCrdtEvent;\n _crdt_update_log: CrdtUpdateLogItem;\n};\n\ntype FirstAcceptedSyncIdRow = { timestamp: string; first_sync_id: number };\n\nexport function createCrdtStorage(storage: DbSyncerStorage) {\n let localSyncId = storage.initialLocalSyncId;\n\n const db = storage.db as InternalSQLiteWrapper<InternalDbSchema>;\n\n const crdtEventsTable = storage.dbConfig.eventsTable.fullIdentifier as \"_crdt_events\";\n const quotedEventsTable = quoteId(storage.dbConfig.eventsTable.fullIdentifier);\n const noOpPayloadSqlLiteral = `'${CRDT_EVENT_NO_OP_PAYLOAD}'`;\n\n const [persistedClock] = db.executePreparedRaw<[], { timestamp: string | null }>({\n key: \"get-persisted-hlc\",\n sql: `select max(\"timestamp\") as \"timestamp\" from (\n select max(\"timestamp\") as \"timestamp\" from ${quotedEventsTable} where \"status\" = 'applied'\n union all\n select max(fields.value) as \"timestamp\"\n from ${quoteId(storage.dbConfig.updateLogTable.fullIdentifier)} as updates, json_each(updates.payload) as fields\n )`,\n meta: { loggerLevel: \"system\" },\n });\n if (persistedClock?.timestamp) {\n storage.hlc.restoreHLC(deserializeHLC(persistedClock.timestamp));\n }\n\n const getInitialSequentialSyncId = () => {\n const [firstPendingEvent] = db.executePrepared(\n \"get-first-pending-event\",\n { status: \"pending\" as const },\n (db, params) =>\n db\n .selectFrom(crdtEventsTable)\n .select(\"sync_id\")\n .where(\"status\", \"=\", params(\"status\"))\n .orderBy(\"sync_id\", \"asc\")\n .limit(sql.lit(1)),\n { loggerLevel: \"system\" },\n );\n\n return firstPendingEvent ? firstPendingEvent.sync_id - 1 : localSyncId;\n };\n\n let sequentialSyncId = getInitialSequentialSyncId();\n\n const eventTarget = createTypedEventTarget<{\n \"events-applied\": EventsAppliedPayload;\n \"remote-event-apply-failed\": { syncId: number };\n }>();\n\n // Column order is the JSON array order for persist/read.\n const EVENT_COLUMNS = [\n \"sync_id\",\n \"schema_version\",\n \"status\",\n \"type\",\n \"timestamp\",\n \"origin\",\n \"source_node_id\",\n \"dataset\",\n \"item_id\",\n \"payload\",\n ] as const;\n\n const quotedEventColumns = EVENT_COLUMNS.map((column) => quoteId(column)).join(\", \");\n\n const persistEventsSql = `insert into ${quotedEventsTable} (${quotedEventColumns}) select ${EVENT_COLUMNS.map(\n (_, index) => `\"value\"->>${index}`,\n ).join(\", \")} from json_each(?)`;\n\n const pendingEventsSql = `select json_group_array(json_array(${quotedEventColumns})) as \"batch\" from (select ${quotedEventColumns} from ${quotedEventsTable} where \"status\" = ? order by \"sync_id\" asc limit ?)`;\n\n const PERSIST_EVENTS_CHUNK_SIZE = 100;\n\n const eventToRow = (event: PersistedCrdtEvent) => [\n event.sync_id,\n event.schema_version,\n event.status,\n event.type,\n event.timestamp,\n event.origin,\n event.source_node_id,\n event.dataset,\n event.item_id,\n event.payload,\n ];\n\n type EventRow = [\n number,\n number,\n CrdtEventStatus,\n CrdtEventType,\n string,\n CrdtEventOrigin,\n string,\n string,\n string,\n string,\n ];\n\n const rowToEvent = (row: EventRow): PersistedCrdtEvent => ({\n sync_id: row[0],\n schema_version: row[1],\n status: row[2],\n type: row[3],\n timestamp: row[4],\n origin: row[5],\n source_node_id: row[6],\n dataset: row[7],\n item_id: row[8],\n payload: row[9],\n });\n\n const persistEvents = (tx: InternalSQLiteTransactionWrapper<InternalDbSchema>, events: PersistedCrdtEvent[]) => {\n for (let offset = 0; offset < events.length; offset += PERSIST_EVENTS_CHUNK_SIZE) {\n const chunk = events.slice(offset, offset + PERSIST_EVENTS_CHUNK_SIZE);\n tx.executePreparedRaw<[string], never>({\n key: \"persist-crdt-events\",\n sql: persistEventsSql,\n params: [JSON.stringify(chunk.map(eventToRow))],\n meta: { loggerLevel: \"system\" },\n });\n }\n };\n\n const readPendingEvents = (limit: number): PersistedCrdtEvent[] => {\n const [row] = db.executePreparedRaw<[string, number], { batch: string }>({\n key: \"get-enqueued-pending-events\",\n sql: pendingEventsSql,\n params: [\"pending\", limit],\n meta: { loggerLevel: \"system\" },\n });\n\n return (JSON.parse(row.batch) as EventRow[]).map(rowToEvent);\n };\n\n const enqueueEvents = (\n origin: CrdtEventOrigin,\n sourceNodeId: string,\n events: EnqueuedCrdtEvent[],\n ): EnqueueEventsResult => {\n const beforeSyncId = localSyncId;\n if (events.length === 0) {\n return { beforeSyncId, afterSyncId: beforeSyncId, processed: Promise.resolve() };\n }\n\n db.executeTransaction((tx) => {\n const persistedEvents = events.map(\n (event): PersistedCrdtEvent => ({\n schema_version: event.schema_version ?? storage.migrator.currentSchemaVersion,\n timestamp: event.timestamp ?? serializeHLC(storage.hlc.getNextHLC()),\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n origin: origin,\n source_node_id: sourceNodeId,\n payload: event.payload,\n sync_id: ++localSyncId,\n status: \"pending\",\n }),\n );\n persistEvents(tx, persistedEvents);\n });\n\n return { beforeSyncId, afterSyncId: localSyncId, processed: processEnqueuedEvents() };\n };\n\n const enqueueLocalEvents = (events: LocalCrdtEvent[], sourceNodeId: string): EnqueueEventsResult => {\n const known = getFirstAcceptedSyncIdsByTimestamp(db, events);\n const newEvents = events.filter((event) => {\n if (known.has(event.timestamp)) {\n return false;\n }\n known.set(event.timestamp, 0);\n return true;\n });\n return enqueueEvents(\"local\", sourceNodeId, newEvents);\n };\n\n const enqueueRemoteEvents = (events: RemoteCrdtEvent[]): EnqueueEventsResult => {\n return enqueueEvents(\"remote\", \"\", events);\n };\n\n const notifyEventsApplied = (events: PersistedCrdtEvent[]) => {\n const onEventApplied = storage.onEventApplied;\n if (!onEventApplied || events.length === 0) {\n return;\n }\n\n queueMicrotask(() => {\n let callbackFailed = false;\n let firstError: unknown;\n for (const event of events) {\n try {\n onEventApplied(event);\n } catch (error) {\n callbackFailed = true;\n firstError ??= error;\n }\n }\n if (callbackFailed) {\n throw firstError;\n }\n });\n };\n\n const applyOwnEvents = (events: OwnCrdtEvent[]) => {\n const schema = storage.schema;\n if (!schema) {\n throw new Error(\"applyOwnEvents requires a sync schema; none was provided to createCrdtStorage\");\n }\n\n const validatedEvents: NewCrdtEvent[] = [];\n const errors: string[] = [];\n\n for (const [index, event] of events.entries()) {\n const result = validateNewCrdtEvent(schema, {\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n });\n\n if (!result.success) {\n errors.push(...result.errors.map((error) => `[${index}] ${error}`));\n continue;\n }\n\n validatedEvents.push(result.event);\n }\n\n if (errors.length > 0) {\n throw new CrdtEventValidationError(errors);\n }\n\n db.executeTransaction((tx) => {\n const persistedEvents = validatedEvents.map(\n (event): PersistedCrdtEvent => ({\n schema_version: storage.migrator.currentSchemaVersion,\n timestamp: serializeHLC(storage.hlc.getNextHLC()),\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n origin: \"own-applied\",\n source_node_id: storage.nodeId,\n payload: JSON.stringify(event.payload),\n sync_id: ++localSyncId,\n status: \"pending\",\n }),\n );\n persistEvents(tx, persistedEvents);\n withStateHash(() => {\n for (const persistedEvent of persistedEvents) {\n applyCrdtEvent(persistedEvent);\n }\n });\n });\n\n void processEnqueuedEvents();\n };\n\n const applyOwnSnapshot = (snapshot: OwnCrdtSnapshot) => {\n const syncId = localSyncId + 1;\n db.executeTransaction((tx) => {\n const [currentRow] = tx.executePreparedRaw<[string], Record<string, unknown>>({\n key: `get-crdt-snapshot-row-${snapshot.dataset}`,\n sql: `select * from ${quoteId(snapshot.dataset)} where \"id\" = ? limit 1`,\n params: [snapshot.item_id],\n meta: { loggerLevel: \"system\" },\n });\n\n const event: PersistedCrdtEvent = {\n schema_version: storage.migrator.currentSchemaVersion,\n timestamp: serializeHLC(storage.hlc.getNextHLC()),\n type: \"item-created\",\n dataset: snapshot.dataset,\n item_id: snapshot.item_id,\n origin: \"own-applied\",\n source_node_id: storage.nodeId,\n payload: JSON.stringify({\n ...(currentRow ?? {}),\n ...snapshot.patch,\n id: snapshot.item_id,\n tombstone: false,\n }),\n sync_id: syncId,\n status: \"pending\",\n };\n\n // An event newer than the snapshot (e.g. a pending remote event stamped by a clock ahead of\n // ours) still wins LWW, and fresh replicas can only replay it on top of the row's original\n // history, so the history is kept intact until a later snapshot outranks every event.\n const [newerEvent] = tx.executePreparedRaw<[string, string, string], { sync_id: number }>({\n key: \"get-crdt-row-event-newer-than-snapshot\",\n sql: `select \"sync_id\" from ${quotedEventsTable}\nwhere \"dataset\" = ? and \"item_id\" = ? and \"timestamp\" > ? and \"payload\" <> ${noOpPayloadSqlLiteral}\nlimit 1`,\n params: [event.dataset, event.item_id, event.timestamp],\n meta: { loggerLevel: \"system\" },\n });\n\n if (!newerEvent) {\n tx.executePreparedRaw<[string, string, string], never>({\n key: \"replace-crdt-row-history-with-no-ops\",\n // Keep the no-op sentinel literal in this predicate so SQLite can use the\n // matching partial index. A bound parameter does not imply its predicate.\n sql: `update ${quotedEventsTable}\nset \"payload\" = ?\nwhere \"dataset\" = ? and \"item_id\" = ? and \"payload\" <> ${noOpPayloadSqlLiteral}`,\n params: [CRDT_EVENT_NO_OP_PAYLOAD, event.dataset, event.item_id],\n meta: { loggerLevel: \"system\" },\n });\n }\n persistEvents(tx, [event]);\n applyCrdtEvent(event);\n });\n localSyncId = syncId;\n\n void processEnqueuedEvents();\n };\n\n const applyOwnEventFromTransaction = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n event: OwnCrdtEvent,\n ) => {\n const persistedEvent: PersistedCrdtEvent = {\n schema_version: storage.migrator.currentSchemaVersion,\n timestamp: serializeHLC(storage.hlc.getNextHLC()),\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n origin: \"own-applied\",\n source_node_id: storage.nodeId,\n payload: event.payload,\n sync_id: ++localSyncId,\n status: \"pending\",\n };\n\n persistEvents(tx, [persistedEvent]);\n applyCrdtEvent(persistedEvent);\n };\n\n const applyOwnIntentsFromTransaction = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n intents: CrdtChangeIntent[],\n ) =>\n withStateHash(() => {\n let appliedEvents = 0;\n\n for (const intent of intents) {\n if (intent.type === \"item-updated\") {\n if (intent.item_id !== intent.new_item_id) {\n throw new Error(\n `Cannot update the \"id\" column of an item. It is used to identify the item and must be immutable.`,\n );\n }\n if (intent.payload_json === \"{}\") {\n continue;\n }\n }\n\n applyOwnEventFromTransaction(tx, {\n type: intent.type,\n dataset: intent.dataset,\n item_id: intent.item_id,\n payload: intent.payload_json,\n });\n appliedEvents++;\n }\n\n return { appliedEvents };\n });\n\n const dispatchEventsApplied = (previousSequentialSyncId: number) => {\n if (sequentialSyncId <= previousSequentialSyncId) {\n return;\n }\n eventTarget.dispatchEvent(\"events-applied\", {\n syncId: sequentialSyncId,\n stateHash: sequentialSyncId === localSyncId ? getStateHash() : null,\n });\n };\n\n const publishProcessedBatch = ({\n previousSequentialSyncId,\n nextSequentialSyncId,\n appliedEvents,\n }: {\n previousSequentialSyncId: number;\n nextSequentialSyncId: number;\n appliedEvents: PersistedCrdtEvent[];\n }) => {\n sequentialSyncId = nextSequentialSyncId;\n\n // Schedule per-event observers before dispatching cursor progress, matching\n // the previous ordering while keeping both signals behind one post-commit API.\n notifyEventsApplied(appliedEvents);\n dispatchEventsApplied(previousSequentialSyncId);\n };\n\n const hasPendingEvents = (): boolean => {\n const events = db.executePrepared(\n \"has-pending-events\",\n { status: \"pending\" as const },\n (db, params) =>\n db.selectFrom(crdtEventsTable).select(\"sync_id\").where(\"status\", \"=\", params(\"status\")).limit(sql.lit(1)),\n { loggerLevel: \"system\" },\n );\n return events.length > 0;\n };\n\n const getEventsBatch = (options: GetEventsBatchQuery): GetEventsBatch => {\n const limit = options.limit ?? 50;\n\n const queryParams = {\n limit: limit + 1,\n status: options.status ?? null,\n afterSyncId: options.afterSyncId ?? null,\n excludeOrigin: options.excludeOrigin ?? null,\n excludeNodeId: options.excludeNodeId ?? null,\n maxSyncId: options.status === \"applied\" ? sequentialSyncId : localSyncId,\n };\n\n const filterKeys = [\n queryParams.excludeNodeId ? \"nodeid\" : \"no-nodeid\",\n queryParams.excludeOrigin ? \"origin\" : \"no-origin\",\n ];\n\n const events = db.executePrepared(\n `get-events-batch-${filterKeys.join(\"-\")}`,\n queryParams,\n (db, params) =>\n db\n .selectFrom(crdtEventsTable)\n .where(\"sync_id\", \">\", params(\"afterSyncId\"))\n .where(\"sync_id\", \"<=\", params(\"maxSyncId\"))\n .where(\"status\", \"=\", params(\"status\"))\n .$if(!!queryParams.excludeNodeId, (qb) => qb.where(\"source_node_id\", \"!=\", params(\"excludeNodeId\")))\n .$if(!!queryParams.excludeOrigin, (qb) => qb.where(\"origin\", \"!=\", params(\"excludeOrigin\")))\n .selectAll()\n .limit(params(\"limit\"))\n .orderBy(\"sync_id\", \"asc\"),\n { loggerLevel: \"system\" },\n );\n\n const hasMore = events.length > limit;\n if (hasMore) {\n events.pop();\n }\n\n const lastReturnedSyncId = events[events.length - 1]?.sync_id ?? options.afterSyncId ?? 0;\n let nextSyncId = lastReturnedSyncId;\n if (!hasMore && options.status === \"applied\") {\n nextSyncId = Math.max(lastReturnedSyncId, queryParams.maxSyncId);\n }\n\n return {\n events,\n hasMore,\n nextSyncId,\n };\n };\n\n // Storage is quiescent when there is nothing left to converge: no events waiting\n // to be applied, and no local applied events past `pushedSyncId` still waiting to\n // be pushed to the remote. When quiescent and caught up, the local and remote node\n // share the exact same set of applied events, so their HLC checksums must match.\n const checkIsQuiescent = (pushedSyncId: number): boolean => {\n if (hasPendingEvents()) {\n return false;\n }\n\n const unpushed = getEventsBatch({\n status: \"applied\",\n afterSyncId: pushedSyncId,\n excludeOrigin: \"remote\",\n limit: 1,\n });\n\n return unpushed.events.length === 0;\n };\n\n const stateHash = createStateHashTracker({\n db,\n updateLogTable: storage.dbConfig.updateLogTable.fullIdentifier,\n schema: storage.schema,\n getSchemaVersion: () => storage.migrator.currentSchemaVersion,\n store: storage.stateHash ?? null,\n });\n const withStateHash = stateHash.track;\n const getStateHash = stateHash.read;\n\n const applyCrdtEventWithoutStateHash = createSQLiteCrdtApplyFunction({\n db,\n dbConfig: storage.dbConfig,\n getStateHash: () => stateHash.active,\n });\n const applyCrdtEvent = (event: Parameters<typeof applyCrdtEventWithoutStateHash>[0]) =>\n withStateHash(() => applyCrdtEventWithoutStateHash(event));\n\n const getFirstAcceptedSyncIdsByTimestamp = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n events: { timestamp: string }[],\n ) => {\n const timestamps = Array.from(new Set(events.map((event) => event.timestamp)));\n if (timestamps.length === 0) {\n return new Map<string, number>();\n }\n\n const rows =\n timestamps.length === 1\n ? tx.executePreparedRaw<string[], FirstAcceptedSyncIdRow>({\n key: \"get-first-accepted-sync-id-by-timestamp\",\n sql: `select \"timestamp\", min(\"sync_id\") as \"first_sync_id\" from ${quotedEventsTable} where \"status\" = 'applied' and \"timestamp\" = ? group by \"timestamp\"`,\n params: timestamps,\n meta: { loggerLevel: \"system\" },\n })\n : tx.executePreparedRaw<string[], FirstAcceptedSyncIdRow>({\n key: \"get-first-accepted-sync-ids-by-timestamp\",\n sql: `select \"timestamp\", min(\"sync_id\") as \"first_sync_id\" from ${quotedEventsTable} where \"status\" = 'applied' and \"timestamp\" in (select \"value\" from json_each(?)) group by \"timestamp\"`,\n params: [JSON.stringify(timestamps)],\n meta: { loggerLevel: \"system\" },\n });\n\n return new Map(rows.map((row) => [row.timestamp, row.first_sync_id]));\n };\n\n const updateEventStatuses = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n events: PersistedCrdtEvent[],\n ) => {\n const syncIdsByStatus = new Map<Exclude<CrdtEventStatus, \"pending\">, number[]>();\n for (const event of events) {\n if (event.status === \"pending\") {\n throw new Error(`Event ${event.sync_id} is still pending after processing`);\n }\n\n const syncIds = syncIdsByStatus.get(event.status) ?? [];\n syncIds.push(event.sync_id);\n syncIdsByStatus.set(event.status, syncIds);\n }\n\n for (const [status, syncIds] of syncIdsByStatus) {\n if (syncIds.length === 1) {\n tx.executePreparedRaw<[string, number], never>({\n key: \"update-crdt-event-status\",\n sql: `update ${quotedEventsTable} set \"status\" = ? where \"sync_id\" = ?`,\n params: [status, syncIds[0]],\n meta: { loggerLevel: \"system\" },\n });\n continue;\n }\n\n tx.executePreparedRaw<[string, string], never>({\n key: \"update-crdt-event-status-batch\",\n sql: `update ${quotedEventsTable} set \"status\" = ? where \"sync_id\" in (select \"value\" from json_each(?))`,\n params: [status, JSON.stringify(syncIds)],\n meta: { loggerLevel: \"system\" },\n });\n }\n };\n\n const processPersistedEvent = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n event: PersistedCrdtEvent,\n firstAcceptedSyncIdByTimestamp: Map<string, number>,\n statusOnlyEvents: PersistedCrdtEvent[],\n ) => {\n if (event.status !== \"pending\") {\n throw new Error(`Event ${event.sync_id} is not pending`);\n }\n\n const persistedEventData = {\n schema_version: event.schema_version,\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n };\n\n try {\n if (event.origin === \"own-applied\") {\n event.status = \"applied\";\n return event;\n }\n\n const firstAcceptedSyncId = firstAcceptedSyncIdByTimestamp.get(event.timestamp);\n if (firstAcceptedSyncId !== undefined && firstAcceptedSyncId < event.sync_id) {\n event.status = \"deduped\";\n return event;\n }\n\n // Always advance HLC, even for no-op events, to maintain monotonic ordering\n if (event.origin === \"local\" || event.origin === \"remote\") {\n storage.hlc.mergeHLC(deserializeHLC(event.timestamp));\n }\n\n if (isNoOpCrdtEventPayload(event.payload)) {\n applyCrdtEvent(event);\n event.status = \"applied\";\n return event;\n }\n\n const migratedEvent = storage.migrator.migrateEvent(event, storage.migrator.latestSchemaVersion);\n\n if (migratedEvent === null) {\n // Event was dropped during migration (e.g., table was deleted)\n event.schema_version = storage.migrator.latestSchemaVersion;\n event.payload = CRDT_EVENT_NO_OP_PAYLOAD;\n\n applyCrdtEvent(event);\n event.status = \"applied\";\n return event;\n }\n\n event.schema_version = migratedEvent.schema_version;\n event.type = migratedEvent.type;\n event.dataset = migratedEvent.dataset;\n event.item_id = migratedEvent.item_id;\n event.payload = migratedEvent.payload;\n\n applyCrdtEvent(event);\n event.status = \"applied\";\n } catch (error) {\n console.error(\"Error applying enqueued CRDT event\", error);\n event.status = \"failed\";\n } finally {\n const eventDataChanged =\n event.schema_version !== persistedEventData.schema_version ||\n event.type !== persistedEventData.type ||\n event.dataset !== persistedEventData.dataset ||\n event.item_id !== persistedEventData.item_id ||\n event.payload !== persistedEventData.payload;\n\n if (eventDataChanged) {\n tx.executePrepared(\n \"update-crdt-event-full\",\n event,\n (db, params) =>\n db\n .updateTable(crdtEventsTable)\n .set({\n status: params(\"status\"),\n schema_version: params(\"schema_version\"),\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n })\n .where(\"sync_id\", \"=\", params(\"sync_id\")),\n { loggerLevel: \"system\" },\n );\n } else {\n statusOnlyEvents.push(event);\n }\n }\n };\n\n const processEnqueuedEvents = ensureSingletonExecution(async () => {\n let hasMore = true;\n while (hasMore) {\n await Promise.resolve();\n\n const batchSize = 100;\n\n const events = readPendingEvents(batchSize + 1);\n hasMore = events.length > batchSize;\n if (hasMore) {\n events.pop();\n }\n\n const previousSequentialSyncId = sequentialSyncId;\n if (events.length === 0) {\n publishProcessedBatch({\n previousSequentialSyncId,\n nextSequentialSyncId: localSyncId,\n appliedEvents: [],\n });\n break;\n }\n\n const failedRemoteSyncIds: number[] = [];\n const appliedEventsToNotify: PersistedCrdtEvent[] = [];\n\n db.executeTransaction((tx) => {\n const firstAcceptedSyncIdByTimestamp = getFirstAcceptedSyncIdsByTimestamp(\n tx,\n events.filter((event) => event.origin !== \"own-applied\"),\n );\n const statusOnlyEvents: PersistedCrdtEvent[] = [];\n\n withStateHash(() => {\n for (const event of events) {\n processPersistedEvent(tx, event, firstAcceptedSyncIdByTimestamp, statusOnlyEvents);\n if (event.status === \"applied\") {\n const firstAcceptedSyncId = firstAcceptedSyncIdByTimestamp.get(event.timestamp);\n if (firstAcceptedSyncId === undefined || event.sync_id < firstAcceptedSyncId) {\n firstAcceptedSyncIdByTimestamp.set(event.timestamp, event.sync_id);\n }\n if (storage.onEventApplied) {\n appliedEventsToNotify.push(event);\n }\n }\n if (event.status === \"failed\" && event.origin === \"remote\") {\n failedRemoteSyncIds.push(event.sync_id);\n }\n }\n });\n updateEventStatuses(tx, statusOnlyEvents);\n });\n publishProcessedBatch({\n previousSequentialSyncId,\n nextSequentialSyncId: hasMore ? (events[events.length - 1]?.sync_id ?? sequentialSyncId) : localSyncId,\n appliedEvents: appliedEventsToNotify,\n });\n\n // A remote event was accepted by the server but could not be applied\n // locally, which means our local state has diverged from the server.\n for (const syncId of failedRemoteSyncIds) {\n eventTarget.dispatchEvent(\"remote-event-apply-failed\", { syncId });\n }\n }\n });\n\n if (storage.stateHash) {\n db.executeTransaction(stateHash.initialize);\n }\n\n void processEnqueuedEvents();\n\n return {\n getEventsBatch,\n enqueueLocalEvents,\n enqueueRemoteEvents,\n applyOwnEvents,\n applyOwnSnapshot,\n checkIsQuiescent,\n getStateHash,\n\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n\n internal: {\n applyOwnEventFromTransaction,\n applyOwnIntentsFromTransaction,\n processEnqueuedEvents,\n },\n };\n}\n","import type { CrdtStorage } from \"./crdt-storage\";\n\ntype CrdtSyncProducer = {\n storage: CrdtStorage;\n broadcastEvents: (request: { newSyncId: number; stateHash: string | null }) => void;\n};\n\nexport const createCrdtSyncProducer = ({ storage, broadcastEvents }: CrdtSyncProducer) => {\n storage.addEventListener(\"events-applied\", (event) => {\n broadcastEvents({\n newSyncId: event.payload.syncId,\n stateHash: event.payload.stateHash,\n });\n });\n};\n"],"mappings":"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"}
@@ -1,4 +1,212 @@
1
- import { c as ensureSingletonExecution, h as tryCatchAsync, o as createDeferredPromise, s as createTypedEventTarget } from "./crdt-table-schema-BRm_WdcW.js";
1
+ import { c as ensureSingletonExecution, h as tryCatchAsync, o as createDeferredPromise, p as quoteId, s as createTypedEventTarget } from "./crdt-table-schema-D6Yf61Ex.js";
2
+ import initXxhash from "xxhash-wasm";
3
+ //#region src/xxh64.ts
4
+ const P1_HI = 2654435761;
5
+ const P1_LO = 2246822535;
6
+ const P2_HI = 3266489917;
7
+ const P2_LO = 668265295;
8
+ const P3_HI = 374761393;
9
+ const P3_LO = 2654435833;
10
+ const P4_HI = 2246822519;
11
+ const P4_LO = 3266489955;
12
+ const P5_HI = 668265263;
13
+ const P5_LO = 374761413;
14
+ const TWO_32 = 4294967296;
15
+ let hi = 0;
16
+ let lo = 0;
17
+ function add(aHi, aLo, bHi, bLo) {
18
+ const sum = aLo + bLo;
19
+ hi = aHi + bHi + (sum >= TWO_32 ? 1 : 0) >>> 0;
20
+ lo = sum >>> 0;
21
+ }
22
+ function sub(aHi, aLo, bHi, bLo) {
23
+ const difference = aLo - bLo;
24
+ hi = aHi - bHi - (difference < 0 ? 1 : 0) >>> 0;
25
+ lo = difference >>> 0;
26
+ }
27
+ function mul(aHi, aLo, bHi, bLo) {
28
+ const a0 = aLo & 65535;
29
+ const a1 = aLo >>> 16;
30
+ const b0 = bLo & 65535;
31
+ const b1 = bLo >>> 16;
32
+ const mid = a0 * b1 + a1 * b0;
33
+ const low = a0 * b0 + mid % 65536 * 65536;
34
+ const carry = Math.floor(low / TWO_32);
35
+ hi = a1 * b1 + Math.floor(mid / 65536) + carry + Math.imul(aHi, bLo) + Math.imul(aLo, bHi) >>> 0;
36
+ lo = low >>> 0;
37
+ }
38
+ /** `bits` must be between 1 and 31. */
39
+ function rotl(h, l, bits) {
40
+ hi = (h << bits | l >>> 32 - bits) >>> 0;
41
+ lo = (l << bits | h >>> 32 - bits) >>> 0;
42
+ }
43
+ function round(accHi, accLo, inputHi, inputLo) {
44
+ mul(inputHi, inputLo, P2_HI, P2_LO);
45
+ add(accHi, accLo, hi, lo);
46
+ rotl(hi, lo, 31);
47
+ mul(hi, lo, P1_HI, P1_LO);
48
+ }
49
+ function mergeRound(accHi, accLo, valueHi, valueLo) {
50
+ round(0, 0, valueHi, valueLo);
51
+ mul((accHi ^ hi) >>> 0, (accLo ^ lo) >>> 0, P1_HI, P1_LO);
52
+ add(hi, lo, P4_HI, P4_LO);
53
+ }
54
+ function read32(bytes, offset) {
55
+ return (bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24) >>> 0;
56
+ }
57
+ function xxh64(bytes, seed) {
58
+ const seedHi = Number(seed >> 32n & 4294967295n);
59
+ const seedLo = Number(seed & 4294967295n);
60
+ const length = bytes.length;
61
+ let offset = 0;
62
+ let hashHi;
63
+ let hashLo;
64
+ if (length >= 32) {
65
+ add(seedHi, seedLo, P1_HI, P1_LO);
66
+ add(hi, lo, P2_HI, P2_LO);
67
+ let v1Hi = hi;
68
+ let v1Lo = lo;
69
+ add(seedHi, seedLo, P2_HI, P2_LO);
70
+ let v2Hi = hi;
71
+ let v2Lo = lo;
72
+ let v3Hi = seedHi;
73
+ let v3Lo = seedLo;
74
+ sub(seedHi, seedLo, P1_HI, P1_LO);
75
+ let v4Hi = hi;
76
+ let v4Lo = lo;
77
+ const limit = length - 32;
78
+ while (offset <= limit) {
79
+ round(v1Hi, v1Lo, read32(bytes, offset + 4), read32(bytes, offset));
80
+ v1Hi = hi;
81
+ v1Lo = lo;
82
+ round(v2Hi, v2Lo, read32(bytes, offset + 12), read32(bytes, offset + 8));
83
+ v2Hi = hi;
84
+ v2Lo = lo;
85
+ round(v3Hi, v3Lo, read32(bytes, offset + 20), read32(bytes, offset + 16));
86
+ v3Hi = hi;
87
+ v3Lo = lo;
88
+ round(v4Hi, v4Lo, read32(bytes, offset + 28), read32(bytes, offset + 24));
89
+ v4Hi = hi;
90
+ v4Lo = lo;
91
+ offset += 32;
92
+ }
93
+ rotl(v1Hi, v1Lo, 1);
94
+ hashHi = hi;
95
+ hashLo = lo;
96
+ rotl(v2Hi, v2Lo, 7);
97
+ add(hashHi, hashLo, hi, lo);
98
+ hashHi = hi;
99
+ hashLo = lo;
100
+ rotl(v3Hi, v3Lo, 12);
101
+ add(hashHi, hashLo, hi, lo);
102
+ hashHi = hi;
103
+ hashLo = lo;
104
+ rotl(v4Hi, v4Lo, 18);
105
+ add(hashHi, hashLo, hi, lo);
106
+ mergeRound(hi, lo, v1Hi, v1Lo);
107
+ mergeRound(hi, lo, v2Hi, v2Lo);
108
+ mergeRound(hi, lo, v3Hi, v3Lo);
109
+ mergeRound(hi, lo, v4Hi, v4Lo);
110
+ hashHi = hi;
111
+ hashLo = lo;
112
+ } else {
113
+ add(seedHi, seedLo, P5_HI, P5_LO);
114
+ hashHi = hi;
115
+ hashLo = lo;
116
+ }
117
+ add(hashHi, hashLo, Math.floor(length / TWO_32), length >>> 0);
118
+ hashHi = hi;
119
+ hashLo = lo;
120
+ while (offset + 8 <= length) {
121
+ round(0, 0, read32(bytes, offset + 4), read32(bytes, offset));
122
+ rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 27);
123
+ mul(hi, lo, P1_HI, P1_LO);
124
+ add(hi, lo, P4_HI, P4_LO);
125
+ hashHi = hi;
126
+ hashLo = lo;
127
+ offset += 8;
128
+ }
129
+ if (offset + 4 <= length) {
130
+ mul(0, read32(bytes, offset), P1_HI, P1_LO);
131
+ rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 23);
132
+ mul(hi, lo, P2_HI, P2_LO);
133
+ add(hi, lo, P3_HI, P3_LO);
134
+ hashHi = hi;
135
+ hashLo = lo;
136
+ offset += 4;
137
+ }
138
+ while (offset < length) {
139
+ mul(0, bytes[offset], P5_HI, P5_LO);
140
+ rotl((hashHi ^ hi) >>> 0, (hashLo ^ lo) >>> 0, 11);
141
+ mul(hi, lo, P1_HI, P1_LO);
142
+ hashHi = hi;
143
+ hashLo = lo;
144
+ offset++;
145
+ }
146
+ mul(hashHi, (hashLo ^ hashHi >>> 1) >>> 0, P2_HI, P2_LO);
147
+ hashHi = hi;
148
+ hashLo = lo;
149
+ mul((hashHi ^ hashHi >>> 29) >>> 0, (hashLo ^ (hashLo >>> 29 | hashHi << 3)) >>> 0, P3_HI, P3_LO);
150
+ hashHi = hi;
151
+ hashLo = (lo ^ hi) >>> 0;
152
+ return BigInt(hashHi) << 32n | BigInt(hashLo);
153
+ }
154
+ const textEncoder = typeof TextEncoder === "undefined" ? null : new TextEncoder();
155
+ function encodeUtf8(value) {
156
+ return textEncoder ? textEncoder.encode(value) : encodeUtf8Fallback(value);
157
+ }
158
+ /** Matches TextEncoder, including U+FFFD for lone surrogates. */
159
+ function encodeUtf8Fallback(value) {
160
+ const bytes = [];
161
+ for (let index = 0; index < value.length; index++) {
162
+ let codePoint = value.charCodeAt(index);
163
+ if (codePoint >= 55296 && codePoint <= 56319) {
164
+ const next = index + 1 < value.length ? value.charCodeAt(index + 1) : 0;
165
+ if (next >= 56320 && next <= 57343) {
166
+ codePoint = 65536 + (codePoint - 55296 << 10) + (next - 56320);
167
+ index++;
168
+ } else codePoint = 65533;
169
+ } else if (codePoint >= 56320 && codePoint <= 57343) codePoint = 65533;
170
+ if (codePoint < 128) bytes.push(codePoint);
171
+ else if (codePoint < 2048) bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
172
+ else if (codePoint < 65536) bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
173
+ else bytes.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
174
+ }
175
+ return Uint8Array.from(bytes);
176
+ }
177
+ //#endregion
178
+ //#region src/hash.ts
179
+ let loadPromise = null;
180
+ let hasher = null;
181
+ const jsHasher = { h64: (input, seed) => xxh64(encodeUtf8(input), seed) };
182
+ function createWasmHasher(api) {
183
+ return { h64: (input, seed) => api.h64(input, seed) };
184
+ }
185
+ function ensureLoaded() {
186
+ if (!loadPromise) {
187
+ if (typeof WebAssembly === "undefined") {
188
+ hasher = jsHasher;
189
+ loadPromise = Promise.resolve();
190
+ } else loadPromise = initXxhash().then((api) => {
191
+ hasher = createWasmHasher(api);
192
+ }, () => {
193
+ hasher = jsHasher;
194
+ });
195
+ }
196
+ return loadPromise;
197
+ }
198
+ function getHasher() {
199
+ if (!hasher) throw new Error("xxhash is not initialized; call xxhash.ensureLoaded() first");
200
+ return hasher;
201
+ }
202
+ function h64(input, seed = 0n) {
203
+ return getHasher().h64(input, seed);
204
+ }
205
+ const xxhash = {
206
+ ensureLoaded,
207
+ h64
208
+ };
209
+ //#endregion
2
210
  //#region src/hlc.ts
3
211
  const MAX_COUNTER = 36 ** 5 - 1;
4
212
  const DEFAULT_MAX_DRIFT_MS = 216e5;
@@ -174,6 +382,128 @@ function isCrdtEventType(value) {
174
382
  return typeof value === "string" && crdtEventTypes.includes(value);
175
383
  }
176
384
  //#endregion
385
+ //#region src/sqlite-crdt/state-hash.ts
386
+ const STATE_HASH_PATTERN = /^(-?\d+):([0-9a-f]{16})$/;
387
+ function formatStateHash(schemaVersion, sum) {
388
+ return `${schemaVersion}:${sum.toString(16).padStart(16, "0")}`;
389
+ }
390
+ function parseStateHash(value) {
391
+ const match = value ? STATE_HASH_PATTERN.exec(value) : null;
392
+ if (!match) return null;
393
+ return {
394
+ schemaVersion: Number(match[1]),
395
+ sum: BigInt(`0x${match[2]}`)
396
+ };
397
+ }
398
+ /**
399
+ * The state hash is a sum of per-cell hashes over the CRDT update log, tagged with
400
+ * the schema version it was computed for. It depends only on current state, not on
401
+ * history, so replicas that converged agree on it no matter how they got there.
402
+ *
403
+ * Cells are the dataset's schema columns except `id`. A cell missing from the
404
+ * update log hashes as written by the item's latest create (the HLC stored under
405
+ * `id`). Older builds filled column defaults into creates migrated through an
406
+ * add-column migration, so replicas that replayed history hold such cells while
407
+ * replicas that applied the creates earlier never stored them.
408
+ */
409
+ function createStateHashAccumulator({ sum, getColumns }) {
410
+ let current = sum;
411
+ const cellHash = (dataset, itemId, column, hlc) => xxhash.h64(`${dataset}\u0000${itemId}\u0000${column}\u0000${hlc}`);
412
+ const columnsOf = (dataset, meta) => getColumns(dataset) ?? Object.keys(meta).filter((column) => column !== "id");
413
+ return {
414
+ get sum() {
415
+ return current;
416
+ },
417
+ addItem(dataset, itemId, meta) {
418
+ const createHlc = meta.id ?? "";
419
+ for (const column of columnsOf(dataset, meta)) current = BigInt.asUintN(64, current + cellHash(dataset, itemId, column, meta[column] ?? createHlc));
420
+ },
421
+ /**
422
+ * `meta` is the item's update log after the write. `replacedCells` holds each written
423
+ * cell's HLC from before it, and `previousCreateHlc` the item's create HLC from before.
424
+ */
425
+ updateItem(dataset, itemId, meta, previousCreateHlc, replacedCells) {
426
+ const columns = getColumns(dataset);
427
+ for (const [column, previousHlc] of replacedCells) {
428
+ if (columns ? !columns.includes(column) : column === "id") continue;
429
+ const previous = previousHlc ?? (columns ? previousCreateHlc ?? "" : void 0);
430
+ if (previous !== void 0) current = BigInt.asUintN(64, current - cellHash(dataset, itemId, column, previous));
431
+ current = BigInt.asUintN(64, current + cellHash(dataset, itemId, column, meta[column]));
432
+ }
433
+ if (!columns || meta.id === previousCreateHlc) return;
434
+ for (const column of columns) if (meta[column] === void 0) current = BigInt.asUintN(64, current - cellHash(dataset, itemId, column, previousCreateHlc ?? "") + cellHash(dataset, itemId, column, meta.id ?? ""));
435
+ }
436
+ };
437
+ }
438
+ /** Keeps the stored state hash in step with the update log. Without a store it does nothing. */
439
+ function createStateHashTracker({ db, updateLogTable, schema, getSchemaVersion, store }) {
440
+ const columnsByDataset = new Map((schema?.tablesConfig ?? []).flatMap((config) => {
441
+ const table = schema?.tables[config.crdtTableName];
442
+ return table ? [[config.baseTableName, Object.keys(table.columns).filter((column) => column !== "id")]] : [];
443
+ }));
444
+ const getColumns = (dataset) => columnsByDataset.get(dataset) ?? null;
445
+ let active = null;
446
+ const rebuild = () => {
447
+ const accumulator = createStateHashAccumulator({
448
+ sum: 0n,
449
+ getColumns
450
+ });
451
+ const batchSize = 1e3;
452
+ let after = ["", ""];
453
+ for (;;) {
454
+ const rows = db.executePreparedRaw({
455
+ key: "get-update-log-batch",
456
+ sql: `select "dataset", "item_id", "payload" from ${quoteId(updateLogTable)}
457
+ where ("item_id", "dataset") > (?, ?)
458
+ order by "item_id", "dataset"
459
+ limit ?`,
460
+ params: [...after, batchSize],
461
+ meta: { loggerLevel: "system" }
462
+ });
463
+ for (const row of rows) accumulator.addItem(row.dataset, row.item_id, JSON.parse(row.payload));
464
+ const lastRow = rows[rows.length - 1];
465
+ if (!lastRow || rows.length < batchSize) break;
466
+ after = [lastRow.item_id, lastRow.dataset];
467
+ }
468
+ return accumulator.sum;
469
+ };
470
+ const readSum = (store) => {
471
+ const stored = parseStateHash(store.read());
472
+ if (stored && stored.schemaVersion === getSchemaVersion()) return stored.sum;
473
+ const sum = rebuild();
474
+ store.write(formatStateHash(getSchemaVersion(), sum));
475
+ return sum;
476
+ };
477
+ return {
478
+ /** The accumulator of the running `track` call, for apply functions to add cells to. */
479
+ get active() {
480
+ return active;
481
+ },
482
+ read: () => store?.read() ?? null,
483
+ /** Rebuilds a missing or outdated hash. Must run inside a transaction. */
484
+ initialize: () => {
485
+ if (store) readSum(store);
486
+ },
487
+ /** Must run inside the transaction that changes the update log, so a rollback reverts the hash too. */
488
+ track: (callback) => {
489
+ if (!store || active) return callback();
490
+ const initialSum = readSum(store);
491
+ const accumulator = createStateHashAccumulator({
492
+ sum: initialSum,
493
+ getColumns
494
+ });
495
+ active = accumulator;
496
+ try {
497
+ const result = callback();
498
+ if (accumulator.sum !== initialSum) store.write(formatStateHash(getSchemaVersion(), accumulator.sum));
499
+ return result;
500
+ } finally {
501
+ active = null;
502
+ }
503
+ }
504
+ };
505
+ }
506
+ //#endregion
177
507
  //#region src/sqlite-crdt/crdt-sync-remote-source.ts
178
508
  const DISCONNECT_WATCHDOG_MS = 15e3;
179
509
  const REQUEST_TIMEOUT_MS = 1e4;
@@ -292,11 +622,11 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
292
622
  let created;
293
623
  try {
294
624
  created = factory({
295
- onEventsAvailable: ({ newSyncId, remoteEventHlcSum }) => {
625
+ onEventsAvailable: ({ newSyncId, remoteStateHash }) => {
296
626
  lastActivityAt = Date.now();
297
627
  pull({
298
628
  remoteSyncId: newSyncId,
299
- remoteEventHlcSum,
629
+ remoteStateHash,
300
630
  includeSelf: false
301
631
  });
302
632
  },
@@ -340,7 +670,7 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
340
670
  const activeSource = source;
341
671
  const remoteSyncId = request?.remoteSyncId;
342
672
  if (remoteSyncId !== void 0 && remoteSyncId <= pullSyncId.current) {
343
- checkRemoteConsistency(remoteSyncId, request?.remoteEventHlcSum ?? null);
673
+ checkRemoteConsistency(remoteSyncId, request?.remoteStateHash ?? null);
344
674
  return Promise.resolve();
345
675
  }
346
676
  if (activePull) {
@@ -497,15 +827,17 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
497
827
  if (current) await current.sync();
498
828
  else if (wantsOnline && !remoteState.schemaVersionMismatched) await goOnline();
499
829
  };
500
- const checkRemoteConsistency = (remoteSyncId, remoteEventHlcSum) => {
501
- if (remoteEventHlcSum === null) return;
830
+ const checkRemoteConsistency = (remoteSyncId, remoteStateHash) => {
831
+ if (remoteStateHash == null) return;
502
832
  if (remoteSyncId !== pullSyncId.current) return;
503
833
  if (!storage.checkIsQuiescent(pushSyncId.current)) return;
504
- const localEventHlcSum = storage.getEventHlcAccumulator();
505
- if (localEventHlcSum === null) return;
506
- if (localEventHlcSum === remoteEventHlcSum) return;
834
+ const localStateHash = storage.getStateHash();
835
+ if (localStateHash === null || localStateHash === remoteStateHash) return;
836
+ const local = parseStateHash(localStateHash);
837
+ const remote = parseStateHash(remoteStateHash);
838
+ if (!local || !remote || local.schemaVersion !== remote.schemaVersion) return;
507
839
  eventTarget.dispatchEvent("de-sync-detected", { reason: "CHECKSUM_MISMATCH" });
508
- console.warn(`[sqlite-sync] De-sync detected at syncId ${remoteSyncId}: local HLC checksum ${localEventHlcSum} != remote ${remoteEventHlcSum}. Local and remote have diverged despite being caught up.`);
840
+ console.warn(`[sqlite-sync] De-sync detected at syncId ${remoteSyncId}: local state hash ${localStateHash} != remote ${remoteStateHash}. Local and remote have diverged despite being caught up.`);
509
841
  if (!remoteState.deSynced) patchRemoteState({ deSynced: true });
510
842
  };
511
843
  const eventsAppliedSubscription = storage.addEventListener("events-applied", () => {
@@ -538,6 +870,6 @@ const createCrdtSyncRemoteSource = ({ bufferSize, storage, migrator, pullSyncId,
538
870
  };
539
871
  };
540
872
  //#endregion
541
- export { HLCCounter as a, serializeHLC as c, DEFAULT_MAX_DRIFT_MS as i, CrdtEventValidationError as n, compareHLC as o, validateNewCrdtEvent as r, deserializeHLC as s, createCrdtSyncRemoteSource as t };
873
+ export { DEFAULT_MAX_DRIFT_MS as a, deserializeHLC as c, validateNewCrdtEvent as i, serializeHLC as l, createStateHashTracker as n, HLCCounter as o, CrdtEventValidationError as r, compareHLC as s, createCrdtSyncRemoteSource as t, xxhash as u };
542
874
 
543
- //# sourceMappingURL=crdt-sync-remote-source-CPXzmSIS.js.map
875
+ //# sourceMappingURL=crdt-sync-remote-source-yeTEkOjH.js.map