@sqlite-sync/core 0.8.2 → 0.9.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/server-common.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { EventsPushResponse } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { EventsPullResponse } from \"../worker-db/worker-common\";\n\nconst pullEventsZodSchema = z.object({\n type: z.literal(\"pull-events\"),\n requestId: z.string(),\n afterSyncId: z.number(),\n excludeNodeId: z.string().optional(),\n});\nconst pushEventsZodSchema = z.object({\n type: z.literal(\"push-events\"),\n requestId: z.string(),\n nodeId: z.string(),\n events: z.array(\n z.object({\n schema_version: z.number(),\n timestamp: z.string(),\n type: z.enum([\"item-created\", \"item-updated\", \"item-deleted\"]),\n dataset: z.string(),\n item_id: z.string(),\n payload: z.string(),\n }),\n ),\n});\n\nexport const syncServerZodSchema = {\n pullEvents: pullEventsZodSchema,\n pushEvents: pushEventsZodSchema,\n request: z.discriminatedUnion(\"type\", [pullEventsZodSchema, pushEventsZodSchema]),\n};\n\nexport type SyncServerMessage =\n | {\n type: \"events-pull-response\";\n requestId: string;\n data: EventsPullResponse;\n }\n | {\n type: \"events-push-response\";\n requestId: string;\n data: EventsPushResponse;\n }\n | {\n type: \"events-applied\";\n newSyncId: number;\n /**\n * Remote's HLC checksum after applying up to `newSyncId`. Used by clients\n * to detect de-sync when they are caught up.\n */\n eventHlcSum: string | null;\n };\n\nexport type SyncServerRequest = z.infer<typeof syncServerZodSchema.request>;\n\nexport type ExtractSyncServerRequest<T extends SyncServerRequest[\"type\"]> = Extract<SyncServerRequest, { type: T }>;\n"],"mappings":";;;;;AAAA,SAAS,SAAS;AAIlB,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,EACtB,eAAe,EAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AACD,IAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,WAAW,EAAE,OAAO;AAAA,EACpB,QAAQ,EAAE,OAAO;AAAA,EACjB,QAAQ,EAAE;AAAA,IACR,EAAE,OAAO;AAAA,MACP,gBAAgB,EAAE,OAAO;AAAA,MACzB,WAAW,EAAE,OAAO;AAAA,MACpB,MAAM,EAAE,KAAK,CAAC,gBAAgB,gBAAgB,cAAc,CAAC;AAAA,MAC7D,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,MAClB,SAAS,EAAE,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AACF,CAAC;AAEM,IAAM,sBAAsB;AAAA,EACjC,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,SAAS,EAAE,mBAAmB,QAAQ,CAAC,qBAAqB,mBAAmB,CAAC;AAClF;","names":[]}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../src/server/server-common.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { EventsPushResponse } from \"../sqlite-crdt/crdt-sync-remote-source\";\nimport type { EventsPullResponse } from \"../worker-db/worker-common\";\n\nconst pullEventsZodSchema = z.object({\n type: z.literal(\"pull-events\"),\n requestId: z.string(),\n afterSyncId: z.number(),\n excludeNodeId: z.string().optional(),\n});\nconst pushEventsZodSchema = z.object({\n type: z.literal(\"push-events\"),\n requestId: z.string(),\n nodeId: z.string(),\n events: z.array(\n z.object({\n schema_version: z.number(),\n timestamp: z.string(),\n type: z.enum([\"item-created\", \"item-updated\", \"item-deleted\"]),\n dataset: z.string(),\n item_id: z.string(),\n payload: z.string(),\n }),\n ),\n});\n\nexport const syncServerZodSchema = {\n pullEvents: pullEventsZodSchema,\n pushEvents: pushEventsZodSchema,\n request: z.discriminatedUnion(\"type\", [pullEventsZodSchema, pushEventsZodSchema]),\n};\n\nexport type SyncServerMessage =\n | {\n type: \"events-pull-response\";\n requestId: string;\n data: EventsPullResponse;\n }\n | {\n type: \"events-push-response\";\n requestId: string;\n data: EventsPushResponse;\n }\n | {\n type: \"events-applied\";\n newSyncId: number;\n /**\n * Remote's HLC checksum after applying up to `newSyncId`. Used by clients\n * to detect de-sync when they are caught up.\n */\n eventHlcSum: string | null;\n };\n\nexport type SyncServerRequest = z.infer<typeof syncServerZodSchema.request>;\n\nexport type ExtractSyncServerRequest<T extends SyncServerRequest[\"type\"]> = Extract<SyncServerRequest, { type: T }>;\n"],"mappings":";;;AAIA,MAAM,sBAAsB,EAAE,OAAO;CACnC,MAAM,EAAE,QAAQ,aAAa;CAC7B,WAAW,EAAE,OAAO;CACpB,aAAa,EAAE,OAAO;CACtB,eAAe,EAAE,OAAO,CAAC,CAAC,SAAS;AACrC,CAAC;AACD,MAAM,sBAAsB,EAAE,OAAO;CACnC,MAAM,EAAE,QAAQ,aAAa;CAC7B,WAAW,EAAE,OAAO;CACpB,QAAQ,EAAE,OAAO;CACjB,QAAQ,EAAE,MACR,EAAE,OAAO;EACP,gBAAgB,EAAE,OAAO;EACzB,WAAW,EAAE,OAAO;EACpB,MAAM,EAAE,KAAK;GAAC;GAAgB;GAAgB;EAAc,CAAC;EAC7D,SAAS,EAAE,OAAO;EAClB,SAAS,EAAE,OAAO;EAClB,SAAS,EAAE,OAAO;CACpB,CAAC,CACH;AACF,CAAC;AAED,MAAa,sBAAsB;CACjC,YAAY;CACZ,YAAY;CACZ,SAAS,EAAE,mBAAmB,QAAQ,CAAC,qBAAqB,mBAAmB,CAAC;AAClF"}
@@ -0,0 +1,185 @@
1
+ import { l as parseTableName } from "./utils-Dr5JwcTP.js";
2
+ import { sql } from "kysely";
3
+ //#region src/sqlite-crdt/crdt-table-schema.ts
4
+ /** Persisted on applied events that were accepted but did not mutate materialized state. */
5
+ const CRDT_EVENT_NO_OP_PAYLOAD = "no-op";
6
+ function isNoOpCrdtEventPayload(payload) {
7
+ return payload === CRDT_EVENT_NO_OP_PAYLOAD;
8
+ }
9
+ //#endregion
10
+ //#region src/sqlite-crdt/stored-value.ts
11
+ function createStoredValue({ initialValue, saveToStorage }) {
12
+ let currentValue = initialValue;
13
+ return {
14
+ get current() {
15
+ return currentValue;
16
+ },
17
+ set current(newValue) {
18
+ saveToStorage?.(newValue);
19
+ currentValue = newValue;
20
+ }
21
+ };
22
+ }
23
+ //#endregion
24
+ //#region src/sqlite-kv-store.ts
25
+ function createKvStoreTableQuery(schema, tableName) {
26
+ return schema.createTable(tableName).ifNotExists().addColumn("key", "text", (col) => col.notNull().primaryKey()).addColumn("value", "text", (col) => col.notNull()).modifyEnd(sql`without rowid`);
27
+ }
28
+ function createSQLiteKvStore({ db, metaTableName }) {
29
+ const metaDb = db;
30
+ const get = (key) => {
31
+ const [result] = metaDb.executePrepared("get-meta-value", { key }, (db, params) => db.selectFrom(metaTableName).where("key", "=", params("key")).select("value").limit((eb) => eb.lit(1)), { loggerLevel: "system" });
32
+ return result?.value ?? null;
33
+ };
34
+ const set = (key, value) => {
35
+ metaDb.executePrepared("set-meta-value", {
36
+ key,
37
+ value
38
+ }, (db, params) => db.insertInto(metaTableName).values({
39
+ key: params("key"),
40
+ value: params("value")
41
+ }).onConflict((oc) => oc.doUpdateSet({ value: params("value") })), { loggerLevel: "system" });
42
+ };
43
+ const remove = (key) => {
44
+ metaDb.executePrepared("remove-meta-value", { key }, (db, params) => db.deleteFrom(metaTableName).where("key", "=", params("key")), { loggerLevel: "system" });
45
+ };
46
+ const getNumberOrDefault = (key, defaultValue) => {
47
+ const value = get(key);
48
+ if (!value) return defaultValue;
49
+ const parsedValue = Number.parseInt(value, 10);
50
+ return Number.isNaN(parsedValue) ? defaultValue : parsedValue;
51
+ };
52
+ return {
53
+ get,
54
+ set,
55
+ remove,
56
+ createStringStoredValue: (key, defaultValue) => createStoredValue({
57
+ initialValue: get(key) ?? defaultValue,
58
+ saveToStorage: (val) => set(key, val)
59
+ }),
60
+ createNumberStoredValue: (key, defaultValue) => createStoredValue({
61
+ initialValue: getNumberOrDefault(key, defaultValue),
62
+ saveToStorage: (val) => set(key, val.toString())
63
+ })
64
+ };
65
+ }
66
+ //#endregion
67
+ //#region src/migrations/system-schema.ts
68
+ function createSystemDbConfig({ eventsTableName, updateLogTableName }) {
69
+ return {
70
+ eventsTable: parseTableName(eventsTableName),
71
+ updateLogTable: parseTableName(updateLogTableName)
72
+ };
73
+ }
74
+ const workerDbConfig = createSystemDbConfig({
75
+ eventsTableName: "worker.crdt_events",
76
+ updateLogTableName: "crdt_update_log"
77
+ });
78
+ const memoryDbConfig = createSystemDbConfig({
79
+ eventsTableName: "persisted_crdt_events",
80
+ updateLogTableName: "crdt_update_log"
81
+ });
82
+ const nonNoOpEventPredicate = `"payload" <> '${CRDT_EVENT_NO_OP_PAYLOAD}'`;
83
+ const baseSystemMigrations = [
84
+ {
85
+ version: 0,
86
+ up: (ctx) => {
87
+ ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.eventsTable.fullIdentifier} (
88
+ "sync_id" integer NOT NULL PRIMARY KEY,
89
+ "schema_version" integer NOT NULL,
90
+ "status" text NOT NULL,
91
+ "type" text NOT NULL,
92
+ "timestamp" text NOT NULL,
93
+ "origin" text NOT NULL,
94
+ "dataset" text NOT NULL,
95
+ "item_id" text NOT NULL,
96
+ "payload" text NOT NULL
97
+ )`);
98
+ ctx.execute(`CREATE TABLE IF NOT EXISTS ${ctx.updateLogTable.fullIdentifier} (
99
+ "dataset" text NOT NULL,
100
+ "item_id" text NOT NULL,
101
+ "payload" text NOT NULL,
102
+ PRIMARY KEY ("item_id", "dataset")
103
+ )`);
104
+ }
105
+ },
106
+ {
107
+ version: 1,
108
+ up: (ctx) => {
109
+ ctx.execute(`ALTER TABLE ${ctx.eventsTable.fullIdentifier} ADD COLUMN "source_node_id" TEXT NOT NULL DEFAULT ''`);
110
+ }
111
+ },
112
+ {
113
+ version: 2,
114
+ up: (ctx) => {
115
+ const indexName = `${ctx.eventsTable.table}_status_sync_id_idx`;
116
+ ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("status", "sync_id")`);
117
+ }
118
+ },
119
+ {
120
+ version: 3,
121
+ up: (ctx) => {
122
+ const indexName = `${ctx.eventsTable.table}_timestamp_status_sync_id_idx`;
123
+ ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("timestamp", "status", "sync_id")`);
124
+ }
125
+ },
126
+ {
127
+ version: 4,
128
+ up: (ctx) => {
129
+ const indexName = `${ctx.eventsTable.table}_dataset_item_id_non_no_op_idx`;
130
+ ctx.execute(`CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} ("dataset", "item_id") WHERE ${nonNoOpEventPredicate}`);
131
+ }
132
+ }
133
+ ];
134
+ function runSystemMigrations(opts) {
135
+ const ctx = {
136
+ ...opts.dbConfig,
137
+ execute: opts.execute
138
+ };
139
+ for (const migration of opts.migrations) if (migration.version > opts.version.current) opts.transaction(() => {
140
+ migration.up(ctx);
141
+ opts.version.current = migration.version;
142
+ });
143
+ }
144
+ function applyWorkerDbSchema(db) {
145
+ db.executeKysely((kysely) => createKvStoreTableQuery(kysely.schema, "worker.kv"), { loggerLevel: "system" });
146
+ const kvStore = createSQLiteKvStore({
147
+ db,
148
+ metaTableName: "worker.kv"
149
+ });
150
+ runSystemMigrations({
151
+ migrations: baseSystemMigrations,
152
+ version: kvStore.createNumberStoredValue("internal-schema-version", -1),
153
+ dbConfig: workerDbConfig,
154
+ execute: (sql) => db.execute(sql, { loggerLevel: "system" }),
155
+ transaction: (callback) => db.executeTransaction(callback)
156
+ });
157
+ return { kvStore };
158
+ }
159
+ function applyMemoryDbSchema(db) {
160
+ db.execute(`CREATE TABLE IF NOT EXISTS ${memoryDbConfig.eventsTable.fullIdentifier} (
161
+ "sync_id" integer NOT NULL PRIMARY KEY,
162
+ "schema_version" integer NOT NULL,
163
+ "status" text NOT NULL,
164
+ "type" text NOT NULL,
165
+ "timestamp" text NOT NULL,
166
+ "origin" text NOT NULL,
167
+ "source_node_id" text NOT NULL DEFAULT '',
168
+ "dataset" text NOT NULL,
169
+ "item_id" text NOT NULL,
170
+ "payload" text NOT NULL
171
+ )`, { loggerLevel: "system" });
172
+ db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} ("status", "sync_id")`, { loggerLevel: "system" });
173
+ db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_timestamp_status_sync_id_idx ON ${memoryDbConfig.eventsTable.table} ("timestamp", "status", "sync_id")`, { loggerLevel: "system" });
174
+ db.execute(`CREATE INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_dataset_item_id_non_no_op_idx ON ${memoryDbConfig.eventsTable.table} ("dataset", "item_id") WHERE ${nonNoOpEventPredicate}`, { loggerLevel: "system" });
175
+ db.execute(`CREATE TABLE IF NOT EXISTS ${memoryDbConfig.updateLogTable.fullIdentifier} (
176
+ "dataset" text NOT NULL,
177
+ "item_id" text NOT NULL,
178
+ "payload" text NOT NULL,
179
+ PRIMARY KEY ("item_id", "dataset")
180
+ )`, { loggerLevel: "system" });
181
+ }
182
+ //#endregion
183
+ export { memoryDbConfig as a, createKvStoreTableQuery as c, CRDT_EVENT_NO_OP_PAYLOAD as d, isNoOpCrdtEventPayload as f, createSystemDbConfig as i, createSQLiteKvStore as l, applyWorkerDbSchema as n, runSystemMigrations as o, baseSystemMigrations as r, workerDbConfig as s, applyMemoryDbSchema as t, createStoredValue as u };
184
+
185
+ //# sourceMappingURL=system-schema-CkglcfTS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-schema-CkglcfTS.js","names":[],"sources":["../src/sqlite-crdt/crdt-table-schema.ts","../src/sqlite-crdt/stored-value.ts","../src/sqlite-kv-store.ts","../src/migrations/system-schema.ts"],"sourcesContent":["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","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 {\n CRDT_EVENT_NO_OP_PAYLOAD,\n type CrdtUpdateLogItem,\n type PersistedCrdtEvent,\n} 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\nconst nonNoOpEventPredicate = `\"payload\" <> '${CRDT_EVENT_NO_OP_PAYLOAD}'`;\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 version: 4,\n up: (ctx: SystemMigrationContext) => {\n const indexName = `${ctx.eventsTable.table}_dataset_item_id_non_no_op_idx`;\n ctx.execute(\n `CREATE INDEX IF NOT EXISTS ${ctx.eventsTable.schema}.${indexName} ON ${ctx.eventsTable.table} (\"dataset\", \"item_id\") WHERE ${nonNoOpEventPredicate}`,\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 INDEX IF NOT EXISTS ${memoryDbConfig.eventsTable.table}_dataset_item_id_non_no_op_idx ON ${memoryDbConfig.eventsTable.table} (\"dataset\", \"item_id\") WHERE ${nonNoOpEventPredicate}`,\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"],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ //#region src/utils.ts
2
+ function createDeferredPromise(opts) {
3
+ let resolve;
4
+ let reject;
5
+ let timeoutId;
6
+ return {
7
+ promise: new Promise((_resolve, _reject) => {
8
+ resolve = (value) => {
9
+ if (timeoutId) clearTimeout(timeoutId);
10
+ _resolve(value);
11
+ };
12
+ reject = (reason) => {
13
+ if (timeoutId) clearTimeout(timeoutId);
14
+ _reject(reason);
15
+ };
16
+ if (opts?.timeout) timeoutId = setTimeout(rejectTimeout, opts.timeout, _reject, opts);
17
+ }),
18
+ resolve,
19
+ reject
20
+ };
21
+ }
22
+ function rejectTimeout(reject, opts) {
23
+ reject(/* @__PURE__ */ new Error(`Promise timed out after ${opts.timeout}ms`));
24
+ tryCatch(() => opts?.onTimeout?.());
25
+ }
26
+ const generateId = () => {
27
+ return crypto.randomUUID();
28
+ };
29
+ function ensureSingletonExecution(fn, opts = { queueReExecution: true }) {
30
+ let executingPromise = null;
31
+ let shouldReExecute = false;
32
+ const wrappedFn = (...args) => {
33
+ if (executingPromise) {
34
+ shouldReExecute = true;
35
+ return executingPromise;
36
+ }
37
+ executingPromise = fn(...args).finally(() => {
38
+ executingPromise = null;
39
+ if (shouldReExecute && opts?.queueReExecution) {
40
+ shouldReExecute = false;
41
+ wrappedFn(...args);
42
+ }
43
+ });
44
+ return executingPromise;
45
+ };
46
+ wrappedFn.promise = () => executingPromise;
47
+ wrappedFn.isExecuting = () => !!executingPromise;
48
+ return wrappedFn;
49
+ }
50
+ var TypedBroadcastChannel = class {
51
+ channel;
52
+ constructor(name) {
53
+ this.channel = new BroadcastChannel(name);
54
+ }
55
+ postMessage(message) {
56
+ this.channel.postMessage(message);
57
+ }
58
+ set onmessage(callback) {
59
+ this.channel.onmessage = callback;
60
+ }
61
+ close() {
62
+ this.channel.close();
63
+ }
64
+ };
65
+ var TypedEvent = class extends Event {
66
+ payload;
67
+ constructor(type, payload) {
68
+ super(type);
69
+ this.payload = payload;
70
+ }
71
+ };
72
+ const createTypedEventTarget = () => {
73
+ const eventTarget = new EventTarget();
74
+ const addEventListener = (type, listener) => {
75
+ eventTarget.addEventListener(type, listener);
76
+ return { unsubscribe: () => {
77
+ eventTarget.removeEventListener(type, listener);
78
+ } };
79
+ };
80
+ const removeEventListener = (type, listener) => {
81
+ eventTarget.removeEventListener(type, listener);
82
+ };
83
+ const dispatchEvent = (type, payload) => {
84
+ eventTarget.dispatchEvent(new TypedEvent(type, payload));
85
+ };
86
+ return {
87
+ addEventListener,
88
+ removeEventListener,
89
+ dispatchEvent
90
+ };
91
+ };
92
+ function tryCatch(fn) {
93
+ try {
94
+ return {
95
+ success: true,
96
+ data: fn()
97
+ };
98
+ } catch (error) {
99
+ return {
100
+ success: false,
101
+ error
102
+ };
103
+ }
104
+ }
105
+ async function tryCatchAsync(fn) {
106
+ try {
107
+ return {
108
+ success: true,
109
+ data: await fn()
110
+ };
111
+ } catch (error) {
112
+ return {
113
+ success: false,
114
+ error
115
+ };
116
+ }
117
+ }
118
+ function jsonSafeParse(json) {
119
+ return tryCatch(() => JSON.parse(json));
120
+ }
121
+ /** Quote a SQLite identifier (table/column name), handling dot-separated schema qualifiers. */
122
+ function quoteId(name) {
123
+ return name.split(".").map((s) => `"${s.replace(/"/g, "\"\"")}"`).join(".");
124
+ }
125
+ function noop(..._args) {}
126
+ function parseTableName(tableName) {
127
+ if (!tableName?.trim()) throw new Error("Parse table: missing table name");
128
+ const parts = tableName.split(".");
129
+ if (parts.length > 2) throw new Error("Parse table: too many dot-delimited segments in table name");
130
+ return parts.length === 1 ? {
131
+ schema: "main",
132
+ table: parts[0],
133
+ fullIdentifier: parts[0]
134
+ } : {
135
+ schema: parts[0],
136
+ table: parts[1],
137
+ fullIdentifier: `${parts[0]}.${parts[1]}`
138
+ };
139
+ }
140
+ //#endregion
141
+ export { ensureSingletonExecution as a, noop as c, tryCatch as d, tryCatchAsync as f, createTypedEventTarget as i, parseTableName as l, TypedEvent as n, generateId as o, createDeferredPromise as r, jsonSafeParse as s, TypedBroadcastChannel as t, quoteId as u };
142
+
143
+ //# sourceMappingURL=utils-Dr5JwcTP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-Dr5JwcTP.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["export type DeferredPromise<T> = {\n promise: Promise<T>;\n resolve: (value: T) => void;\n reject: (error: Error) => void;\n};\n\ntype DeferredPromiseOptions = {\n timeout?: number;\n onTimeout?: () => void;\n};\n\nexport function createDeferredPromise<T>(opts?: DeferredPromiseOptions): DeferredPromise<T> {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const promise = new Promise<T>((_resolve, _reject) => {\n resolve = (value) => {\n if (timeoutId) clearTimeout(timeoutId);\n _resolve(value);\n };\n reject = (reason) => {\n if (timeoutId) clearTimeout(timeoutId);\n _reject(reason);\n };\n\n if (opts?.timeout) {\n timeoutId = setTimeout(rejectTimeout, opts.timeout, _reject, opts);\n }\n });\n\n return { promise, resolve, reject };\n}\n\nfunction rejectTimeout(reject: (reason?: unknown) => void, opts: DeferredPromiseOptions) {\n reject(new Error(`Promise timed out after ${opts.timeout}ms`));\n tryCatch(() => opts?.onTimeout?.());\n}\n\nexport const generateId = () => {\n return crypto.randomUUID();\n};\n\nexport type DistributiveOmit<T, K extends keyof T> = T extends any ? Omit<T, K> : never;\n\nexport function ensureSingletonExecution<T, TArgs extends any[]>(\n fn: (...args: TArgs) => Promise<T>,\n opts: { queueReExecution?: boolean } = { queueReExecution: true },\n) {\n let executingPromise: Promise<T> | null = null;\n let shouldReExecute = false;\n\n const wrappedFn = (...args: TArgs) => {\n if (executingPromise) {\n shouldReExecute = true;\n return executingPromise;\n }\n\n executingPromise = fn(...args).finally(() => {\n executingPromise = null;\n\n if (shouldReExecute && opts?.queueReExecution) {\n shouldReExecute = false;\n wrappedFn(...args);\n }\n });\n return executingPromise;\n };\n\n wrappedFn.promise = () => executingPromise;\n wrappedFn.isExecuting = () => !!executingPromise;\n\n return wrappedFn;\n}\n\nexport function createAutoFlushBuffer<T>({ size, flush }: { size: number; flush: (items: T[]) => void }) {\n const buffer: T[] = [];\n\n return {\n add(item: T) {\n buffer.push(item);\n if (buffer.length >= size) {\n flush(buffer);\n buffer.length = 0;\n }\n },\n flush() {\n flush(buffer);\n buffer.length = 0;\n },\n };\n}\n\nexport function createAsyncAutoFlushBuffer<T>({\n size,\n flush,\n}: {\n size: number;\n flush: (items: T[]) => void | Promise<void>;\n}) {\n const buffer: T[] = [];\n\n return {\n async add(item: T) {\n buffer.push(item);\n if (buffer.length >= size) {\n await this.flush();\n }\n },\n async flush() {\n const itemsToFlush = buffer.splice(0);\n if (itemsToFlush.length === 0) {\n return;\n }\n await flush(itemsToFlush);\n },\n };\n}\n\nexport class TypedBroadcastChannel<TMessage> {\n private readonly channel: BroadcastChannel;\n\n constructor(name: string) {\n this.channel = new BroadcastChannel(name);\n }\n\n postMessage(message: TMessage) {\n this.channel.postMessage(message);\n }\n\n set onmessage(callback: ((event: MessageEvent<TMessage>) => void) | null) {\n this.channel.onmessage = callback;\n }\n\n close() {\n this.channel.close();\n }\n}\n\nexport class TypedEvent<T = unknown> extends Event {\n readonly payload: T;\n constructor(type: string, payload: T) {\n super(type);\n this.payload = payload;\n }\n}\n\nexport type TypedEventTarget<T extends Record<string, unknown>> = {\n addEventListener: <K extends keyof T & string>(\n type: K,\n listener: (event: TypedEvent<T[K]>) => void,\n ) => { unsubscribe: () => void };\n removeEventListener: <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => void;\n dispatchEvent: <K extends keyof T & string>(type: K, payload: T[K]) => void;\n};\n\nexport const createTypedEventTarget = <T extends Record<string, unknown>>(): TypedEventTarget<T> => {\n const eventTarget = new EventTarget();\n\n const addEventListener = <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => {\n eventTarget.addEventListener(type, listener as (e: Event) => void);\n\n return {\n unsubscribe: () => {\n eventTarget.removeEventListener(type, listener as (e: Event) => void);\n },\n };\n };\n\n const removeEventListener = <K extends keyof T & string>(type: K, listener: (event: TypedEvent<T[K]>) => void) => {\n eventTarget.removeEventListener(type, listener as (e: Event) => void);\n };\n\n const dispatchEvent = <K extends keyof T & string>(type: K, payload: T[K]) => {\n eventTarget.dispatchEvent(new TypedEvent(type, payload));\n };\n\n return {\n addEventListener,\n removeEventListener,\n dispatchEvent,\n };\n};\n\ntype TryCatchResult<T> =\n | {\n success: true;\n data: T;\n }\n | {\n success: false;\n error: unknown;\n };\n\nexport function tryCatch<T>(fn: () => T): TryCatchResult<T> {\n try {\n return {\n success: true,\n data: fn(),\n };\n } catch (error) {\n return {\n success: false,\n error,\n };\n }\n}\n\nexport async function tryCatchAsync<T>(fn: () => Promise<T>): Promise<TryCatchResult<T>> {\n try {\n return {\n success: true,\n data: await fn(),\n };\n } catch (error) {\n return {\n success: false,\n error,\n };\n }\n}\n\nexport function jsonSafeParse<T>(json: string) {\n return tryCatch(() => JSON.parse(json) as T);\n}\n\n/** Quote a SQLite identifier (table/column name), handling dot-separated schema qualifiers. */\nexport function quoteId(name: string): string {\n return name\n .split(\".\")\n .map((s) => `\"${s.replace(/\"/g, '\"\"')}\"`)\n .join(\".\");\n}\n\nexport function noop(..._args: unknown[]): void {}\n\nexport type ParsedTableName = {\n schema: \"main\" | (string & {});\n table: string;\n fullIdentifier: string;\n};\n\nexport function parseTableName(tableName: string): ParsedTableName {\n if (!tableName?.trim()) {\n throw new Error(\"Parse table: missing table name\");\n }\n const parts = tableName.split(\".\");\n if (parts.length > 2) {\n throw new Error(\"Parse table: too many dot-delimited segments in table name\");\n }\n\n return parts.length === 1\n ? {\n schema: \"main\",\n table: parts[0],\n fullIdentifier: parts[0],\n }\n : {\n schema: parts[0],\n table: parts[1],\n fullIdentifier: `${parts[0]}.${parts[1]}`,\n };\n}\n"],"mappings":"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"}
package/dist/worker.d.ts CHANGED
@@ -1,44 +1,42 @@
1
- import { az as CreateRemoteSourceFactory, ax as WorkerConfig, d as SyncDbSchema, L as Logger } from './crdt-sync-remote-source-CnB4p4Ry.js';
2
- export { a8 as EventsPullRequest, a9 as EventsPushRequest, aa as EventsPushResponse, aA as GetEventsBatch } from './crdt-sync-remote-source-CnB4p4Ry.js';
3
- import { b as ResetStore } from './reset-state-D6UslXaj.js';
4
- import 'kysely';
5
- import '@sqlite.org/sqlite-wasm';
6
-
1
+ import { $ as GetEventsBatch, It as Logger, S as WorkerConfig, Yt as SyncDbSchema, _ as EventsPullRequest, g as CreateRemoteSourceFactory, r as ResetStore, v as EventsPushRequest, y as EventsPushResponse } from "./index-Ct_YbBmV.js";
2
+ //#region src/web-socket/ws-remote-source.d.ts
7
3
  type WsRemoteSourceConfig = {
8
- createWebSocket: () => Pick<WebSocket, "onmessage" | "close" | "addEventListener"> & {
9
- send: (data: string) => void;
10
- };
4
+ createWebSocket: () => Pick<WebSocket, "onmessage" | "close" | "addEventListener"> & {
5
+ send: (data: string) => void;
6
+ };
11
7
  };
