@sqlite-sync/core 0.5.0 → 0.7.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.
- package/dist/{chunk-NZDRISFS.js → chunk-LBYL5L4O.js} +372 -35
- package/dist/chunk-LBYL5L4O.js.map +1 -0
- package/dist/{crdt-sync-remote-source-CJrVzW-B.d.ts → crdt-sync-remote-source-BFl7v5_I.d.ts} +135 -16
- package/dist/index.d.ts +70 -5
- package/dist/index.js +230 -74
- package/dist/index.js.map +1 -1
- package/dist/reset-state-D6UslXaj.d.ts +19 -0
- package/dist/server.d.ts +2 -2
- package/dist/worker.d.ts +10 -3
- package/dist/worker.js +12 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-NZDRISFS.js.map +0 -1
- package/dist/reset-state-Comk7IC0.d.ts +0 -81
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dummy-kysely.ts","../src/hash.ts","../src/hlc.ts","../src/introspection.ts","../src/logger.ts","../src/sqlite-db-wrapper.ts","../src/sqlite-crdt/crdt-table-schema.ts","../src/migrations/migrator.ts","../src/sqlite-crdt/stored-value.ts","../src/sqlite-kv-store.ts","../src/migrations/system-schema.ts","../src/schema/validate-crdt-event.ts","../src/schema/verify-sync-schema.ts","../src/sqlite-crdt/apply-crdt-event.ts","../src/sqlite-crdt/crdt-storage.ts","../src/sqlite-crdt/event-consistency.ts","../src/sqlite-crdt/crdt-sync-producer.ts","../src/sqlite-crdt/retry-remote-operation.ts","../src/sqlite-crdt/crdt-sync-remote-source.ts","../src/worker-db/reset-state.ts","../src/worker-db/worker-common.ts"],"sourcesContent":["import { DummyDriver, Kysely, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from \"kysely\";\n\nexport const dummyKysely: Kysely<any> = new Kysely({\n dialect: {\n createAdapter: () => new SqliteAdapter(),\n createDriver: () => new DummyDriver(),\n createQueryCompiler: () => new SqliteQueryCompiler(),\n createIntrospector: (db) => new SqliteIntrospector(db),\n },\n});\n","import initXxhash, { type XXHashAPI } from \"xxhash-wasm\";\n\nlet loadPromise: Promise<void> | null = null;\nlet api: XXHashAPI | null = null;\n\nfunction ensureLoaded(): Promise<void> {\n if (!loadPromise) {\n loadPromise = initXxhash().then((hasher) => {\n api = hasher;\n });\n }\n return loadPromise;\n}\n\nfunction h64(input: string, seed = 0n): bigint {\n if (!api) {\n throw new Error(\"xxhash is not initialized; call xxhash.ensureLoaded() first\");\n }\n return api.h64(input, seed);\n}\n\nexport const xxhash = {\n ensureLoaded,\n h64,\n};\n","export interface HLC {\n timestamp: number;\n counter: number;\n nodeId: string;\n}\n\nconst MAX_COUNTER = 36 ** 5 - 1; // 60,466,175 — max value that fits in 5-char base36\nconst DEFAULT_MAX_DRIFT_MS = 6 * 60 * 60 * 1000; // 6 hours\n\nexport class HLCCounter {\n private timestamp: number;\n private counter: number;\n private nodeId: string;\n\n private readonly getTimestamp: () => number;\n private readonly maxDrift: number;\n\n constructor(nodeId: string, getTimestamp: () => number, maxDrift: number = DEFAULT_MAX_DRIFT_MS) {\n this.timestamp = getTimestamp();\n this.counter = 0;\n this.nodeId = nodeId;\n this.getTimestamp = getTimestamp;\n this.maxDrift = maxDrift;\n }\n\n getCurrentHLC(): HLC {\n return {\n timestamp: this.timestamp,\n counter: this.counter,\n nodeId: this.nodeId,\n };\n }\n\n getNextHLC(): HLC {\n const now = this.getTimestamp();\n\n if (now > this.timestamp) {\n this.timestamp = now;\n this.counter = 0;\n return this.getCurrentHLC();\n }\n\n this.counter++;\n if (this.counter > MAX_COUNTER) {\n throw new Error(`HLC counter overflow: exceeded max value ${MAX_COUNTER}`);\n }\n return this.getCurrentHLC();\n }\n\n mergeHLC(hlc: HLC) {\n const now = this.getTimestamp();\n if (hlc.timestamp - now > this.maxDrift) {\n console.warn(\n `HLC: ignoring far-future timestamp (remote=${hlc.timestamp}, local=${now}, drift=${hlc.timestamp - now}ms)`,\n );\n return;\n }\n\n if (this.timestamp === hlc.timestamp) {\n this.counter = Math.max(this.counter, hlc.counter) + 1;\n } else if (this.timestamp > hlc.timestamp) {\n this.counter++;\n } else {\n this.timestamp = hlc.timestamp;\n this.counter = hlc.counter + 1;\n }\n if (this.counter > MAX_COUNTER) {\n throw new Error(`HLC counter overflow: exceeded max value ${MAX_COUNTER}`);\n }\n }\n}\n\nexport function serializeHLC(hlc: HLC) {\n return `${hlc.timestamp.toString().padStart(15, \"0\")}:${hlc.counter.toString(36).padStart(5, \"0\")}:${hlc.nodeId}`;\n}\n\nexport function deserializeHLC(serialized: string): HLC {\n const parts = serialized.split(\":\");\n if (parts.length < 3) {\n throw new Error(`Invalid HLC format: expected at least 3 colon-separated segments, got ${parts.length}`);\n }\n\n const timestamp = parseInt(parts[0], 10);\n const counter = parseInt(parts[1], 36);\n\n if (Number.isNaN(timestamp) || Number.isNaN(counter)) {\n throw new Error(`Invalid HLC values: timestamp=${parts[0]}, counter=${parts[1]}`);\n }\n\n return {\n timestamp,\n counter,\n nodeId: parts.slice(2).join(\":\"),\n };\n}\n\nexport function compareHLC(one: HLC, two: HLC) {\n if (one.timestamp === two.timestamp) {\n if (one.counter === two.counter) {\n if (one.nodeId === two.nodeId) {\n return 0;\n }\n return one.nodeId < two.nodeId ? -1 : 1;\n }\n return one.counter - two.counter;\n }\n return one.timestamp - two.timestamp;\n}\n","import type { Kysely, QueryCreator } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { SQLiteDbWrapper } from \"./sqlite-db-wrapper\";\n\ninterface SqliteSystemDatabase {\n // https://www.sqlite.org/schematab.html#alternative_names\n sqlite_master: SQliteMasterTable;\n}\n\n// https://www.sqlite.org/schematab.html#interpretation_of_the_schema_table\ninterface SQliteMasterTable {\n name: string;\n rootpage: number | null;\n sql: string;\n tbl_name: string;\n type: \"index\" | \"table\" | \"trigger\" | \"view\";\n}\n\n// https://www.sqlite.org/pragma.html#pragma_table_info\ninterface PragmaTableInfo {\n cid: number;\n dflt_value: unknown;\n name: string;\n notnull: 0 | 1;\n pk: number;\n type: string;\n}\n\nfunction tablesQuery(qb: QueryCreator<SqliteSystemDatabase> | Kysely<SqliteSystemDatabase>) {\n return qb\n .selectFrom(\"sqlite_master\")\n .where(\"type\", \"in\", [\"table\", \"view\"])\n .where(\"name\", \"not like\", \"sqlite_%\")\n .select([\"name\", \"sql\", \"type\"])\n .orderBy(\"name\");\n}\n\nexport type TableMetadata = {\n name: string;\n isView: boolean;\n columns: ColumnMetadata[];\n};\n\nexport type DatabaseIntrospection = Record<string, TableMetadata>;\n\nexport type ColumnMetadata = {\n name: string;\n dataType: string;\n isNullable: boolean;\n isAutoIncrementing: boolean;\n hasDefaultValue: boolean;\n defaultValueSql: string | null;\n isPrimaryKey: boolean;\n comment: undefined;\n};\n\nexport function introspectDb<BaseDatabase>(_db: SQLiteDbWrapper<BaseDatabase>): DatabaseIntrospection {\n const db = _db as unknown as SQLiteDbWrapper<SqliteSystemDatabase>;\n const tables = db.executeKysely((db) => tablesQuery(db as unknown as Kysely<SqliteSystemDatabase>), {\n loggerLevel: \"system\",\n }).rows;\n\n const tablesMetadata = db.executeKysely(\n (db) =>\n db\n .with(\"table_list\", (qb) => tablesQuery(qb as unknown as Kysely<SqliteSystemDatabase>))\n .selectFrom([\"table_list as tl\", sql<PragmaTableInfo>`pragma_table_info(tl.name)`.as(\"p\")])\n .select([\"tl.name as table\", \"p.cid\", \"p.name\", \"p.type\", \"p.notnull\", \"p.dflt_value\", \"p.pk\"])\n .orderBy(\"tl.name\")\n .orderBy(\"p.cid\"),\n { loggerLevel: \"system\" },\n ).rows;\n\n const columnsByTable: Record<string, typeof tablesMetadata> = {};\n for (const row of tablesMetadata) {\n columnsByTable[row.table] ??= [];\n columnsByTable[row.table].push(row);\n }\n\n return Object.fromEntries(\n tables.map(({ name, sql, type }) => {\n // Try to find the name of the column that has `autoincrement`\n let autoIncrementCol = sql\n ?.split(/[(),]/)\n ?.find((it) => it.toLowerCase().includes(\"autoincrement\"))\n ?.trimStart()\n ?.split(/\\s+/)?.[0]\n ?.replace(/[\"`]/g, \"\");\n\n const columns = columnsByTable[name] ?? [];\n\n // Otherwise, check for an INTEGER PRIMARY KEY\n // https://www.sqlite.org/autoinc.html\n if (!autoIncrementCol) {\n const pkCols = columns.filter((r) => r.pk > 0);\n if (pkCols.length === 1 && pkCols[0].type.toLowerCase() === \"integer\") {\n autoIncrementCol = pkCols[0].name;\n }\n }\n\n return [\n name,\n {\n name: name,\n isView: type === \"view\",\n columns: columns.map((col) => ({\n name: col.name,\n dataType: col.type,\n isNullable: !col.notnull,\n isAutoIncrementing: col.name === autoIncrementCol,\n hasDefaultValue: col.dflt_value != null,\n defaultValueSql: col.dflt_value == null ? null : String(col.dflt_value),\n isPrimaryKey: col.pk > 0,\n comment: undefined,\n })),\n },\n ];\n }),\n );\n}\n","export type LogLevel = \"info\" | \"warning\" | \"error\" | \"trace\" | \"system\";\n\nexport type Logger = (type: string, message: string, level?: LogLevel) => void;\n\nexport const startPerformanceLogger = (logger: Logger) => {\n let startTime = performance.now();\n\n return {\n restart: () => {\n startTime = performance.now();\n },\n logEnd: (type: string, message: string, level: LogLevel = \"info\") => {\n const elapsed = performance.now() - startTime;\n\n logger(type, `${elapsed.toFixed(2)}ms - ${message}`, level);\n },\n };\n};\n","import type {\n BindableValue,\n FunctionOptions,\n Database as SQLiteDatabase,\n Sqlite3Static,\n SqlValue,\n} from \"@sqlite.org/sqlite-wasm\";\nimport type { Compilable, CompiledQuery, Kysely } from \"kysely\";\nimport { dummyKysely } from \"./dummy-kysely\";\nimport { type DatabaseIntrospection, introspectDb } from \"./introspection\";\nimport { type Logger, startPerformanceLogger } from \"./logger\";\n\nexport type ExecuteParams = {\n sql: string;\n parameters: readonly unknown[];\n};\n\nexport type ExecuteResult<T> = {\n rows: T[];\n};\n\nexport type PreparedStatement<TParams extends unknown[], TResult> = {\n execute: (parameters: TParams) => TResult[];\n finalize: () => void;\n isFinalized: boolean;\n};\n\ntype ScalarFunctionOptions<TArgs extends readonly SqlValue[], TResult extends SqlValue | undefined> = {\n name: string;\n callback: (...args: TArgs) => TResult;\n} & Pick<FunctionOptions, \"deterministic\" | \"directOnly\" | \"innocuous\">;\n\ntype SqliteWrapperOptions = {\n logger?: Logger;\n loggerPrefix?: string;\n sqlite3: Sqlite3Static;\n db: () => SQLiteDatabase;\n};\n\nexport type SQLiteTransactionWrapper<TDatabase = unknown> = Pick<\n SQLiteDbWrapper<TDatabase>,\n \"sql\" | \"execute\" | \"executePrepared\" | \"executePreparedRaw\" | \"executeKysely\"\n>;\n\nexport type InternalSQLiteTransactionWrapper<TDatabase = unknown> = Pick<\n SQLiteDbWrapper<TDatabase>,\n \"executePrepared\" | \"executePreparedRaw\"\n>;\n\nexport type InternalSQLiteWrapper<TDatabase = unknown> = InternalSQLiteTransactionWrapper<TDatabase> & {\n executeTransaction: (callback: (db: InternalSQLiteTransactionWrapper<TDatabase>) => void) => void;\n};\n\ntype QueryMetaOpts = {\n loggerLevel?: \"info\" | \"system\";\n};\n\nexport class SQLiteDbWrapper<TDatabase = unknown> {\n private db: SQLiteDatabase | null = null;\n private sqlite3: Sqlite3Static;\n private logger?: Logger;\n private loggerPrefix?: string;\n\n private loadedDbSchema: DatabaseIntrospection | null = null;\n\n private readonly dataPointers = [] as number[];\n\n private preparedStatements: PreparedStatement<SqlValue[], unknown>[] = [];\n private preparedStatementsMap = new Map<string, TypedStatement<Record<string, unknown>, unknown>>();\n private preparedRawStatementsMap = new Map<string, PreparedStatement<SqlValue[], unknown>>();\n\n constructor(opts: SqliteWrapperOptions) {\n this.db = opts.db();\n this.sqlite3 = opts.sqlite3;\n this.logger = opts.logger;\n this.loggerPrefix = opts.loggerPrefix;\n }\n\n get ensureDb() {\n if (!this.db) {\n throw new Error(\"Database is already closed\");\n }\n return this.db;\n }\n\n get dbSchema() {\n if (!this.loadedDbSchema) {\n this.loadedDbSchema = introspectDb(this);\n }\n return this.loadedDbSchema;\n }\n\n execute<T = unknown>(opts: ExecuteParams | string | CompiledQuery<T>, meta?: QueryMetaOpts): ExecuteResult<T> {\n const sql = typeof opts === \"string\" ? opts : opts.sql;\n const bind = typeof opts === \"string\" ? undefined : opts.parameters;\n\n const perf = this.logger ? startPerformanceLogger(this.logger) : undefined;\n const rows = this.ensureDb.exec({\n sql,\n bind: bind as BindableValue[],\n returnValue: \"resultRows\",\n rowMode: \"object\",\n });\n perf?.logEnd(`${this.loggerPrefix ?? \"\"}:query`, sql, meta?.loggerLevel);\n\n return { rows: rows as T[] };\n }\n\n executeTransaction<T>(callback: (db: SQLiteTransactionWrapper<TDatabase>) => T): T {\n const transaction = this.beginTransaction();\n try {\n const result = callback(this);\n transaction.commit();\n return result;\n } catch (error) {\n transaction.rollback();\n throw error;\n }\n }\n\n isInTransaction() {\n return this.sqlite3.capi.sqlite3_get_autocommit(this.ensureDb) === 0;\n }\n\n beginTransaction() {\n this.executePreparedRaw({\n key: \"$begin-transaction\",\n sql: \"begin\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n\n return {\n commit: () => {\n this.executePreparedRaw({\n key: \"$commit-transaction\",\n sql: \"commit\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n },\n rollback: () => {\n this.executePreparedRaw({\n key: \"$rollback-transaction\",\n sql: \"rollback\",\n meta: {\n loggerLevel: \"system\",\n },\n });\n },\n };\n }\n\n prepare<TParams extends unknown[], TResult>(sql: string, opts?: QueryMetaOpts) {\n const perf = this.logger ? startPerformanceLogger(this.logger) : undefined;\n const stmt = this.ensureDb.prepare(sql);\n perf?.logEnd(`${this.loggerPrefix ?? \"\"}:prepare`, sql, opts?.loggerLevel);\n\n let isFinalized = false;\n\n const execute = (params: TParams) => {\n if (isFinalized) {\n throw new Error(\"Statement is finalized\");\n }\n\n const perf = this.logger ? startPerformanceLogger(this.logger) : undefined;\n try {\n if (params.length > 0) {\n stmt.bind(params as SqlValue[]);\n }\n\n const results = [] as TResult[];\n while (stmt.step()) {\n results.push(stmt.get({}) as TResult);\n }\n\n return results;\n } finally {\n stmt.reset(true);\n perf?.logEnd(`${this.loggerPrefix ?? \"\"}:prepare-execute`, sql, opts?.loggerLevel);\n }\n };\n\n const finalize = () => {\n isFinalized = true;\n stmt.finalize();\n };\n\n const preparedStatement: PreparedStatement<TParams, TResult> = {\n execute,\n finalize,\n get isFinalized() {\n return isFinalized;\n },\n };\n\n this.preparedStatements.push(preparedStatement as PreparedStatement<SqlValue[], unknown>);\n\n return preparedStatement;\n }\n\n prepareKysely<TParams extends Record<string, unknown>>(opts?: QueryMetaOpts) {\n return <TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyStatementFactory<TParams, TDatabase, TQuery, TResult>,\n ): TypedStatement<TParams, TResult> => {\n const query = factory(dummyKysely, (key) => key as any).compile();\n const statement = this.prepare<SqlValue[], TResult>(query.sql, opts);\n\n return {\n execute: (parameters) => {\n const params = query.parameters.map((param) => parameters[param as keyof TParams]);\n const result = statement.execute(params as SqlValue[]);\n return result;\n },\n };\n };\n }\n\n executeKysely<TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n meta?: QueryMetaOpts,\n ) {\n const query = factory(dummyKysely).compile();\n return this.execute(query, meta);\n }\n\n executePrepared<\n TParams extends Record<string, unknown>,\n TQuery extends Compilable<TResult>,\n TResult = QueryBuilderOutput<TQuery>,\n >(\n key: string,\n params: TParams,\n factory: KyselyStatementFactory<TParams, TDatabase, TQuery, TResult>,\n meta?: QueryMetaOpts,\n ) {\n let statement = this.preparedStatementsMap.get(key) as TypedStatement<TParams, TResult> | undefined;\n if (!statement) {\n statement = this.prepareKysely<TParams>(meta)(factory);\n this.preparedStatementsMap.set(key, statement as TypedStatement<Record<string, unknown>, unknown>);\n }\n\n return statement.execute(params);\n }\n\n executePreparedRaw<TParams extends unknown[], TResult>({\n key,\n sql,\n params,\n meta,\n }: {\n key: string;\n sql: string;\n params?: TParams;\n meta?: QueryMetaOpts;\n }) {\n let statement = this.preparedRawStatementsMap.get(key) as PreparedStatement<TParams, TResult> | undefined;\n if (!statement) {\n statement = this.prepare(sql, meta);\n this.preparedRawStatementsMap.set(key, statement as PreparedStatement<any[], unknown>);\n }\n\n return statement.execute((params ?? []) as TParams);\n }\n\n sql<T = unknown>(templateOrString: TemplateStringsArray | string, ...parameters: unknown[]) {\n if (typeof templateOrString === \"string\") {\n return this.execute<T>({\n sql: templateOrString,\n parameters,\n });\n }\n return this.execute<T>({\n sql: templateOrString.join(\"?\"),\n parameters,\n });\n }\n\n createScalarFunction<TArgs extends SqlValue[], TResult extends SqlValue | undefined>({\n name,\n callback,\n deterministic,\n directOnly,\n innocuous,\n }: ScalarFunctionOptions<TArgs, TResult>) {\n return this.ensureDb.createFunction({\n name,\n xFunc: (_, ...args) => {\n const result = callback(...(args as TArgs)) as SqlValue;\n return result;\n },\n arity: callback.length,\n deterministic,\n directOnly,\n innocuous,\n });\n }\n\n useSnapshot(snapshot: Uint8Array<ArrayBufferLike>) {\n const perf = this.logger ? startPerformanceLogger(this.logger) : undefined;\n const dataPointer = this.sqlite3.wasm.allocFromTypedArray(snapshot);\n this.dataPointers.push(dataPointer);\n\n const resultCode = this.sqlite3.capi.sqlite3_deserialize(\n this.ensureDb,\n \"main\",\n dataPointer,\n snapshot.byteLength,\n snapshot.byteLength,\n this.sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE | this.sqlite3.capi.SQLITE_DESERIALIZE_RESIZEABLE,\n );\n\n this.ensureDb.checkRc(resultCode);\n\n this.invalidateDbSchema();\n\n perf?.logEnd(\"useSnapshot\", \"success\", \"system\");\n }\n\n createSnapshot() {\n return this.sqlite3.capi.sqlite3_js_db_export(this.ensureDb);\n }\n\n invalidateDbSchema() {\n this.loadedDbSchema = null;\n }\n\n cleanup() {\n this.preparedStatements.forEach((stmt) => {\n stmt.finalize();\n });\n this.preparedStatements.splice(0);\n this.preparedStatementsMap.clear();\n this.preparedRawStatementsMap.clear();\n }\n\n close() {\n this.cleanup();\n\n this.db?.close();\n this.db = null;\n }\n}\n\nexport type QueryBuilderOutput<QB> = QB extends Compilable<infer O> ? O : never;\ntype ParamsGetter<TParams> = <TKey extends keyof TParams>(key: TKey) => TParams[TKey];\n\ntype TypedStatement<TParams extends Record<string, unknown>, TResult> = {\n execute: (parameters: TParams) => TResult[];\n};\nexport type KyselyStatementFactory<\n TParams extends Record<string, unknown>,\n TDatabase,\n TQuery extends Compilable<TResult>,\n TResult = QueryBuilderOutput<TQuery>,\n> = (kysely: Kysely<TDatabase>, params: ParamsGetter<TParams>) => TQuery;\nexport type KyselyQueryFactory<TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (\n kysely: Kysely<TDatabase>,\n) => TQuery;\n","import type { SchemaModule } from \"kysely\";\n\nexport type NewCrdtEvent = {\n type: CrdtEventType;\n dataset: string;\n item_id: string;\n payload: Record<string, unknown>;\n};\n\nexport type CrdtEventType = \"item-created\" | \"item-updated\" | \"item-deleted\";\n\nexport type CrdtEventStatus = \"pending\" | \"applied\" | \"failed\" | \"deduped\";\n\n/** Persisted on applied events that were accepted but did not mutate materialized state. */\nexport const CRDT_EVENT_NO_OP_PAYLOAD = \"no-op\";\n\nexport function isNoOpCrdtEventPayload(payload: string) {\n return payload === CRDT_EVENT_NO_OP_PAYLOAD;\n}\n\nexport type CrdtEventOrigin = \"remote\" | \"own\" | \"own-applied\" | \"local\";\n\nexport type PersistedCrdtEvent = {\n schema_version: number;\n sync_id: number;\n status: CrdtEventStatus;\n type: CrdtEventType;\n timestamp: string;\n origin: CrdtEventOrigin;\n source_node_id: string;\n dataset: string;\n item_id: string;\n payload: string;\n};\n\nexport type CrdtUpdateLogItem = {\n dataset: string;\n item_id: string;\n payload: string;\n};\n\nexport type CrdtUpdateLogPayload = Record<string, string>;\n\nexport const crdtSchema = {\n persistedEventsTable: createPersistedEventsTable,\n crdtUpdateLogTable: createCrdtUpdateLogTableQuery,\n};\n\nfunction createPersistedEventsTable(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"sync_id\", \"integer\", (col) => col.notNull().primaryKey())\n .addColumn(\"schema_version\", \"integer\", (col) => col.notNull())\n .addColumn(\"status\", \"text\", (col) => col.notNull())\n .addColumn(\"type\", \"text\", (col) => col.notNull())\n .addColumn(\"timestamp\", \"text\", (col) => col.notNull())\n .addColumn(\"origin\", \"text\", (col) => col.notNull())\n .addColumn(\"dataset\", \"text\", (col) => col.notNull())\n .addColumn(\"item_id\", \"text\", (col) => col.notNull())\n .addColumn(\"payload\", \"text\", (col) => col.notNull());\n}\n\nfunction createCrdtUpdateLogTableQuery(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"dataset\", \"text\", (col) => col.notNull())\n .addColumn(\"item_id\", \"text\", (col) => col.notNull())\n .addColumn(\"payload\", \"text\", (col) => col.notNull())\n .addPrimaryKeyConstraint(`pk_${tableName}`, [\"item_id\", \"dataset\"]);\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 TableRename = { oldTable: string; newTable: string };\n\ntype MigrationStep = {\n sql: MigrationStepSql | MigrationStepSql[];\n eventTransformer?: MigrationEventTransformers;\n tableRenames?: TableRename[];\n tableDrops?: string[];\n};\n\ntype RawMigrationStep = {\n sql: MigrationSql[];\n eventTransformer?: CompiledMigrationEventTransformer;\n tableRenames?: TableRename[];\n tableDrops?: string[];\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 tableDrops: [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 tableRenames: [{ 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 };\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 sql: (db) => db.schema.alterTable(table).addColumn(column, type, (x) => build(x).defaultTo(defaultValue)),\n eventTransformer: {\n [table]: (event) => {\n if (event.type !== \"item-created\") {\n return event;\n }\n\n event.payload[column] = defaultValue;\n\n return event;\n },\n },\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 };\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 tableRenames = steps.flatMap((s) => s.tableRenames ?? []);\n const tableDrops = steps.flatMap((s) => s.tableDrops ?? []);\n\n return [\n version,\n {\n sql: buildMigrationSql(steps),\n eventTransformer: buildMigrationEventTransformer(steps),\n ...(tableRenames.length > 0 && { tableRenames }),\n ...(tableDrops.length > 0 && { tableDrops }),\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) {\n if (migration.tableRenames) {\n for (const { oldTable, newTable } of migration.tableRenames) {\n tx.execute(`UPDATE ${updateLogTableName} SET \"dataset\" = ? WHERE \"dataset\" = ?`, [newTable, oldTable], {\n loggerLevel: \"system\",\n });\n }\n }\n if (migration.tableDrops) {\n for (const table of migration.tableDrops) {\n tx.execute(`DELETE FROM ${updateLogTableName} WHERE \"dataset\" = ?`, [table], { loggerLevel: \"system\" });\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","export type StoredValue<T> = {\n get current(): T;\n set current(newValue: T);\n};\n\nexport function createStoredValue<T>({\n initialValue,\n saveToStorage,\n}: {\n initialValue: T;\n saveToStorage?: (value: T) => void;\n}): StoredValue<T> {\n let currentValue = initialValue;\n\n return {\n get current() {\n return currentValue;\n },\n set current(newValue: T) {\n saveToStorage?.(newValue);\n currentValue = newValue;\n },\n };\n}\n","import { type SchemaModule, sql } from \"kysely\";\nimport { createStoredValue } from \"./sqlite-crdt/stored-value\";\nimport type { InternalSQLiteWrapper } from \"./sqlite-db-wrapper\";\n\nexport type KvStoreItem = {\n key: string;\n value: string;\n};\n\nexport function createKvStoreTableQuery(schema: SchemaModule, tableName: string) {\n return schema\n .createTable(tableName)\n .ifNotExists()\n .addColumn(\"key\", \"text\", (col) => col.notNull().primaryKey())\n .addColumn(\"value\", \"text\", (col) => col.notNull())\n .modifyEnd(sql`without rowid`);\n}\n\nexport function createSQLiteKvStore({ db, metaTableName }: { db: InternalSQLiteWrapper<any>; metaTableName: string }) {\n const metaDb = db as InternalSQLiteWrapper<{\n meta: KvStoreItem;\n }>;\n\n const get = (key: string): string | null => {\n const [result] = metaDb.executePrepared(\n \"get-meta-value\",\n { key },\n (db, params) =>\n db\n .selectFrom(metaTableName as \"meta\")\n .where(\"key\", \"=\", params(\"key\"))\n .select(\"value\")\n .limit((eb) => eb.lit(1)),\n { loggerLevel: \"system\" },\n );\n\n return result?.value ?? null;\n };\n\n const set = (key: string, value: string) => {\n metaDb.executePrepared(\n \"set-meta-value\",\n { key, value },\n (db, params) =>\n db\n .insertInto(metaTableName as \"meta\")\n .values({ key: params(\"key\"), value: params(\"value\") })\n .onConflict((oc) => oc.doUpdateSet({ value: params(\"value\") })),\n { loggerLevel: \"system\" },\n );\n };\n\n const remove = (key: string) => {\n metaDb.executePrepared(\n \"remove-meta-value\",\n { key },\n (db, params) => db.deleteFrom(metaTableName as \"meta\").where(\"key\", \"=\", params(\"key\")),\n { loggerLevel: \"system\" },\n );\n };\n\n const getNumberOrDefault = <T>(key: string, defaultValue: T): T | number => {\n const value = get(key);\n if (!value) return defaultValue;\n const parsedValue = Number.parseInt(value, 10);\n return Number.isNaN(parsedValue) ? defaultValue : parsedValue;\n };\n\n return {\n get,\n set,\n remove,\n createStringStoredValue: (key: string, defaultValue: string) =>\n createStoredValue<string>({\n initialValue: get(key) ?? defaultValue,\n saveToStorage: (val) => set(key, val),\n }),\n createNumberStoredValue: (key: string, defaultValue: number) =>\n createStoredValue<number>({\n initialValue: getNumberOrDefault(key, defaultValue),\n saveToStorage: (val) => set(key, val.toString()),\n }),\n };\n}\n\nexport type KvStore = ReturnType<typeof createSQLiteKvStore>;\n","import type { CrdtUpdateLogItem, PersistedCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { StoredValue } from \"../sqlite-crdt/stored-value\";\nimport type { SQLiteDbWrapper } from \"../sqlite-db-wrapper\";\nimport { createKvStoreTableQuery, createSQLiteKvStore, type KvStoreItem } from \"../sqlite-kv-store\";\nimport { type ParsedTableName, parseTableName } from \"../utils\";\n\nexport type SystemDbConfig = {\n eventsTable: ParsedTableName;\n updateLogTable: ParsedTableName;\n};\n\nexport function createSystemDbConfig({\n eventsTableName,\n updateLogTableName,\n}: {\n eventsTableName: string;\n updateLogTableName: string;\n}): SystemDbConfig {\n return {\n eventsTable: parseTableName(eventsTableName),\n updateLogTable: parseTableName(updateLogTableName),\n };\n}\n\nexport type WorkerDbSchema = {\n crdt_update_log: CrdtUpdateLogItem;\n \"worker.kv\": KvStoreItem;\n \"worker.crdt_events\": PersistedCrdtEvent;\n};\n\nexport const workerDbConfig = createSystemDbConfig({\n eventsTableName: \"worker.crdt_events\",\n updateLogTableName: \"crdt_update_log\",\n});\n\nexport type MemoryDbSchema = {\n crdt_update_log: CrdtUpdateLogItem;\n persisted_crdt_events: PersistedCrdtEvent;\n};\n\nexport const memoryDbConfig = createSystemDbConfig({\n eventsTableName: \"persisted_crdt_events\",\n updateLogTableName: \"crdt_update_log\",\n});\n\nexport type SystemMigrationContext = SystemDbConfig & {\n execute: (sql: string) => void;\n};\n\nexport type SystemMigration = {\n version: number;\n up: (ctx: SystemMigrationContext) => void;\n};\n\nexport const baseSystemMigrations: SystemMigration[] = [\n {\n version: 0,\n up: (ctx: SystemMigrationContext) => {\n ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.eventsTable.fullIdentifier} (\n \"sync_id\" integer NOT NULL PRIMARY KEY,\n \"schema_version\" integer NOT NULL,\n \"status\" text NOT NULL,\n \"type\" text NOT NULL,\n \"timestamp\" text NOT NULL,\n \"origin\" text NOT NULL,\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL\n )`);\n ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.updateLogTable.fullIdentifier} (\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL,\n PRIMARY KEY (\"item_id\", \"dataset\")\n )`);\n },\n },\n {\n version: 1,\n up: (ctx: SystemMigrationContext) => {\n ctx.execute(`ALTER TABLE ${ctx.eventsTable.fullIdentifier} ADD COLUMN \"source_node_id\" TEXT NOT NULL DEFAULT ''`);\n },\n },\n {\n version: 2,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_status_sync_id_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"status\", \"sync_id\")`,\n );\n },\n },\n {\n version: 3,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_timestamp_status_sync_id_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"timestamp\", \"status\", \"sync_id\")`,\n );\n },\n },\n];\n\nexport function runSystemMigrations(opts: {\n version: StoredValue<number>;\n migrations: SystemMigration[];\n dbConfig: SystemDbConfig;\n execute: (sql: string) => void;\n transaction: (callback: () => void) => void;\n}): void {\n const ctx: SystemMigrationContext = {\n ...opts.dbConfig,\n execute: opts.execute,\n };\n for (const migration of opts.migrations) {\n if (migration.version > opts.version.current) {\n opts.transaction(() => {\n migration.up(ctx);\n opts.version.current = migration.version;\n });\n }\n }\n}\n\nexport function applyWorkerDbSchema(db: SQLiteDbWrapper<any>) {\n // KV table stays separate — needed before system migrations for version tracking\n db.executeKysely((kysely) => createKvStoreTableQuery(kysely.schema, \"worker.kv\"), { loggerLevel: \"system\" });\n\n // System schema migrations (each in its own transaction)\n const kvStore = createSQLiteKvStore({ db, metaTableName: \"worker.kv\" });\n runSystemMigrations({\n migrations: baseSystemMigrations,\n version: kvStore.createNumberStoredValue(\"internal-schema-version\", -1),\n dbConfig: workerDbConfig,\n execute: (sql) => db.execute(sql, { loggerLevel: \"system\" }),\n transaction: (callback) => db.executeTransaction(callback),\n });\n\n return { kvStore };\n}\n\nexport function applyMemoryDbSchema(db: SQLiteDbWrapper<any>) {\n db.execute(\n `CREATE TABLE IF NOT EXISTS ${memoryDbConfig.eventsTable.fullIdentifier} (\n \"sync_id\" integer NOT NULL PRIMARY KEY,\n \"schema_version\" integer NOT NULL,\n \"status\" text NOT NULL,\n \"type\" text NOT NULL,\n \"timestamp\" text NOT NULL,\n \"origin\" text NOT NULL,\n \"source_node_id\" text NOT NULL DEFAULT '',\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL\n )`,\n { loggerLevel: \"system\" },\n );\n db.execute(\n `CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} (\"status\", \"sync_id\")`,\n {\n loggerLevel: \"system\",\n },\n );\n db.execute(\n `CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_timestamp_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} (\"timestamp\", \"status\", \"sync_id\")`,\n {\n loggerLevel: \"system\",\n },\n );\n db.execute(\n `CREATE TABLE IF NOT EXISTS ${memoryDbConfig.updateLogTable.fullIdentifier} (\n \"dataset\" text NOT NULL,\n \"item_id\" text NOT NULL,\n \"payload\" text NOT NULL,\n PRIMARY KEY (\"item_id\", \"dataset\")\n)`,\n { loggerLevel: \"system\" },\n );\n}\n","import type { SyncDbSchema } from \"../sqlite-crdt/crdt-schema\";\nimport type { CrdtEventType, NewCrdtEvent } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { AnyTableBuilder } from \"./table-builder\";\n\nexport type NewCrdtEventValidationResult =\n | { success: true; event: NewCrdtEvent }\n | { success: false; errors: string[] };\n\nexport class CrdtEventValidationError extends Error {\n constructor(readonly errors: string[]) {\n super(`Invalid CRDT events: ${errors.join(\"; \")}`);\n this.name = \"CrdtEventValidationError\";\n }\n}\n\nconst crdtEventTypes = [\"item-created\", \"item-updated\", \"item-deleted\"] as const;\n\nexport function validateNewCrdtEvent(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n input: unknown,\n): NewCrdtEventValidationResult {\n const errors: string[] = [];\n\n if (!isRecord(input)) {\n return { success: false, errors: [\"Event must be an object\"] } satisfies NewCrdtEventValidationResult;\n }\n\n const type = input.type;\n const dataset = input.dataset;\n const itemId = input.item_id;\n\n let payload = input.payload;\n let payloadParseFailed = false;\n if (typeof payload === \"string\") {\n try {\n payload = JSON.parse(payload);\n } catch {\n payloadParseFailed = true;\n errors.push(`Event payload is not valid JSON`);\n }\n }\n\n if (!isCrdtEventType(type)) {\n errors.push(`Invalid event type \"${String(type)}\"`);\n }\n if (typeof dataset !== \"string\") {\n errors.push(`Event dataset must be a string`);\n }\n if (typeof itemId !== \"string\") {\n errors.push(`Event item_id must be a string`);\n }\n if (!payloadParseFailed && !isRecord(payload)) {\n errors.push(`Event payload must be an object`);\n }\n\n const datasetMatch = typeof dataset === \"string\" ? findTableForDataset(schema, dataset) : null;\n if (typeof dataset === \"string\" && !datasetMatch) {\n errors.push(`Unknown dataset \"${dataset}\"`);\n }\n\n if (isCrdtEventType(type) && datasetMatch && isRecord(payload)) {\n switch (type) {\n case \"item-created\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-created\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n if (typeof itemId === \"string\" && \"id\" in payload && payload.id !== itemId) {\n errors.push(`payload: Column \"id\" must match item_id \"${itemId}\"`);\n }\n break;\n }\n case \"item-updated\": {\n const result = datasetMatch.table.validatePayload(payload, { event: \"item-updated\" });\n if (!result.success) {\n errors.push(...result.errors.map((error) => `payload: ${error}`));\n }\n break;\n }\n case \"item-deleted\":\n if (Object.keys(payload).length > 0) {\n errors.push(`payload: item-deleted events must have an empty payload`);\n }\n break;\n }\n }\n\n if (errors.length > 0) {\n return { success: false, errors } satisfies NewCrdtEventValidationResult;\n }\n\n return {\n success: true,\n event: {\n type: type as CrdtEventType,\n dataset: datasetMatch?.baseTableName ?? (dataset as string),\n item_id: itemId as string,\n payload: payload as Record<string, unknown>,\n },\n } satisfies NewCrdtEventValidationResult;\n}\n\nfunction findTableForDataset(\n schema: Pick<SyncDbSchema, \"tables\" | \"tablesConfig\">,\n dataset: string,\n): { table: AnyTableBuilder; baseTableName: string } | null {\n const config = schema.tablesConfig.find(\n (tableConfig) => tableConfig.baseTableName === dataset || tableConfig.crdtTableName === dataset,\n );\n if (config) {\n const table = schema.tables[config.crdtTableName];\n return table ? { table, baseTableName: config.baseTableName } : null;\n }\n const table = schema.tables[dataset];\n return table ? { table, baseTableName: dataset } : null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isCrdtEventType(value: unknown): value is CrdtEventType {\n return typeof value === \"string\" && crdtEventTypes.includes(value as CrdtEventType);\n}\n","import sqlite3InitModule, { type Sqlite3Static } from \"@sqlite.org/sqlite-wasm\";\nimport { 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 { 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\nlet sqliteModule: Sqlite3Static | null = null;\n\n/**\n * Verify that replaying the full migration history on a fresh database produces\n * exactly the declared table schema. Creates a throwaway in-memory SQLite db,\n * applies all migrations, introspects the result, and diffs it against the\n * `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 async function verifySyncSchema(schema: SyncDbSchema): Promise<SchemaVerificationIssue[]> {\n if (!sqliteModule) {\n sqliteModule = await sqlite3InitModule();\n }\n const sqlite3 = sqliteModule;\n\n const db = new SQLiteDbWrapper({\n db: () => new sqlite3.oo1.DB({ filename: \":memory:\" }),\n logger: () => {},\n loggerPrefix: \"schema-verify\",\n sqlite3,\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 { SqlValue } from \"@sqlite.org/sqlite-wasm\";\nimport type { Kysely } from \"kysely\";\nimport type { SystemDbConfig } from \"../migrations/system-schema\";\nimport type { InternalSQLiteWrapper } from \"../sqlite-db-wrapper\";\nimport { quoteId } from \"../utils\";\nimport {\n type CrdtEventType,\n type CrdtUpdateLogItem,\n type CrdtUpdateLogPayload,\n isNoOpCrdtEventPayload,\n} from \"./crdt-table-schema\";\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}: {\n db: InternalSQLiteWrapper<any>;\n dbConfig: SystemDbConfig;\n}) => {\n const applyCrdtEvent = createCrdtApplyFunction({\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 const insertPayload = {} as Record<string, unknown>;\n for (const key of Object.keys(opts.payload)) {\n insertPayload[key] = key;\n }\n db.executePrepared(\n `crdt-insert-item-${opts.dataset}`,\n opts.payload,\n (db) => db.insertInto(opts.dataset).values(insertPayload),\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 SqlValue[],\n meta: { loggerLevel: \"system\" },\n });\n },\n });\n\n return applyCrdtEvent;\n};\n\ntype CreateCrdtApplyOpts = {\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 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 };\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\n const updatePayload = {} as Record<string, unknown>;\n let hasUpdates = false;\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 meta[key] = currentUpdateTimestamp;\n hasUpdates = true;\n }\n }\n\n if (!hasUpdates) {\n return;\n }\n\n updateItem({\n dataset: event.dataset,\n itemId: event.item_id,\n payload: updatePayload,\n });\n updateCrdtUpdateLog({\n dataset: event.dataset,\n itemId: event.item_id,\n payload: JSON.stringify(meta),\n });\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 } 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 { createEventHlcAccumulator } from \"./event-consistency\";\nimport type { StoredValue } from \"./stored-value\";\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\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\">;\n\ntype DbSyncerStorage = {\n nodeId: string;\n initialLocalSyncId: number;\n migrator: SyncDbMigrator;\n db: InternalSQLiteWrapper<any>;\n dbConfig: SystemDbConfig;\n hlc: StorageHLC;\n eventHlcAccumulator?: StoredValue<string>;\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 eventHlcSum: string | null;\n};\n\ntype InternalDbSchema = {\n _crdt_events: PersistedCrdtEvent;\n _crdt_update_log: CrdtUpdateLogItem;\n};\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\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 const persistEvent = (tx: InternalSQLiteTransactionWrapper<InternalDbSchema>, event: PersistedCrdtEvent) => {\n tx.executePrepared(\n \"persist-crdt-event\",\n event,\n (db, params) =>\n db.insertInto(crdtEventsTable).values({\n type: params(\"type\"),\n dataset: params(\"dataset\"),\n item_id: params(\"item_id\"),\n payload: params(\"payload\"),\n schema_version: params(\"schema_version\"),\n sync_id: params(\"sync_id\"),\n status: params(\"status\"),\n timestamp: params(\"timestamp\"),\n origin: params(\"origin\"),\n source_node_id: params(\"source_node_id\"),\n }),\n { loggerLevel: \"system\" },\n );\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 for (const event of events) {\n persistEvent(tx, {\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 });\n\n return { beforeSyncId, afterSyncId: localSyncId, processed: processEnqueuedEvents() };\n };\n\n const enqueueLocalEvents = (events: LocalCrdtEvent[], sourceNodeId: string): EnqueueEventsResult => {\n return enqueueEvents(\"local\", sourceNodeId, events);\n };\n\n const enqueueOwnEvents = (events: OwnCrdtEvent[]): EnqueueEventsResult => {\n return enqueueEvents(\"own\", storage.nodeId, events);\n };\n\n const enqueueRemoteEvents = (events: RemoteCrdtEvent[]): EnqueueEventsResult => {\n return enqueueEvents(\"remote\", \"\", events);\n };\n\n const notifyEventApplied = (event: PersistedCrdtEvent) => {\n if (event.status === \"applied\") {\n queueMicrotask(() => {\n storage.onEventApplied?.(event);\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 for (const event of validatedEvents) {\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: JSON.stringify(event.payload),\n sync_id: ++localSyncId,\n status: \"pending\",\n };\n\n persistEvent(tx, persistedEvent);\n applyCrdtEvent(persistedEvent);\n }\n });\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 persistEvent(tx, persistedEvent);\n applyCrdtEvent(persistedEvent);\n };\n\n const dispatchEventsApplied = (previousSequentialSyncId: number) => {\n if (sequentialSyncId <= previousSequentialSyncId) {\n return;\n }\n eventTarget.dispatchEvent(\"events-applied\", {\n syncId: sequentialSyncId,\n eventHlcSum: sequentialSyncId === localSyncId ? (eventHlcAccumulator?.current ?? null) : null,\n });\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 applyCrdtEvent = createSQLiteCrdtApplyFunction({\n db,\n dbConfig: storage.dbConfig,\n });\n const eventHlcAccumulator = storage.eventHlcAccumulator\n ? createEventHlcAccumulator(storage.eventHlcAccumulator.current)\n : null;\n\n const persistEventHlcAccumulator = () => {\n if (eventHlcAccumulator && storage.eventHlcAccumulator) {\n storage.eventHlcAccumulator.current = eventHlcAccumulator.current;\n }\n };\n\n // Rebuild the accumulator from the full applied-event history when it has\n // never been computed for this storage. An empty stored value is the \"never\n // computed\" sentinel: once any event is applied the accumulator is persisted\n // as padded hex, never \"\". This recovers storages created before the\n // accumulator existed and lets us force a recompute by bumping the stored\n // value's key version. The accumulator is a commutative sum, so scan order\n // does not matter.\n const recomputeEventHlcAccumulatorIfNeeded = () => {\n if (!eventHlcAccumulator || !storage.eventHlcAccumulator) {\n return;\n }\n if (storage.eventHlcAccumulator.current !== \"\") {\n return;\n }\n\n const batchSize = 1000;\n let afterSyncId = 0;\n for (;;) {\n const rows = db.executePrepared(\n \"get-applied-event-timestamps\",\n { status: \"applied\" as const, afterSyncId, limit: batchSize },\n (db, params) =>\n db\n .selectFrom(crdtEventsTable)\n .select([\"sync_id\", \"timestamp\"])\n .where(\"status\", \"=\", params(\"status\"))\n .where(\"sync_id\", \">\", params(\"afterSyncId\"))\n .orderBy(\"sync_id\", \"asc\")\n .limit(params(\"limit\")),\n { loggerLevel: \"system\" },\n );\n\n if (rows.length === 0) {\n break;\n }\n\n for (const row of rows) {\n eventHlcAccumulator.add(row.timestamp);\n afterSyncId = row.sync_id;\n }\n\n if (rows.length < batchSize) {\n break;\n }\n }\n\n persistEventHlcAccumulator();\n };\n\n const hasAcceptedEventWithTimestamp = (\n tx: InternalSQLiteTransactionWrapper<InternalDbSchema>,\n event: PersistedCrdtEvent,\n ) => {\n const [existingEvent] = tx.executePrepared(\n \"get-accepted-crdt-event-by-timestamp\",\n {\n timestamp: event.timestamp,\n sync_id: event.sync_id,\n },\n (db, params) =>\n db\n .selectFrom(crdtEventsTable)\n .select(\"sync_id\")\n .where(\"timestamp\", \"=\", params(\"timestamp\"))\n .where(\"sync_id\", \"<\", params(\"sync_id\"))\n .where(\"status\", \"=\", sql.lit(\"applied\"))\n .limit(sql.lit(1)),\n { loggerLevel: \"system\" },\n );\n\n return existingEvent !== undefined;\n };\n\n const processPersistedEvent = (tx: InternalSQLiteTransactionWrapper<InternalDbSchema>, event: PersistedCrdtEvent) => {\n if (event.status !== \"pending\") {\n throw new Error(`Event ${event.sync_id} is not pending`);\n }\n\n try {\n if (event.origin === \"own-applied\") {\n event.status = \"applied\";\n return event;\n }\n\n if (hasAcceptedEventWithTimestamp(tx, event)) {\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 tx.executePrepared(\n \"update-crdt-event\",\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 }\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 = db.executePrepared(\n \"get-enqueued-pending-events\",\n {\n status: \"pending\" as const,\n limit: batchSize + 1,\n },\n (db, params) =>\n db\n .selectFrom(crdtEventsTable)\n .selectAll()\n .where(\"status\", \"=\", params(\"status\"))\n .limit(params(\"limit\"))\n .orderBy(\"sync_id\", \"asc\"),\n { loggerLevel: \"system\" },\n );\n hasMore = events.length > batchSize;\n if (hasMore) {\n events.pop();\n }\n\n const previousSequentialSyncId = sequentialSyncId;\n if (events.length === 0) {\n sequentialSyncId = localSyncId;\n dispatchEventsApplied(previousSequentialSyncId);\n break;\n }\n\n const failedRemoteSyncIds: number[] = [];\n\n db.executeTransaction((tx) => {\n for (const event of events) {\n processPersistedEvent(tx, event);\n if (event.status === \"applied\") {\n eventHlcAccumulator?.add(event.timestamp);\n }\n notifyEventApplied(event);\n if (event.status === \"failed\" && event.origin === \"remote\") {\n failedRemoteSyncIds.push(event.sync_id);\n }\n }\n persistEventHlcAccumulator();\n });\n\n sequentialSyncId = hasMore ? (events[events.length - 1]?.sync_id ?? sequentialSyncId) : localSyncId;\n dispatchEventsApplied(previousSequentialSyncId);\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 recomputeEventHlcAccumulatorIfNeeded();\n\n void processEnqueuedEvents();\n\n return {\n getEventsBatch,\n enqueueLocalEvents,\n enqueueOwnEvents,\n enqueueRemoteEvents,\n applyOwnEvents,\n checkIsQuiescent,\n getEventHlcAccumulator: () => eventHlcAccumulator?.current ?? null,\n\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n\n internal: {\n applyOwnEventFromTransaction,\n processEnqueuedEvents,\n },\n };\n}\n","import { xxhash } from \"../hash\";\n\nconst MASK_128 = (1n << 128n) - 1n;\nconst HEX_128_PATTERN = /^[0-9a-f]{32}$/;\n\nexport function createEventHlcAccumulator(initialValue: string) {\n let current = parseHex128(initialValue);\n\n return {\n add(timestamp: string) {\n current = (current + hash128BigInt(timestamp)) & MASK_128;\n },\n get current() {\n return toHex128(current);\n },\n };\n}\n\nfunction parseHex128(value: string) {\n if (value === \"\") {\n return 0n;\n }\n const normalized = value.toLowerCase();\n if (!HEX_128_PATTERN.test(normalized)) {\n throw new Error(`Invalid event HLC accumulator value: ${value}`);\n }\n return BigInt(`0x${normalized}`);\n}\n\nfunction toHex128(value: bigint) {\n return value.toString(16).padStart(32, \"0\");\n}\n\nfunction hash128BigInt(value: string) {\n return xxhash.h64(value, 0n) | (xxhash.h64(value, 1n) << 64n);\n}\n","import type { CrdtStorage } from \"./crdt-storage\";\n\ntype CrdtSyncProducer = {\n storage: CrdtStorage;\n broadcastEvents: (request: { newSyncId: number; eventHlcSum: 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 eventHlcSum: event.payload.eventHlcSum,\n });\n });\n};\n","type RetryOptions = {\n maxAttempts: number;\n backoffBaseMs: number;\n backoffExponent: number;\n backoffJitterMs: number;\n timeoutMs: number;\n};\n\nexport const REMOTE_RETRY_OPTIONS: RetryOptions = {\n maxAttempts: 3,\n backoffBaseMs: 100,\n backoffExponent: 1.5,\n backoffJitterMs: 150,\n timeoutMs: 10000,\n};\n\nclass RetryTimeoutError extends Error {\n constructor(\n message: string,\n public previous?: unknown,\n ) {\n super(message);\n this.name = \"TimeoutError\";\n }\n}\n\nconst applyJitter = (delayMs: number, maxJitterMs: number): number => {\n const jitter = Math.random() * maxJitterMs * (Math.random() > 0.5 ? 1 : -1);\n return Math.max(0, delayMs + jitter);\n};\n\nconst delay = (delayMs: number) => new Promise((resolve) => setTimeout(resolve, delayMs));\n\nconst withTimeout = async <T>(operation: () => Promise<T>, timeoutMs: number, previousError?: unknown): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n return await Promise.race([\n operation(),\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new RetryTimeoutError(\"Remote operation timed out\", previousError)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n};\n\nexport const retryRemoteOperation = async <T>(operation: () => Promise<T>, options: RetryOptions): Promise<T> => {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {\n try {\n return await withTimeout(operation, options.timeoutMs, lastError);\n } catch (error) {\n lastError = error;\n\n if (attempt >= options.maxAttempts) {\n throw error;\n }\n\n const backoffDelay = applyJitter(\n options.backoffBaseMs * options.backoffExponent ** (attempt - 1),\n options.backoffJitterMs,\n );\n if (backoffDelay > 0) {\n await delay(backoffDelay);\n }\n }\n }\n\n throw lastError;\n};\n","import type { SyncDbMigrator } from \"../migrations/migrator\";\nimport { createTypedEventTarget, ensureSingletonExecution, tryCatchAsync } from \"../utils\";\nimport type { EventsPullResponse, WorkerState } from \"../worker-db/worker-common\";\nimport type { PendingCrdtEvent } from \"./apply-crdt-event\";\nimport type { CrdtStorage } from \"./crdt-storage\";\nimport { REMOTE_RETRY_OPTIONS, retryRemoteOperation } from \"./retry-remote-operation\";\nimport type { StoredValue } from \"./stored-value\";\n\ntype CrdtSyncRemoteSourceConfig = {\n bufferSize: number;\n storage: CrdtStorage;\n migrator: SyncDbMigrator;\n pullSyncId: StoredValue<number>;\n pushSyncId: StoredValue<number>;\n nodeId: string;\n remoteFactory?: CreateRemoteSourceFactory;\n};\n\nexport type EventsPullRequest = {\n afterSyncId: number;\n excludeNodeId?: string;\n};\n\nexport type EventsPushRequest = {\n nodeId: string;\n events: (PendingCrdtEvent & { schema_version: number })[];\n};\nexport type EventsPushResponse = {\n ok: boolean;\n /** Remote sync_id right before the pushed events were enqueued. */\n beforeSyncId?: number;\n /** Remote sync_id right after the pushed events were enqueued. */\n afterSyncId?: number;\n};\n\nexport type CrdtSyncRemoteSource = ReturnType<typeof createCrdtSyncRemoteSource>;\n\nexport type EventsAvailable = {\n newSyncId: number;\n remoteEventHlcSum: string | null;\n};\n\nexport type CreateRemoteSourceFactory = (opts: {\n onEventsAvailable: (event: EventsAvailable) => void;\n}) => RemoteSource | Promise<RemoteSource>;\n\ntype RemoteSource = {\n pullEvents: (request: EventsPullRequest) => Promise<EventsPullResponse>;\n pushEvents: (request: EventsPushRequest) => Promise<EventsPushResponse>;\n disconnect?: () => void | Promise<void>;\n};\n\nexport class SchemaVersionMismatchError extends Error {\n constructor(\n public remoteSchemaVersion: number,\n public localSchemaVersion: number,\n ) {\n super(`Schema version mismatch: remote ${remoteSchemaVersion} != local ${localSchemaVersion}`);\n this.name = \"SchemaVersionMismatchError\";\n }\n}\n\ntype RemoteSourceState =\n | {\n type: \"pending\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"offline\";\n reason: OfflineReason;\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n }\n | {\n type: \"online\";\n source: RemoteSource;\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n };\n\nexport type OfflineReason =\n | \"NOT_INITIALIZED\"\n | \"INITIALIZATION_FAILED\"\n | \"REMOTE_PUSH_ERROR\"\n | \"REMOTE_PULL_ERROR\"\n | \"DISCONNECTED\";\n\nexport type DeSyncDetectedReason = \"CHECKSUM_MISMATCH\" | \"ERROR_APPLYING_REMOTE_EVENT\";\n\nexport const createCrdtSyncRemoteSource = ({\n bufferSize,\n storage,\n migrator,\n pullSyncId,\n pushSyncId,\n nodeId,\n remoteFactory,\n}: CrdtSyncRemoteSourceConfig) => {\n const eventTarget = createTypedEventTarget<{\n \"state-changed\": RemoteSourceState[\"type\"];\n \"de-sync-detected\": {\n reason: DeSyncDetectedReason;\n };\n \"remote-schema-version-mismatch\": {\n remoteSchemaVersion: number;\n localSchemaVersion: number;\n };\n }>();\n\n let remoteState: RemoteSourceState = {\n type: \"offline\",\n reason: \"NOT_INITIALIZED\",\n deSynced: false,\n schemaVersionMismatched: false,\n };\n\n const patchRemoteState = (state: Partial<RemoteSourceState>) => {\n remoteState = { ...remoteState, ...state } as RemoteSourceState;\n eventTarget.dispatchEvent(\"state-changed\", remoteState.type);\n };\n\n const initRemote = ensureSingletonExecution(\n async () => {\n if (remoteState.type !== \"offline\") {\n throw new Error(\"Remote source is not offline\");\n }\n\n if (!remoteFactory) {\n console.warn(\"Remote source factory not provided. Going offline.\");\n patchRemoteState({ type: \"offline\", reason: \"NOT_INITIALIZED\" });\n return;\n }\n\n patchRemoteState({ type: \"pending\" });\n\n const factoryResult = await tryCatchAsync(async () => {\n return await remoteFactory?.({\n onEventsAvailable: ({ newSyncId, remoteEventHlcSum }) => {\n pullEvents({ remoteSyncId: newSyncId, remoteEventHlcSum, includeSelf: false });\n },\n });\n });\n\n if (!factoryResult.success) {\n patchRemoteState({ type: \"offline\", reason: \"INITIALIZATION_FAILED\" });\n console.warn(\"Failed to create remote source\", factoryResult.error);\n return;\n }\n\n patchRemoteState({\n type: \"online\",\n source: factoryResult.data,\n deSynced: false,\n schemaVersionMismatched: false,\n });\n },\n { queueReExecution: false },\n );\n\n const syncWithRemote = ensureSingletonExecution(\n async () => {\n if (remoteState.type !== \"online\") {\n return;\n }\n\n await pullEvents();\n await startPushingEvents();\n },\n { queueReExecution: false },\n );\n\n const goOffline = ensureSingletonExecution(\n async (reason: OfflineReason) => {\n if (remoteState.type !== \"online\") {\n return;\n }\n const source = remoteState.source;\n\n patchRemoteState({ type: \"pending\" });\n\n const disconnectResult = await tryCatchAsync(async () => {\n return await source.disconnect?.();\n });\n\n if (!disconnectResult.success) {\n console.warn(\"Error while disconnecting from remote source\", disconnectResult.error);\n }\n\n patchRemoteState({ type: \"offline\", reason });\n },\n { queueReExecution: false },\n );\n\n const goOnline = async () => {\n if (remoteState.type !== \"online\") {\n await initRemote();\n }\n\n if (remoteState.type === \"online\") {\n await syncWithRemote();\n }\n };\n\n let requestedPullSyncId: number | null = null;\n let pullPromise: Promise<void> | null = null;\n const pullEvents = (request?: {\n remoteSyncId?: number;\n remoteEventHlcSum?: string | null;\n includeSelf?: boolean;\n }) => {\n if (remoteState.type !== \"online\") {\n return Promise.resolve();\n }\n\n const remoteSyncId = request?.remoteSyncId;\n\n if (remoteSyncId !== undefined && remoteSyncId <= pullSyncId.current) {\n // We are already caught up to this broadcast, so there is nothing to pull.\n // This is the quiescent moment to verify we have not diverged from the\n // remote (the check is a no-op unless we are exactly aligned: remoteSyncId\n // === pullSyncId.current).\n checkRemoteConsistency(remoteSyncId, request?.remoteEventHlcSum ?? null);\n return Promise.resolve();\n }\n\n if (pullPromise) {\n if (remoteSyncId !== undefined && (!requestedPullSyncId || requestedPullSyncId < remoteSyncId)) {\n requestedPullSyncId = remoteSyncId;\n }\n return pullPromise;\n }\n\n pullPromise = pullAllEvents({\n afterSyncId: pullSyncId.current,\n excludeNodeId: request?.includeSelf ? undefined : nodeId,\n })\n .catch((error) => {\n console.error(\"Error pulling events. Going offline.\", error);\n goOffline(\"REMOTE_PULL_ERROR\");\n })\n .finally(() => {\n pullPromise = null;\n\n const nextTarget = requestedPullSyncId;\n requestedPullSyncId = null;\n\n if (nextTarget && nextTarget > pullSyncId.current) {\n pullEvents({ remoteSyncId: nextTarget });\n }\n });\n return pullPromise;\n };\n\n const pullAllEvents = async (opts: EventsPullRequest) => {\n let hasMore = true;\n let afterSyncId = opts.afterSyncId;\n while (hasMore) {\n if (remoteState.type !== \"online\") {\n return;\n }\n const source = remoteState.source;\n\n const response = await retryRemoteOperation(\n () =>\n source.pullEvents({\n ...opts,\n afterSyncId,\n }),\n REMOTE_RETRY_OPTIONS,\n );\n hasMore = response.hasMore;\n afterSyncId = response.nextSyncId;\n\n if (response.events) {\n storage.enqueueRemoteEvents(\n response.events.map((x) => {\n if (x.schema_version > migrator.currentSchemaVersion) {\n eventTarget.dispatchEvent(\"remote-schema-version-mismatch\", {\n remoteSchemaVersion: x.schema_version,\n localSchemaVersion: migrator.currentSchemaVersion,\n });\n if (remoteState.type === \"online\" && !remoteState.schemaVersionMismatched) {\n patchRemoteState({ schemaVersionMismatched: true });\n }\n throw new SchemaVersionMismatchError(x.schema_version, migrator.currentSchemaVersion);\n }\n return x;\n }),\n );\n }\n if (response.nextSyncId <= pullSyncId.current) {\n break;\n }\n if (response.nextSyncId > pullSyncId.current) {\n pullSyncId.current = response.nextSyncId;\n }\n }\n };\n\n // De-sync detection: when we are exactly caught up to the remote's broadcast\n // sync id and fully quiescent, our applied-event set must equal the remote's,\n // so our HLC checksums must match. A mismatch means the nodes have diverged.\n const checkRemoteConsistency = (remoteSyncId: number, remoteEventHlcSum: string | null) => {\n // A remote with no accumulator gives us nothing to compare against.\n if (remoteEventHlcSum === null) {\n return;\n }\n\n // Only meaningful when we are exactly caught up: if we are behind we still\n // need to pull; if we are ahead our state covers events the remote checksum\n // does not.\n if (remoteSyncId !== pullSyncId.current) {\n return;\n }\n\n // Quiescence: the accumulator only matches the remote's when nothing is left\n // to apply locally and no local applied events are still waiting to be pushed\n // (those are in our accumulator but the remote has not seen them yet).\n if (!storage.checkIsQuiescent(pushSyncId.current)) {\n return;\n }\n\n const localEventHlcSum = storage.getEventHlcAccumulator();\n if (localEventHlcSum === null) {\n return;\n }\n\n if (localEventHlcSum === remoteEventHlcSum) {\n // No de-sync detected.\n return;\n }\n\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"CHECKSUM_MISMATCH\" });\n console.warn(\n `[sqlite-sync] De-sync detected at syncId ${remoteSyncId}: local HLC checksum ${localEventHlcSum} != remote ${remoteEventHlcSum}. Local and remote have diverged despite being caught up.`,\n );\n if (remoteState.type === \"online\" && !remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n };\n\n const startPushingEvents = ensureSingletonExecution(async () => {\n while (true) {\n const eventsBatch = storage.getEventsBatch({\n status: \"applied\",\n afterSyncId: pushSyncId.current,\n excludeOrigin: \"remote\",\n limit: bufferSize,\n });\n if (eventsBatch.events.length === 0) {\n break;\n }\n\n if (remoteState.type !== \"online\") {\n break;\n }\n const source = remoteState.source;\n\n let response: EventsPushResponse;\n try {\n response = await retryRemoteOperation(\n () =>\n source.pushEvents({\n nodeId,\n events: eventsBatch.events.map((event) => ({\n schema_version: event.schema_version,\n timestamp: event.timestamp,\n type: event.type,\n dataset: event.dataset,\n item_id: event.item_id,\n payload: event.payload,\n })),\n }),\n REMOTE_RETRY_OPTIONS,\n );\n } catch (error) {\n console.error(\"Error pushing events. Going offline.\", error);\n goOffline(\"REMOTE_PUSH_ERROR\");\n return;\n }\n\n pushSyncId.current = eventsBatch.nextSyncId;\n\n // Fast-forward the pull cursor: the remote assigns sync ids for the pushed\n // events synchronously, so (beforeSyncId, afterSyncId] contains only this\n // node's own events. If we are caught up to at least beforeSyncId, the skipped\n // range (pullSyncId, afterSyncId] contains only our own events, so there is\n // nothing to pull up to afterSyncId.\n if (\n response.ok &&\n response.beforeSyncId !== undefined &&\n response.afterSyncId !== undefined &&\n response.beforeSyncId <= pullSyncId.current &&\n response.afterSyncId > pullSyncId.current\n ) {\n pullSyncId.current = response.afterSyncId;\n }\n if (!eventsBatch.hasMore) {\n break;\n }\n }\n });\n\n const eventsAppliedSubscription = storage.addEventListener(\"events-applied\", () => {\n startPushingEvents();\n });\n\n const remoteEventApplyFailedSubscription = storage.addEventListener(\"remote-event-apply-failed\", () => {\n eventTarget.dispatchEvent(\"de-sync-detected\", { reason: \"ERROR_APPLYING_REMOTE_EVENT\" });\n if (remoteState.type === \"online\" && !remoteState.deSynced) {\n patchRemoteState({ deSynced: true });\n }\n });\n\n const getState = (): WorkerState => ({\n remoteState: remoteState.type,\n deSynced: remoteState.deSynced,\n schemaVersionMismatched: remoteState.schemaVersionMismatched,\n });\n\n const dispose = async () => {\n await goOffline(\"DISCONNECTED\");\n eventsAppliedSubscription.unsubscribe();\n remoteEventApplyFailedSubscription.unsubscribe();\n };\n\n return {\n goOnline,\n goOffline,\n syncWithRemote,\n getState,\n dispose,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n};\n","import { createStore, del, get, set } from \"idb-keyval\";\nimport { generateId } from \"../utils\";\nimport type { WorkerNotificationMessage } from \"./worker-common\";\n\nexport type ResetRequest = {\n epoch: string;\n requestedAt: number;\n};\n\n/** Durable async key-value storage for reset state. Injectable for tests. */\nexport type ResetStore = {\n get: <T>(key: string) => Promise<T | undefined>;\n set: (key: string, value: unknown) => Promise<void>;\n delete: (key: string) => Promise<void>;\n};\n\n/** Default IndexedDB-backed reset store (idb-keyval over a dedicated database). */\nexport function createIdbResetStore(): ResetStore {\n const store = createStore(\"sqlite-sync\", \"kv\");\n return {\n get: (key) => get(key, store),\n set: (key, value) => set(key, value, store),\n delete: (key) => del(key, store),\n };\n}\n\n/**\n * A clean reset is a recovery action for a de-sync detected now. If the reload\n * never happens (broadcast lost, tab crashed mid-reload, browser killed the page),\n * the request must not fire on an arbitrary later cold start and silently wipe\n * local-only writes accumulated since.\n */\nexport const RESET_REQUEST_TTL_MS = 10 * 60 * 1000; // 10 minutes\n\nconst resetRequestKey = (dbId: string) => `sqlite-sync-reset-request-${dbId}`;\nconst resetAppliedKey = (dbId: string) => `sqlite-sync-reset-applied-${dbId}`;\n\ntype ResetStateStoreOptions = {\n store: ResetStore;\n dbId: string;\n now?: () => number;\n};\n\nexport type ResetStateStore = ReturnType<typeof createResetStateStore>;\n\n/**\n * Worker-owned durable reset state. The reset decision must be owned by the\n * elected worker, not by tabs during `createSyncedDb` — otherwise a later\n * worker election could repeat an already-applied wipe.\n */\nexport function createResetStateStore({ store, dbId, now = () => Date.now() }: ResetStateStoreOptions) {\n const requestKey = resetRequestKey(dbId);\n const appliedKey = resetAppliedKey(dbId);\n\n return {\n async writeResetRequest(epoch: string): Promise<ResetRequest> {\n const request: ResetRequest = { epoch, requestedAt: now() };\n await store.set(requestKey, request);\n return request;\n },\n /**\n * Read the pending reset request after winning the worker election.\n * Returns the request only when it has not been applied yet and is within\n * the TTL. Stale requests are deleted so they cannot fire on a later cold start.\n */\n async resolvePendingReset(): Promise<ResetRequest | undefined> {\n const request = await store.get<ResetRequest>(requestKey);\n if (!request) {\n return undefined;\n }\n\n if (now() - request.requestedAt > RESET_REQUEST_TTL_MS) {\n await store.delete(requestKey);\n return undefined;\n }\n\n const appliedEpoch = await store.get<string>(appliedKey);\n if (request.epoch === appliedEpoch) {\n return undefined;\n }\n\n return request;\n },\n /**\n * Record the epoch as applied. Must be called only after the worker has\n * successfully initialized with `clearOnInit: true`, so a failed init can\n * be retried by a later elected worker.\n */\n async markResetApplied(epoch: string): Promise<void> {\n await store.set(appliedKey, epoch);\n },\n };\n}\n\ntype ReloadRequestHandlerOptions = {\n resetState: ResetStateStore;\n broadcast: (message: WorkerNotificationMessage) => void;\n generateEpoch?: () => string;\n};\n\n/**\n * Worker-side `requestReload` RPC implementation. For `clean: true` the reset\n * request is durably stored before broadcasting and before the RPC resolves,\n * so the epoch survives no matter which path triggers the reload.\n */\nexport function createReloadRequestHandler({\n resetState,\n broadcast,\n generateEpoch = generateId,\n}: ReloadRequestHandlerOptions) {\n return async (options: { clean: boolean }): Promise<void> => {\n const reloadEpoch = generateEpoch();\n\n if (options.clean) {\n await resetState.writeResetRequest(reloadEpoch);\n }\n\n broadcast({\n notificationType: \"reload-requested\",\n reloadEpoch,\n clean: options.clean,\n });\n };\n}\n","import type {\n DeSyncDetectedReason,\n EventsPullRequest,\n EventsPushRequest,\n EventsPushResponse,\n} from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { CrdtEventType } from \"../sqlite-crdt/crdt-table-schema\";\nimport type { ExecuteParams, ExecuteResult } from \"../sqlite-db-wrapper\";\nimport { TypedBroadcastChannel } from \"../utils\";\n\nexport const syncDbWorkerLockName = \"sync-db-worker-lock\";\nexport const syncDbClientLockName = \"sync-db-client-lock\";\n\nexport type WorkerNotificationMessage =\n | {\n notificationType: \"new-event-chunk-applied\";\n newSyncId: number;\n /** Worker's HLC checksum after applying up to `newSyncId`. */\n eventHlcSum: string | null;\n }\n | {\n notificationType: \"state-changed\";\n state: WorkerState;\n }\n | {\n notificationType: \"reload-requested\";\n reloadEpoch: string;\n clean: boolean;\n }\n | {\n notificationType: \"de-sync-detected\";\n reason: DeSyncDetectedReason;\n }\n | {\n notificationType: \"remote-schema-version-mismatch\";\n remoteSchemaVersion: number;\n localSchemaVersion: number;\n };\n\nexport type WorkerState = {\n remoteState: \"online\" | \"offline\" | \"pending\";\n deSynced: boolean;\n schemaVersionMismatched: boolean;\n};\n\nexport type GetSnapshotResponse = {\n file: Uint8Array<ArrayBufferLike>;\n syncId: number;\n schemaVersion: number;\n};\n\nexport type EventsPullResponse = {\n events: {\n schema_version: number;\n type: CrdtEventType;\n timestamp: string;\n dataset: string;\n item_id: string;\n payload: string;\n }[];\n hasMore: boolean;\n nextSyncId: number;\n};\n\nexport interface WorkerRpc {\n getSnapshot: () => GetSnapshotResponse;\n pushTabEvents: (request: EventsPushRequest) => EventsPushResponse;\n execute: (query: ExecuteParams) => ExecuteResult<unknown>;\n pullEvents: (params: EventsPullRequest) => EventsPullResponse;\n postState: () => void;\n goOnline: () => Promise<void>;\n goOffline: () => void;\n requestReload: (options: { clean: boolean }) => Promise<void>;\n}\n\nexport type WorkerRequestMethod = keyof WorkerRpc;\n\nexport type WorkerRequestMessage<TMethod extends WorkerRequestMethod = WorkerRequestMethod> = {\n type: \"request\";\n requestId: string;\n method: TMethod;\n args: Parameters<WorkerRpc[TMethod]>;\n};\n\nexport type WorkerResponseMessage<TMethod extends WorkerRequestMethod = WorkerRequestMethod> = {\n type: \"response\";\n requestId: string;\n data: ReturnType<WorkerRpc[TMethod]>;\n};\n\nexport type WorkerErrorResponseMessage = {\n type: \"error-response\";\n requestId: string;\n error: string;\n};\n\nexport type AsyncRpc<T> = {\n [K in keyof T]: T[K] extends (...args: infer U) => infer V ? (...args: U) => Promise<Awaited<V>> : never;\n};\n\nexport const broadcastChannelNames = {\n requests: \"sync-db-worker-requests\",\n responses: \"sync-db-worker-responses\",\n} as const;\n\nexport type WorkerBroadcastChannels = {\n requests: TypedBroadcastChannel<WorkerRequestMessage>;\n responses: TypedBroadcastChannel<WorkerResponseMessage | WorkerErrorResponseMessage | WorkerNotificationMessage>;\n};\n\nexport const createBroadcastChannels = (prefix: string): WorkerBroadcastChannels => {\n return {\n requests: new TypedBroadcastChannel(`${prefix}-${broadcastChannelNames.requests}`),\n responses: new TypedBroadcastChannel(`${prefix}-${broadcastChannelNames.responses}`),\n };\n};\n\nexport type WorkerConfig<Props = any> = {\n dbId: string;\n clientId: string;\n props: Props;\n};\n\nexport type WorkerInitMessage = {\n type: \"init\";\n config: WorkerConfig;\n};\n\nexport function isWorkerInitMessage(message: unknown): message is WorkerInitMessage {\n return typeof message === \"object\" && message !== null && \"type\" in message && message.type === \"init\";\n}\n\nexport function isWorkerRequestMessage(message: unknown): message is WorkerRequestMessage {\n return typeof message === \"object\" && message !== null && \"type\" in message && message.type === \"request\";\n}\n\nexport function isWorkerResponseMessage(message: unknown): message is WorkerResponseMessage {\n return (\n typeof message === \"object\" && message !== null && \"type\" in message && \"requestId\" in message && \"data\" in message\n );\n}\n\nexport function isWorkerErrorResponseMessage(message: unknown): message is WorkerErrorResponseMessage {\n return typeof message === \"object\" && message !== null && \"type\" in message && message.type === \"error-response\";\n}\n\nexport function isWorkerNotificationMessage(message: unknown): message is WorkerNotificationMessage {\n return typeof message === \"object\" && message !== null && \"notificationType\" in message && !!message.notificationType;\n}\n"],"mappings":"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","names":["db","sql","sql","perf","sql","sql","sql","get","db","set","sql","table","sql","db","sql","db","sql"]}
|
package/dist/{crdt-sync-remote-source-CJrVzW-B.d.ts → crdt-sync-remote-source-BFl7v5_I.d.ts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompiledQuery, Compilable, Kysely, CreateTableBuilder, CreateIndexBuilder, ColumnDataType, Expression, ColumnDefinitionBuilderCallback } from 'kysely';
|
|
1
|
+
import { CompiledQuery, Compilable, Kysely, CreateTableBuilder, CreateIndexBuilder, ColumnDataType, Expression, ColumnDefinitionBuilderCallback, ColumnType } from 'kysely';
|
|
2
2
|
import { Sqlite3Static, Database, SqlValue, FunctionOptions } from '@sqlite.org/sqlite-wasm';
|
|
3
3
|
|
|
4
4
|
interface HLC {
|
|
@@ -116,6 +116,8 @@ type ColumnMetadata = {
|
|
|
116
116
|
isNullable: boolean;
|
|
117
117
|
isAutoIncrementing: boolean;
|
|
118
118
|
hasDefaultValue: boolean;
|
|
119
|
+
defaultValueSql: string | null;
|
|
120
|
+
isPrimaryKey: boolean;
|
|
119
121
|
comment: undefined;
|
|
120
122
|
};
|
|
121
123
|
declare function introspectDb<BaseDatabase>(_db: SQLiteDbWrapper<BaseDatabase>): DatabaseIntrospection;
|
|
@@ -169,12 +171,18 @@ type ParsedTableName = {
|
|
|
169
171
|
fullIdentifier: string;
|
|
170
172
|
};
|
|
171
173
|
|
|
174
|
+
type NewCrdtEvent = {
|
|
175
|
+
type: CrdtEventType;
|
|
176
|
+
dataset: string;
|
|
177
|
+
item_id: string;
|
|
178
|
+
payload: Record<string, unknown>;
|
|
179
|
+
};
|
|
172
180
|
type CrdtEventType = "item-created" | "item-updated" | "item-deleted";
|
|
173
181
|
type CrdtEventStatus = "pending" | "applied" | "failed" | "deduped";
|
|
174
182
|
/** Persisted on applied events that were accepted but did not mutate materialized state. */
|
|
175
183
|
declare const CRDT_EVENT_NO_OP_PAYLOAD = "no-op";
|
|
176
184
|
declare function isNoOpCrdtEventPayload(payload: string): payload is "no-op";
|
|
177
|
-
type CrdtEventOrigin = "remote" | "own" | "local";
|
|
185
|
+
type CrdtEventOrigin = "remote" | "own" | "own-applied" | "local";
|
|
178
186
|
type PersistedCrdtEvent = {
|
|
179
187
|
schema_version: number;
|
|
180
188
|
sync_id: number;
|
|
@@ -203,12 +211,6 @@ declare function createStoredValue<T>({ initialValue, saveToStorage, }: {
|
|
|
203
211
|
saveToStorage?: (value: T) => void;
|
|
204
212
|
}): StoredValue<T>;
|
|
205
213
|
|
|
206
|
-
type CrdtEvent = {
|
|
207
|
-
type: CrdtEventType;
|
|
208
|
-
dataset: string;
|
|
209
|
-
item_id: string;
|
|
210
|
-
payload: Record<string, unknown>;
|
|
211
|
-
};
|
|
212
214
|
type MigratableEvent = {
|
|
213
215
|
schema_version: number;
|
|
214
216
|
type: CrdtEventType;
|
|
@@ -216,7 +218,7 @@ type MigratableEvent = {
|
|
|
216
218
|
item_id: string;
|
|
217
219
|
payload: string;
|
|
218
220
|
};
|
|
219
|
-
type CrdtEventTransformer = (event:
|
|
221
|
+
type CrdtEventTransformer = (event: NewCrdtEvent) => NewCrdtEvent | null;
|
|
220
222
|
type MigrationStepSql = {
|
|
221
223
|
sql: string;
|
|
222
224
|
parameters?: readonly unknown[];
|
|
@@ -269,7 +271,7 @@ declare const migrationSteps: {
|
|
|
269
271
|
}) => MigrationStep;
|
|
270
272
|
};
|
|
271
273
|
type MigrationEventTransformers = Record<string, CrdtEventTransformer>;
|
|
272
|
-
type CompiledMigrationEventTransformer = (event:
|
|
274
|
+
type CompiledMigrationEventTransformer = (event: NewCrdtEvent) => NewCrdtEvent | null;
|
|
273
275
|
declare function createMigrations(buildMigrations: (builder: typeof migrationSteps) => Record<number, MigrationStep[]>): Record<number, RawMigrationStep>;
|
|
274
276
|
type Migrations = ReturnType<typeof createMigrations>;
|
|
275
277
|
type MigrationsDb = {
|
|
@@ -327,6 +329,113 @@ declare function applyWorkerDbSchema(db: SQLiteDbWrapper<any>): {
|
|
|
327
329
|
};
|
|
328
330
|
declare function applyMemoryDbSchema(db: SQLiteDbWrapper<any>): void;
|
|
329
331
|
|
|
332
|
+
type ColumnKind = "text" | "integer" | "real" | "boolean" | "enum";
|
|
333
|
+
type SqliteStorageType = "text" | "integer" | "real";
|
|
334
|
+
type ColumnMeta = {
|
|
335
|
+
kind: ColumnKind;
|
|
336
|
+
/** Storage type used in generated DDL. Booleans store as integer 0/1. */
|
|
337
|
+
sqlType: SqliteStorageType;
|
|
338
|
+
nullable: boolean;
|
|
339
|
+
hasDefault: boolean;
|
|
340
|
+
/** Declared default in JS form (booleans as booleans). */
|
|
341
|
+
defaultValue?: unknown;
|
|
342
|
+
/** Allowed values for `enum` columns. */
|
|
343
|
+
enumValues?: readonly string[];
|
|
344
|
+
description?: string;
|
|
345
|
+
};
|
|
346
|
+
declare class ColumnBuilder<Value, HasDefault extends boolean = false> {
|
|
347
|
+
readonly meta: ColumnMeta;
|
|
348
|
+
readonly "~types": {
|
|
349
|
+
value: Value;
|
|
350
|
+
hasDefault: HasDefault;
|
|
351
|
+
};
|
|
352
|
+
constructor(meta: ColumnMeta);
|
|
353
|
+
/** Allow SQL NULL; widens the value type with `null`. */
|
|
354
|
+
nullable(): ColumnBuilder<Value | null, HasDefault>;
|
|
355
|
+
/** Declare a column default. The value is used both in DDL and when migrating older events. */
|
|
356
|
+
default(value: Value): ColumnBuilder<Value, true>;
|
|
357
|
+
/** Narrow the TypeScript value type without changing storage, e.g. `t.text().$type<"movie" | "tv">()`. */
|
|
358
|
+
$type<Narrowed extends Value>(): ColumnBuilder<Narrowed, HasDefault>;
|
|
359
|
+
/** Human/AI-readable description, surfaced in generated schema docs. */
|
|
360
|
+
describe(description: string): ColumnBuilder<Value, HasDefault>;
|
|
361
|
+
}
|
|
362
|
+
type AnyColumnBuilder = ColumnBuilder<any, boolean>;
|
|
363
|
+
type TableColumns = Record<string, AnyColumnBuilder>;
|
|
364
|
+
type AnyTableBuilder = TableBuilder<any, any>;
|
|
365
|
+
type SyncSchemaTables = Record<string, AnyTableBuilder>;
|
|
366
|
+
type TableOptions<BaseName extends string | undefined = string | undefined> = {
|
|
367
|
+
/** Override the materialized base table name (defaults to the crdt table name prefixed with "_"). */
|
|
368
|
+
baseName?: BaseName;
|
|
369
|
+
description?: string;
|
|
370
|
+
};
|
|
371
|
+
type Simplify<T> = {
|
|
372
|
+
[K in keyof T]: T[K];
|
|
373
|
+
} & {};
|
|
374
|
+
type ColumnValue<Column> = Column extends ColumnBuilder<infer Value, boolean> ? Value : never;
|
|
375
|
+
type InferRow<Cols extends TableColumns> = Simplify<{
|
|
376
|
+
id: string;
|
|
377
|
+
} & {
|
|
378
|
+
[K in keyof Cols]: ColumnValue<Cols[K]>;
|
|
379
|
+
} & {
|
|
380
|
+
tombstone?: boolean;
|
|
381
|
+
}>;
|
|
382
|
+
type PayloadValidationResult = {
|
|
383
|
+
success: true;
|
|
384
|
+
} | {
|
|
385
|
+
success: false;
|
|
386
|
+
errors: string[];
|
|
387
|
+
};
|
|
388
|
+
declare class TableBuilder<Cols extends TableColumns, BaseName extends string | undefined = undefined> {
|
|
389
|
+
readonly userColumns: Cols;
|
|
390
|
+
readonly "~baseName": BaseName;
|
|
391
|
+
/** All columns in DDL order, including the auto-injected `id` and `tombstone`. */
|
|
392
|
+
readonly columns: Record<string, ColumnMeta>;
|
|
393
|
+
readonly baseName: BaseName;
|
|
394
|
+
readonly description: string | undefined;
|
|
395
|
+
constructor(userColumns: Cols, options?: TableOptions<BaseName>);
|
|
396
|
+
describe(description: string): TableBuilder<Cols, BaseName>;
|
|
397
|
+
/** Type-only: the row shape returned by queries. Do not access at runtime. */
|
|
398
|
+
get $row(): InferRow<Cols>;
|
|
399
|
+
/**
|
|
400
|
+
* Validate a CRDT event payload against the declared columns.
|
|
401
|
+
* `item-created` payloads must contain every column without a default (nullable columns may be omitted);
|
|
402
|
+
* `item-updated` payloads are partial and must not touch `id` or set `tombstone` to a deleted value.
|
|
403
|
+
*/
|
|
404
|
+
validatePayload(payload: Record<string, unknown>, options?: {
|
|
405
|
+
event?: "item-created" | "item-updated";
|
|
406
|
+
}): PayloadValidationResult;
|
|
407
|
+
}
|
|
408
|
+
declare const t: {
|
|
409
|
+
text: () => ColumnBuilder<string, false>;
|
|
410
|
+
integer: () => ColumnBuilder<number, false>;
|
|
411
|
+
real: () => ColumnBuilder<number, false>;
|
|
412
|
+
/** Stored as INTEGER 0/1; always bind booleans as 0/1 when writing raw SQL. */
|
|
413
|
+
boolean: () => ColumnBuilder<boolean, false>;
|
|
414
|
+
/**
|
|
415
|
+
* Union of string literals stored as TEXT, e.g. `t.enum(["movie", "tv"])`. Unlike `$type`, the
|
|
416
|
+
* allowed values are kept as runtime metadata, so payloads are validated against them.
|
|
417
|
+
* For an open-ended set, use `t.text().$type<"idle" | (string & {})>()` instead.
|
|
418
|
+
*/
|
|
419
|
+
enum: <const Values extends readonly [string, ...string[]]>(values: Values) => ColumnBuilder<Values[number], false>;
|
|
420
|
+
table: <Cols extends TableColumns, const BaseName extends string | undefined = undefined>(columns: Cols, options?: TableOptions<BaseName>) => TableBuilder<Cols, BaseName>;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
type CrdtTableConfig = {
|
|
424
|
+
baseTableName: string;
|
|
425
|
+
crdtTableName: string;
|
|
426
|
+
};
|
|
427
|
+
interface SyncDbSchema<ClientDB = {}, ServerDB = {}, MutationsDB = {}> {
|
|
428
|
+
get tablesConfig(): CrdtTableConfig[];
|
|
429
|
+
get migrations(): Migrations;
|
|
430
|
+
get tables(): SyncSchemaTables;
|
|
431
|
+
"~clientSchema": ClientDB;
|
|
432
|
+
"~serverSchema": ServerDB;
|
|
433
|
+
"~mutationsSchema": MutationsDB;
|
|
434
|
+
}
|
|
435
|
+
type ReadonlyTable<Table extends Record<string, unknown>> = {
|
|
436
|
+
[K in keyof Table]: ColumnType<Table[K], never, never>;
|
|
437
|
+
};
|
|
438
|
+
|
|
330
439
|
type PendingCrdtEvent = {
|
|
331
440
|
type: CrdtEventType;
|
|
332
441
|
dataset: string;
|
|
@@ -421,21 +530,23 @@ type DbSyncerStorage = {
|
|
|
421
530
|
hlc: StorageHLC;
|
|
422
531
|
eventHlcAccumulator?: StoredValue<string>;
|
|
423
532
|
onEventApplied?: (event: PersistedCrdtEvent) => void;
|
|
533
|
+
schema: Pick<SyncDbSchema, "tables" | "tablesConfig">;
|
|
424
534
|
};
|
|
425
|
-
type CrdtStorage = ReturnType<typeof createCrdtStorage>;
|
|
535
|
+
type CrdtStorage = Omit<ReturnType<typeof createCrdtStorage>, "internal">;
|
|
426
536
|
type EventsAppliedPayload = {
|
|
427
537
|
syncId: number;
|
|
428
538
|
eventHlcSum: string | null;
|
|
429
539
|
};
|
|
540
|
+
type InternalDbSchema = {
|
|
541
|
+
_crdt_events: PersistedCrdtEvent;
|
|
542
|
+
_crdt_update_log: CrdtUpdateLogItem;
|
|
543
|
+
};
|
|
430
544
|
declare function createCrdtStorage(storage: DbSyncerStorage): {
|
|
431
545
|
getEventsBatch: (options: GetEventsBatchQuery) => GetEventsBatch;
|
|
432
546
|
enqueueLocalEvents: (events: LocalCrdtEvent[], sourceNodeId: string) => EnqueueEventsResult;
|
|
433
547
|
enqueueOwnEvents: (events: OwnCrdtEvent[]) => EnqueueEventsResult;
|
|
434
548
|
enqueueRemoteEvents: (events: RemoteCrdtEvent[]) => EnqueueEventsResult;
|
|
435
|
-
|
|
436
|
-
wrapInTransaction?: boolean;
|
|
437
|
-
}) => void;
|
|
438
|
-
dispatchEventsApplied: (syncId?: number) => void;
|
|
549
|
+
applyOwnEvents: (events: OwnCrdtEvent[]) => void;
|
|
439
550
|
checkIsQuiescent: (pushedSyncId: number) => boolean;
|
|
440
551
|
getEventHlcAccumulator: () => string | null;
|
|
441
552
|
addEventListener: <K extends "events-applied" | "remote-event-apply-failed">(type: K, listener: (event: TypedEvent<{
|
|
@@ -452,6 +563,14 @@ declare function createCrdtStorage(storage: DbSyncerStorage): {
|
|
|
452
563
|
syncId: number;
|
|
453
564
|
};
|
|
454
565
|
}[K]>) => void) => void;
|
|
566
|
+
internal: {
|
|
567
|
+
applyOwnEventFromTransaction: (tx: InternalSQLiteTransactionWrapper<InternalDbSchema>, event: OwnCrdtEvent) => void;
|
|
568
|
+
processEnqueuedEvents: {
|
|
569
|
+
(): Promise<void>;
|
|
570
|
+
promise(): Promise<void> | null;
|
|
571
|
+
isExecuting(): boolean;
|
|
572
|
+
};
|
|
573
|
+
};
|
|
455
574
|
};
|
|
456
575
|
|
|
457
576
|
type WorkerNotificationMessage = {
|
|
@@ -591,4 +710,4 @@ declare const createCrdtSyncRemoteSource: ({ bufferSize, storage, migrator, pull
|
|
|
591
710
|
}[K]>) => void) => void;
|
|
592
711
|
};
|
|
593
712
|
|
|
594
|
-
export { type
|
|
713
|
+
export { type TableOptions as $, type AnyTableBuilder as A, baseSystemMigrations as B, type CrdtStorage as C, type DeSyncDetectedReason as D, type ExecuteParams as E, createSystemDbConfig as F, runSystemMigrations as G, type HLC as H, type InternalSQLiteWrapper as I, type SystemDbConfig as J, type KyselyQueryFactory as K, type Logger as L, type Migrations as M, type NewCrdtEvent as N, type SystemMigration as O, type SystemMigrationContext as P, type QueryBuilderOutput as Q, type ReadonlyTable as R, SQLiteDbWrapper as S, TypedEvent as T, type AnyColumnBuilder as U, ColumnBuilder as V, type WorkerState as W, type ColumnKind as X, type ColumnMeta as Y, type PayloadValidationResult as Z, type SqliteStorageType as _, type StoredValue as a, t as a0, createCrdtApplyFunction as a1, createSQLiteCrdtApplyFunction as a2, type PendingCrdtEvent as a3, createCrdtStorage as a4, type OwnCrdtEvent as a5, type CrdtSyncRemoteSource as a6, createCrdtSyncRemoteSource as a7, type EventsPullRequest as a8, type EventsPushRequest as a9, type GetEventsBatch as aA, type EventsPullResponse as aB, type EventsPushResponse as aa, CRDT_EVENT_NO_OP_PAYLOAD as ab, type CrdtEventOrigin as ac, type CrdtEventStatus as ad, type CrdtEventType as ae, type CrdtUpdateLogItem as af, type CrdtUpdateLogPayload as ag, isNoOpCrdtEventPayload as ah, type PersistedCrdtEvent as ai, createStoredValue as aj, type KyselyStatementFactory as ak, type PreparedStatement as al, createDeferredPromise as am, createTypedEventTarget as an, type DeferredPromise as ao, type DistributiveOmit as ap, generateId as aq, jsonSafeParse as ar, quoteId as as, TypedBroadcastChannel as at, type TypedEventTarget as au, tryCatch as av, tryCatchAsync as aw, type WorkerConfig as ax, type WorkerNotificationMessage as ay, type CreateRemoteSourceFactory as az, type SyncSchemaTables as b, type SyncDbSchema as c, TableBuilder as d, type TableColumns as e, type InferRow as f, type ExecuteResult as g, type SQLiteTransactionWrapper as h, type CrdtTableConfig as i, compareHLC as j, deserializeHLC as k, HLCCounter as l, type ColumnMetadata as m, type DatabaseIntrospection as n, introspectDb as o, type TableMetadata as p, type LogLevel as q, startPerformanceLogger as r, serializeHLC as s, createMigrations as t, createMigrator as u, type MigratableEvent as v, type MigrationsDb as w, type SyncDbMigrator as x, applyMemoryDbSchema as y, applyWorkerDbSchema as z };
|