12
8
  declare const createWsRemoteSource: ({ createWebSocket }: WsRemoteSourceConfig) => CreateRemoteSourceFactory;
13
-
9
+ //#endregion
10
+ //#region src/worker-db/db-worker.d.ts
14
11
  declare function getWorkerConfig<Props = never>(): Promise<WorkerConfig<Props>>;
15
12
  type WorkerOptions = {
16
- syncDbSchema: SyncDbSchema;
17
- logger?: Logger;
18
- createRemoteSource?: CreateRemoteSourceFactory;
19
- workerConfig?: WorkerConfig;
20
- /** Durable storage for reset state. Defaults to an IndexedDB-backed store. */
21
- resetStore?: ResetStore;
22
- /**
23
- * App-provided storage version, combined with the library's internal storage
24
- * version. Bump it when deploying a code change that old persisted local DBs
25
- * cannot survive — on mismatch the elected worker wipes the local DB on startup.
26
- */
27
- storageVersion?: string;
28
- /**
29
- * Dev-time drift check: replays the full migration history on a throwaway
30
- * in-memory database and verifies the result matches the schema declared with
31
- * `defineSyncSchema`. On mismatch the worker throws and refuses to start.
32
- * Enable in development only, e.g. `verifySchema: import.meta.env.DEV`.
33
- */
34
- verifySchema?: boolean;
35
- /**
36
- * Runs local worker event-log garbage collection on startup. Disabled by default;
37
- * set `eventLogGc: true` to enable. GC keeps the latest event rows and never removes
38
- * pending events or local rows that have not been pushed to the remote.
39
- */
40
- eventLogGc?: boolean;
13
+ syncDbSchema: SyncDbSchema;
14
+ logger?: Logger;
15
+ createRemoteSource?: CreateRemoteSourceFactory;
16
+ workerConfig?: WorkerConfig;
17
+ /** Durable storage for reset state. Defaults to an IndexedDB-backed store. */
18
+ resetStore?: ResetStore;
19
+ /**
20
+ * App-provided storage version, combined with the library's internal storage
21
+ * version. Bump it when deploying a code change that old persisted local DBs
22
+ * cannot survive — on mismatch the elected worker wipes the local DB on startup.
23
+ */
24
+ storageVersion?: string;
25
+ /**
26
+ * Dev-time drift check: replays the full migration history on a throwaway
27
+ * in-memory database and verifies the result matches the schema declared with
28
+ * `defineSyncSchema`. On mismatch the worker throws and refuses to start.
29
+ * Enable in development only, e.g. `verifySchema: import.meta.env.DEV`.
30
+ */
31
+ verifySchema?: boolean;
32
+ /**
33
+ * Runs local worker event-log garbage collection on startup. Disabled by default;
34
+ * set `eventLogGc: true` to enable. GC keeps the latest event rows and never removes
35
+ * pending events or local rows that have not been pushed to the remote.
36
+ */
37
+ eventLogGc?: boolean;
41
38
  };
42
39
  declare function startDbWorker(opts: WorkerOptions): Promise<void>;
43
-
44
- export { createWsRemoteSource, getWorkerConfig, startDbWorker };
40
+ //#endregion
41
+ export { type EventsPullRequest, type EventsPushRequest, type EventsPushResponse, type GetEventsBatch, createWsRemoteSource, getWorkerConfig, startDbWorker };
42
+ //# sourceMappingURL=worker.d.ts.map