@sqlite-sync/cloudflare 0.1.1 → 0.2.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/index.d.ts +5 -4
- package/dist/index.js +64 -98
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _sqlite_sync_core from '@sqlite-sync/core';
|
|
2
|
-
import { SyncDbSchema, CrdtStorageMutator, TypedEventTarget, CrdtEventStatus, CrdtEventType, CrdtEventOrigin, Migrations } from '@sqlite-sync/core';
|
|
2
|
+
import { InternalSQLiteWrapper, SyncDbSchema, CrdtStorageMutator, TypedEventTarget, CrdtEventStatus, CrdtEventType, CrdtEventOrigin, Migrations } from '@sqlite-sync/core';
|
|
3
3
|
import { Compilable, Kysely } from 'kysely';
|
|
4
4
|
|
|
5
5
|
type ExecuteParams = {
|
|
@@ -17,6 +17,7 @@ type KyselyExecutor<TDatabase> = {
|
|
|
17
17
|
transaction: (callback: (tx: Pick<KyselyExecutor<TDatabase>, "execute" | "executeKysely">) => void) => void;
|
|
18
18
|
};
|
|
19
19
|
declare function createKyselyExecutor<TDatabase>(db: DurableObjectStorage): KyselyExecutor<TDatabase>;
|
|
20
|
+
declare function createCrdtStorageDb(executor: KyselyExecutor<any>): InternalSQLiteWrapper<any>;
|
|
20
21
|
|
|
21
22
|
type TypedPersistedCrdtEvent<Schema extends SyncDbSchema> = {
|
|
22
23
|
schema_version: number;
|
|
@@ -41,10 +42,10 @@ declare function createDurableObjectCrdtStorage<Schema extends SyncDbSchema>({ s
|
|
|
41
42
|
crdtEventsTable: string;
|
|
42
43
|
batchSize?: number;
|
|
43
44
|
broadcastPayload: (payload: string) => void;
|
|
44
|
-
}): {
|
|
45
|
+
}): Promise<{
|
|
45
46
|
syncDb: ServerSyncDb<Schema>;
|
|
46
47
|
remoteHandler: RemoteHandler;
|
|
47
|
-
}
|
|
48
|
+
}>;
|
|
48
49
|
type MessageResult = {
|
|
49
50
|
success: true;
|
|
50
51
|
payload: string;
|
|
@@ -68,4 +69,4 @@ declare function createMigrator(kv: SyncKvStorage, sqlExecutor: KyselyExecutor<a
|
|
|
68
69
|
};
|
|
69
70
|
type SyncDbMigrator = ReturnType<typeof createMigrator>;
|
|
70
71
|
|
|
71
|
-
export { type KyselyExecutor, type RemoteHandler, type ServerSyncDb, type SyncDbMigrator, type TypedPersistedCrdtEvent, createKyselyExecutor, createMigrator, durableObjectAdapter };
|
|
72
|
+
export { type KyselyExecutor, type RemoteHandler, type ServerSyncDb, type SyncDbMigrator, type TypedPersistedCrdtEvent, createCrdtStorageDb, createKyselyExecutor, createMigrator, durableObjectAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
// src/durable-object-adapter.ts
|
|
2
2
|
import {
|
|
3
|
-
applyKyselyEventsBatchFilters,
|
|
4
3
|
baseSystemMigrations,
|
|
5
|
-
createCrdtApplyFunction,
|
|
6
4
|
createCrdtStorage,
|
|
7
5
|
createCrdtStorageMutator,
|
|
8
6
|
createCrdtSyncProducer,
|
|
9
7
|
createStoredValue as createStoredValue2,
|
|
8
|
+
createSystemDbConfig,
|
|
10
9
|
createTypedEventTarget,
|
|
11
10
|
HLCCounter,
|
|
12
11
|
jsonSafeParse,
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
runSystemMigrations,
|
|
13
|
+
xxhash
|
|
15
14
|
} from "@sqlite-sync/core";
|
|
16
15
|
import {
|
|
17
16
|
syncServerZodSchema
|
|
@@ -38,6 +37,32 @@ function createKyselyExecutor(db) {
|
|
|
38
37
|
};
|
|
39
38
|
return executor;
|
|
40
39
|
}
|
|
40
|
+
function createCrdtStorageDb(executor) {
|
|
41
|
+
const wrapper = {
|
|
42
|
+
executePreparedRaw: ({
|
|
43
|
+
sql,
|
|
44
|
+
params
|
|
45
|
+
}) => executor.execute({
|
|
46
|
+
sql,
|
|
47
|
+
parameters: params ?? []
|
|
48
|
+
}).rows,
|
|
49
|
+
executePrepared: (_, params, factory) => {
|
|
50
|
+
const query = factory(dummyKysely, (key) => key).compile();
|
|
51
|
+
const parameters = query.parameters.map((param) => params[param]);
|
|
52
|
+
const result = executor.execute({
|
|
53
|
+
sql: query.sql,
|
|
54
|
+
parameters
|
|
55
|
+
});
|
|
56
|
+
return result.rows;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
...wrapper,
|
|
61
|
+
executeTransaction: (callback) => executor.transaction(() => {
|
|
62
|
+
callback(wrapper);
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
}
|
|
41
66
|
|
|
42
67
|
// src/migrator.ts
|
|
43
68
|
import { createMigrator as createBaseMigrator, createStoredValue } from "@sqlite-sync/core";
|
|
@@ -72,21 +97,7 @@ function createMigrator(kv, sqlExecutor, migrations, updateLogTableName2) {
|
|
|
72
97
|
|
|
73
98
|
// src/durable-object-adapter.ts
|
|
74
99
|
var updateLogTableName = "__crdt_update_log";
|
|
75
|
-
|
|
76
|
-
...baseSystemMigrations,
|
|
77
|
-
{
|
|
78
|
-
version: 2,
|
|
79
|
-
up: (ctx) => {
|
|
80
|
-
ctx.execute(
|
|
81
|
-
`DELETE FROM ${ctx.eventsTableName} WHERE "sync_id" NOT IN (SELECT MIN("sync_id") FROM ${ctx.eventsTableName} GROUP BY "timestamp", "source_node_id")`
|
|
82
|
-
);
|
|
83
|
-
ctx.execute(
|
|
84
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS "idx_crdt_events_dedup" ON ${ctx.eventsTableName} ("timestamp", "source_node_id")`
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
];
|
|
89
|
-
function createDurableObjectCrdtStorage({
|
|
100
|
+
async function createDurableObjectCrdtStorage({
|
|
90
101
|
storage,
|
|
91
102
|
syncDbSchema,
|
|
92
103
|
nodeId,
|
|
@@ -94,98 +105,49 @@ function createDurableObjectCrdtStorage({
|
|
|
94
105
|
batchSize = 50,
|
|
95
106
|
broadcastPayload
|
|
96
107
|
}) {
|
|
108
|
+
await xxhash.ensureLoaded();
|
|
109
|
+
const dbConfig = createSystemDbConfig({
|
|
110
|
+
eventsTableName: crdtEventsTable,
|
|
111
|
+
updateLogTableName
|
|
112
|
+
});
|
|
97
113
|
const eventTarget = createTypedEventTarget();
|
|
98
114
|
const sqlExecutor = createKyselyExecutor(storage);
|
|
115
|
+
const crdtStorageDb = createCrdtStorageDb(sqlExecutor);
|
|
99
116
|
runSystemMigrations({
|
|
100
|
-
migrations:
|
|
117
|
+
migrations: baseSystemMigrations,
|
|
101
118
|
version: createStoredValue2({
|
|
102
119
|
initialValue: storage.kv.get("internal-schema-version") ?? -1,
|
|
103
120
|
saveToStorage: (val) => storage.kv.put("internal-schema-version", val)
|
|
104
121
|
}),
|
|
105
|
-
|
|
106
|
-
updateLogTableName: quoteId(updateLogTableName),
|
|
122
|
+
dbConfig,
|
|
107
123
|
execute: (sql) => sqlExecutor.execute({ sql, parameters: [] }),
|
|
108
124
|
transaction: (callback) => sqlExecutor.transaction(callback)
|
|
109
125
|
});
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
const migrator = createMigrator(
|
|
127
|
+
storage.kv,
|
|
128
|
+
sqlExecutor,
|
|
129
|
+
syncDbSchema.migrations,
|
|
130
|
+
dbConfig.updateLogTable.fullIdentifier
|
|
131
|
+
);
|
|
114
132
|
migrator.migrateDbToLatest();
|
|
115
|
-
const baseApply = createCrdtApplyFunction({
|
|
116
|
-
getCrdtUpdateLog(opts) {
|
|
117
|
-
const [metaRow] = sqlExecutor.executeKysely(
|
|
118
|
-
(db) => db.selectFrom(updateLogTableName).select("payload").where("item_id", "=", opts.itemId).where("dataset", "=", opts.dataset)
|
|
119
|
-
).rows;
|
|
120
|
-
return metaRow ? JSON.parse(metaRow.payload) : null;
|
|
121
|
-
},
|
|
122
|
-
insertCrdtUpdateLog(opts) {
|
|
123
|
-
sqlExecutor.executeKysely(
|
|
124
|
-
(db) => db.insertInto(updateLogTableName).values({
|
|
125
|
-
item_id: opts.itemId,
|
|
126
|
-
dataset: opts.dataset,
|
|
127
|
-
payload: opts.payload
|
|
128
|
-
})
|
|
129
|
-
);
|
|
130
|
-
},
|
|
131
|
-
updateCrdtUpdateLog(opts) {
|
|
132
|
-
sqlExecutor.executeKysely(
|
|
133
|
-
(db) => db.updateTable(updateLogTableName).set({
|
|
134
|
-
payload: opts.payload
|
|
135
|
-
}).where("item_id", "=", opts.itemId).where("dataset", "=", opts.dataset)
|
|
136
|
-
);
|
|
137
|
-
},
|
|
138
|
-
insertItem(opts) {
|
|
139
|
-
sqlExecutor.executeKysely((db) => db.insertInto(opts.dataset).values(opts.payload));
|
|
140
|
-
},
|
|
141
|
-
updateItem(opts) {
|
|
142
|
-
const keys = Array.from(Object.keys(opts.payload));
|
|
143
|
-
sqlExecutor.execute({
|
|
144
|
-
sql: `update ${quoteId(opts.dataset)} set ${keys.map((key) => `${quoteId(key)} = ?`).join(",")} where id = ?`,
|
|
145
|
-
parameters: [...keys.map((key) => opts.payload[key]), opts.itemId]
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
const handleCrdtEventApply = (event) => {
|
|
150
|
-
sqlExecutor.transaction(() => {
|
|
151
|
-
baseApply(event);
|
|
152
|
-
});
|
|
153
|
-
queueMicrotask(() => {
|
|
154
|
-
eventTarget.dispatchEvent("event-applied", event);
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
133
|
const truncatedNodeId = nodeId.slice(0, 12);
|
|
158
134
|
const hlc = new HLCCounter(truncatedNodeId, () => Date.now());
|
|
159
135
|
const crdtStorage = createCrdtStorage({
|
|
160
136
|
nodeId: truncatedNodeId,
|
|
161
|
-
|
|
137
|
+
initialLocalSyncId: getLatestSyncId(sqlExecutor),
|
|
162
138
|
hlc,
|
|
163
139
|
migrator,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
sqlExecutor.executeKysely(
|
|
176
|
-
(db) => db.insertInto(crdtEventsTable).values(event).onConflict((oc) => oc.columns(["timestamp", "source_node_id"]).doNothing())
|
|
177
|
-
);
|
|
178
|
-
},
|
|
179
|
-
updateEvent: (syncId2, event) => sqlExecutor.executeKysely(
|
|
180
|
-
(db) => db.updateTable(crdtEventsTable).set({
|
|
181
|
-
status: event.status,
|
|
182
|
-
dataset: event.dataset,
|
|
183
|
-
item_id: event.item_id,
|
|
184
|
-
schema_version: event.schema_version,
|
|
185
|
-
type: event.type,
|
|
186
|
-
payload: event.payload
|
|
187
|
-
}).where("sync_id", "=", syncId2)
|
|
188
|
-
)
|
|
140
|
+
db: crdtStorageDb,
|
|
141
|
+
dbConfig,
|
|
142
|
+
eventHlcAccumulator: createStoredValue2({
|
|
143
|
+
initialValue: storage.kv.get("crdt.consistency.event_hlc_sum.v3") ?? "",
|
|
144
|
+
saveToStorage: (val) => storage.kv.put("crdt.consistency.event_hlc_sum.v3", val)
|
|
145
|
+
}),
|
|
146
|
+
onEventApplied: (event) => {
|
|
147
|
+
queueMicrotask(() => {
|
|
148
|
+
eventTarget.dispatchEvent("event-applied", event);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
189
151
|
});
|
|
190
152
|
const remoteHandler = createDurableObjectRemoteHandler({
|
|
191
153
|
bufferSize: batchSize,
|
|
@@ -218,7 +180,8 @@ function createDurableObjectRemoteHandler({
|
|
|
218
180
|
broadcastPayload(
|
|
219
181
|
JSON.stringify({
|
|
220
182
|
type: "events-applied",
|
|
221
|
-
newSyncId: chunk.newSyncId
|
|
183
|
+
newSyncId: chunk.newSyncId,
|
|
184
|
+
eventHlcSum: chunk.eventHlcSum
|
|
222
185
|
})
|
|
223
186
|
);
|
|
224
187
|
}
|
|
@@ -249,7 +212,7 @@ function createDurableObjectRemoteHandler({
|
|
|
249
212
|
limit: bufferSize,
|
|
250
213
|
status: "applied",
|
|
251
214
|
afterSyncId: request.afterSyncId,
|
|
252
|
-
excludeNodeId: request.excludeNodeId
|
|
215
|
+
excludeNodeId: request.excludeNodeId ?? ""
|
|
253
216
|
});
|
|
254
217
|
const eventsPullMessage = {
|
|
255
218
|
type: "events-pull-response",
|
|
@@ -273,12 +236,14 @@ function createDurableObjectRemoteHandler({
|
|
|
273
236
|
};
|
|
274
237
|
};
|
|
275
238
|
const handlePushEvents = (request) => {
|
|
276
|
-
crdtStorage.enqueueLocalEvents(request.events, request.nodeId);
|
|
239
|
+
const { beforeSyncId, afterSyncId } = crdtStorage.enqueueLocalEvents(request.events, request.nodeId);
|
|
277
240
|
const eventsAppliedMessage = {
|
|
278
241
|
type: "events-push-response",
|
|
279
242
|
requestId: request.requestId,
|
|
280
243
|
data: {
|
|
281
|
-
ok: true
|
|
244
|
+
ok: true,
|
|
245
|
+
beforeSyncId,
|
|
246
|
+
afterSyncId
|
|
282
247
|
}
|
|
283
248
|
};
|
|
284
249
|
return {
|
|
@@ -298,6 +263,7 @@ var durableObjectAdapter = {
|
|
|
298
263
|
createCrdtStorage: createDurableObjectCrdtStorage
|
|
299
264
|
};
|
|
300
265
|
export {
|
|
266
|
+
createCrdtStorageDb,
|
|
301
267
|
createKyselyExecutor,
|
|
302
268
|
createMigrator,
|
|
303
269
|
durableObjectAdapter
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/durable-object-adapter.ts","../src/kysely-executor.ts","../src/migrator.ts"],"sourcesContent":["import {\n applyKyselyEventsBatchFilters,\n baseSystemMigrations,\n type CrdtEventOrigin,\n type CrdtEventStatus,\n type CrdtEventType,\n type CrdtStorage,\n type CrdtStorageMutator,\n type CrdtUpdateLogItem,\n type CrdtUpdateLogPayload,\n createCrdtApplyFunction,\n createCrdtStorage,\n createCrdtStorageMutator,\n createCrdtSyncProducer,\n createStoredValue,\n createTypedEventTarget,\n HLCCounter,\n jsonSafeParse,\n type PersistedCrdtEvent,\n quoteId,\n runSystemMigrations,\n type SyncDbSchema,\n type SystemMigration,\n type TypedEventTarget,\n} from \"@sqlite-sync/core\";\nimport {\n type ExtractSyncServerRequest,\n type SyncServerMessage,\n type SyncServerRequest,\n syncServerZodSchema,\n} from \"@sqlite-sync/core/server\";\nimport { createKyselyExecutor, type KyselyExecutor } from \"./kysely-executor\";\nimport { createMigrator } from \"./migrator\";\n\nconst updateLogTableName = \"__crdt_update_log\";\n\nconst durableObjectMigrations: SystemMigration[] = [\n ...baseSystemMigrations,\n {\n version: 2,\n up: (ctx) => {\n ctx.execute(\n `DELETE FROM ${ctx.eventsTableName} WHERE \"sync_id\" NOT IN ` +\n `(SELECT MIN(\"sync_id\") FROM ${ctx.eventsTableName} GROUP BY \"timestamp\", \"source_node_id\")`,\n );\n ctx.execute(\n `CREATE UNIQUE INDEX IF NOT EXISTS \"idx_crdt_events_dedup\" ` +\n `ON ${ctx.eventsTableName} (\"timestamp\", \"source_node_id\")`,\n );\n },\n },\n];\n\ntype AdapterDb = {\n crdtEvents: PersistedCrdtEvent;\n [updateLogTableName]: CrdtUpdateLogItem;\n};\n\nexport type TypedPersistedCrdtEvent<Schema extends SyncDbSchema> = {\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: keyof Schema[`~mutationsSchema`];\n item_id: string;\n payload: string;\n};\n\ntype ServerSyncDbEvents<Schema extends SyncDbSchema> = {\n \"event-applied\": TypedPersistedCrdtEvent<Schema>;\n};\n\nexport type ServerSyncDb<Schema extends SyncDbSchema> = Pick<\n KyselyExecutor<Schema[`~serverSchema`]>,\n \"execute\" | \"executeKysely\"\n> &\n CrdtStorageMutator<Schema[`~mutationsSchema`]> &\n Pick<TypedEventTarget<ServerSyncDbEvents<Schema>>, \"addEventListener\" | \"removeEventListener\">;\n\nfunction createDurableObjectCrdtStorage<Schema extends SyncDbSchema>({\n storage,\n syncDbSchema,\n nodeId,\n crdtEventsTable = \"crdt_events\",\n batchSize = 50,\n broadcastPayload,\n}: {\n storage: DurableObjectStorage;\n syncDbSchema: Schema;\n nodeId: string;\n crdtEventsTable: string;\n batchSize?: number;\n broadcastPayload: (payload: string) => void;\n}): {\n syncDb: ServerSyncDb<Schema>;\n remoteHandler: RemoteHandler;\n} {\n const eventTarget = createTypedEventTarget<ServerSyncDbEvents<Schema>>();\n const sqlExecutor = createKyselyExecutor<AdapterDb>(storage);\n\n runSystemMigrations({\n migrations: durableObjectMigrations,\n version: createStoredValue<number>({\n initialValue: storage.kv.get(\"internal-schema-version\") ?? -1,\n saveToStorage: (val) => storage.kv.put(\"internal-schema-version\", val),\n }),\n eventsTableName: quoteId(crdtEventsTable),\n updateLogTableName: quoteId(updateLogTableName),\n execute: (sql) => sqlExecutor.execute({ sql, parameters: [] }),\n transaction: (callback) => sqlExecutor.transaction(callback),\n });\n\n const syncId = createStoredValue({\n initialValue: getLatestSyncId(sqlExecutor),\n });\n\n const migrator = createMigrator(storage.kv, sqlExecutor, syncDbSchema.migrations, quoteId(updateLogTableName));\n migrator.migrateDbToLatest();\n\n const baseApply = createCrdtApplyFunction({\n getCrdtUpdateLog(opts) {\n const [metaRow] = sqlExecutor.executeKysely((db) =>\n db\n .selectFrom(updateLogTableName)\n .select(\"payload\")\n .where(\"item_id\", \"=\", opts.itemId)\n .where(\"dataset\", \"=\", opts.dataset),\n ).rows;\n return metaRow ? (JSON.parse(metaRow.payload) as CrdtUpdateLogPayload) : null;\n },\n insertCrdtUpdateLog(opts) {\n sqlExecutor.executeKysely((db) =>\n db.insertInto(updateLogTableName).values({\n item_id: opts.itemId,\n dataset: opts.dataset,\n payload: opts.payload,\n }),\n );\n },\n updateCrdtUpdateLog(opts) {\n sqlExecutor.executeKysely((db) =>\n db\n .updateTable(updateLogTableName)\n .set({\n payload: opts.payload,\n })\n .where(\"item_id\", \"=\", opts.itemId)\n .where(\"dataset\", \"=\", opts.dataset),\n );\n },\n insertItem(opts) {\n sqlExecutor.executeKysely((db) => db.insertInto(opts.dataset as any).values(opts.payload));\n },\n updateItem(opts) {\n const keys = Array.from(Object.keys(opts.payload));\n sqlExecutor.execute({\n sql: `update ${quoteId(opts.dataset)} set ${keys.map((key) => `${quoteId(key)} = ?`).join(\",\")} where id = ?`,\n parameters: [...keys.map((key) => opts.payload[key]), opts.itemId],\n });\n },\n });\n\n const handleCrdtEventApply = (event: PersistedCrdtEvent) => {\n sqlExecutor.transaction(() => {\n baseApply(event);\n });\n\n queueMicrotask(() => {\n eventTarget.dispatchEvent(\"event-applied\", event as TypedPersistedCrdtEvent<Schema>);\n });\n };\n\n const truncatedNodeId = nodeId.slice(0, 12);\n const hlc = new HLCCounter(truncatedNodeId, () => Date.now());\n\n const crdtStorage = createCrdtStorage({\n nodeId: truncatedNodeId,\n syncId,\n hlc,\n migrator: migrator,\n handleCrdtEventApply,\n transaction: (callback) => sqlExecutor.transaction(callback),\n getEventsBatch: (opts) => {\n return sqlExecutor.executeKysely((db) =>\n applyKyselyEventsBatchFilters(db.selectFrom(crdtEventsTable as \"crdtEvents\").selectAll(), {\n ...opts,\n limit: opts.limit ?? batchSize,\n }),\n ).rows;\n },\n persistEvent: (event) => {\n sqlExecutor.executeKysely((db) =>\n db\n .insertInto(crdtEventsTable as \"crdtEvents\")\n .values(event)\n .onConflict((oc) => oc.columns([\"timestamp\", \"source_node_id\"]).doNothing()),\n );\n },\n updateEvent: (syncId, event) =>\n sqlExecutor.executeKysely((db) =>\n db\n .updateTable(crdtEventsTable as \"crdtEvents\")\n .set({\n status: event.status,\n dataset: event.dataset,\n item_id: event.item_id,\n schema_version: event.schema_version,\n type: event.type,\n payload: event.payload,\n })\n .where(\"sync_id\", \"=\", syncId),\n ),\n });\n\n const remoteHandler = createDurableObjectRemoteHandler({\n bufferSize: batchSize,\n crdtStorage,\n broadcastPayload,\n });\n\n const syncDbMutator = createCrdtStorageMutator<Schema[`~mutationsSchema`]>({\n storage: crdtStorage,\n });\n\n const syncDbExecutor = sqlExecutor as unknown as KyselyExecutor<Schema[`~serverSchema`]>;\n const syncDb: ServerSyncDb<Schema> = {\n ...syncDbExecutor,\n ...syncDbMutator,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n\n return {\n syncDb,\n remoteHandler,\n };\n}\n\ntype MessageResult = { success: true; payload: string } | { success: false; error: unknown };\nexport type RemoteHandler = {\n handleMessage: (message: string) => MessageResult;\n};\n\nfunction createDurableObjectRemoteHandler({\n bufferSize = 50,\n crdtStorage,\n broadcastPayload,\n}: {\n bufferSize?: number;\n crdtStorage: CrdtStorage;\n broadcastPayload: (payload: string) => void;\n}): RemoteHandler {\n createCrdtSyncProducer({\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastPayload(\n JSON.stringify({\n type: \"events-applied\",\n newSyncId: chunk.newSyncId,\n }),\n );\n },\n });\n\n const handleMessage = (message: string): MessageResult => {\n const requestRaw = jsonSafeParse<SyncServerRequest>(message);\n\n if (!requestRaw.success) {\n return { success: false, error: requestRaw.error };\n }\n\n const requestResult = syncServerZodSchema.request.safeParse(requestRaw.data);\n\n if (!requestResult.success) {\n console.log(\"Invalid request\", requestResult.error);\n return { success: false, error: requestResult.error };\n }\n\n const request = requestResult.data;\n\n switch (request.type) {\n case \"pull-events\":\n return handlePullEvents(request);\n case \"push-events\":\n return handlePushEvents(request);\n default:\n request satisfies never;\n return { success: false, error: new Error(\"Invalid request type\") };\n }\n };\n\n const handlePullEvents = (request: ExtractSyncServerRequest<\"pull-events\">): MessageResult => {\n const batch = crdtStorage.getEventsBatch({\n limit: bufferSize,\n status: \"applied\",\n afterSyncId: request.afterSyncId,\n excludeNodeId: request.excludeNodeId,\n });\n\n const eventsPullMessage: SyncServerMessage = {\n type: \"events-pull-response\",\n requestId: request.requestId,\n data: {\n hasMore: batch.hasMore,\n nextSyncId: batch.nextSyncId,\n events: batch.events.map((x) => ({\n schema_version: x.schema_version,\n timestamp: x.timestamp,\n type: x.type,\n dataset: x.dataset,\n item_id: x.item_id,\n payload: x.payload,\n })),\n },\n };\n\n return {\n success: true,\n payload: JSON.stringify(eventsPullMessage),\n };\n };\n\n const handlePushEvents = (request: ExtractSyncServerRequest<\"push-events\">): MessageResult => {\n crdtStorage.enqueueLocalEvents(request.events, request.nodeId);\n const eventsAppliedMessage: SyncServerMessage = {\n type: \"events-push-response\",\n requestId: request.requestId,\n data: {\n ok: true,\n },\n };\n\n return {\n success: true,\n payload: JSON.stringify(eventsAppliedMessage),\n };\n };\n\n return { handleMessage };\n}\n\nfunction getLatestSyncId(executor: KyselyExecutor<any>) {\n const result = executor.executeKysely((db) =>\n db.selectFrom(\"crdt_events\").select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\")),\n );\n return result.rows[0]?.sync_id ?? 0;\n}\n\nexport const durableObjectAdapter = {\n createCrdtStorage: createDurableObjectCrdtStorage,\n};\n","import { dummyKysely } from \"@sqlite-sync/core\";\nimport type { Compilable, Kysely } from \"kysely\";\n\ntype ExecuteParams = {\n sql: string;\n parameters: readonly unknown[];\n};\n\ntype ExecuteResult<T> = {\n rows: T[];\n};\n\ntype QueryBuilderOutput<QB> = QB extends Compilable<infer O> ? O : never;\n\ntype KyselyQueryFactory<TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (\n kysely: Kysely<TDatabase>,\n) => TQuery;\n\nexport type KyselyExecutor<TDatabase> = {\n execute<TResult = unknown>(query: ExecuteParams): ExecuteResult<TResult>;\n executeKysely<TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n ): ExecuteResult<TResult>;\n transaction: (callback: (tx: Pick<KyselyExecutor<TDatabase>, \"execute\" | \"executeKysely\">) => void) => void;\n};\n\nexport function createKyselyExecutor<TDatabase>(db: DurableObjectStorage): KyselyExecutor<TDatabase> {\n const execute = <TResult = unknown>(query: ExecuteParams): ExecuteResult<TResult> => {\n const rows = db.sql.exec(query.sql, ...query.parameters).toArray();\n return { rows: rows as TResult[] };\n };\n\n const executeKysely = <TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n ): ExecuteResult<TResult> => {\n const query = factory(dummyKysely as any).compile();\n return execute(query);\n };\n\n const transaction = (callback: (tx: Pick<KyselyExecutor<TDatabase>, \"execute\" | \"executeKysely\">) => void) => {\n db.transactionSync(() => callback(executor));\n };\n\n const executor = {\n execute,\n executeKysely,\n transaction,\n };\n\n return executor;\n}\n","import { createMigrator as createBaseMigrator, createStoredValue, type Migrations } from \"@sqlite-sync/core\";\nimport type { KyselyExecutor } from \"./kysely-executor\";\n\nexport function createMigrator(\n kv: SyncKvStorage,\n sqlExecutor: KyselyExecutor<any>,\n migrations: Migrations,\n updateLogTableName?: string,\n) {\n const schemaVersion = createStoredValue<number>({\n initialValue: kv.get(\"schema-version\") ?? -1,\n saveToStorage: (val) => kv.put(\"schema-version\", val),\n });\n\n const baseMigrator = createBaseMigrator({\n migrations,\n schemaVersion,\n updateLogTableName,\n });\n\n return {\n ...baseMigrator,\n migrateDbToLatest: () => {\n baseMigrator.migrateDbToLatest({\n startTransaction: (callback) => {\n sqlExecutor.transaction(() => {\n return callback({\n execute: (sql, parameters) =>\n sqlExecutor.execute({\n sql,\n parameters,\n }),\n });\n });\n },\n });\n },\n };\n}\n\nexport type SyncDbMigrator = ReturnType<typeof createMigrator>;\n"],"mappings":"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","names":["createStoredValue","updateLogTableName","createStoredValue","syncId"]}
|
|
1
|
+
{"version":3,"sources":["../src/durable-object-adapter.ts","../src/kysely-executor.ts","../src/migrator.ts"],"sourcesContent":["import {\n baseSystemMigrations,\n type CrdtEventOrigin,\n type CrdtEventStatus,\n type CrdtEventType,\n type CrdtStorage,\n type CrdtStorageMutator,\n type CrdtUpdateLogItem,\n createCrdtStorage,\n createCrdtStorageMutator,\n createCrdtSyncProducer,\n createStoredValue,\n createSystemDbConfig,\n createTypedEventTarget,\n HLCCounter,\n jsonSafeParse,\n type PersistedCrdtEvent,\n runSystemMigrations,\n type SyncDbSchema,\n type TypedEventTarget,\n xxhash,\n} from \"@sqlite-sync/core\";\nimport {\n type ExtractSyncServerRequest,\n type SyncServerMessage,\n type SyncServerRequest,\n syncServerZodSchema,\n} from \"@sqlite-sync/core/server\";\nimport { createCrdtStorageDb, createKyselyExecutor, type KyselyExecutor } from \"./kysely-executor\";\nimport { createMigrator } from \"./migrator\";\n\nconst updateLogTableName = \"__crdt_update_log\";\n\ntype AdapterDb = {\n crdtEvents: PersistedCrdtEvent;\n [updateLogTableName]: CrdtUpdateLogItem;\n};\n\nexport type TypedPersistedCrdtEvent<Schema extends SyncDbSchema> = {\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: keyof Schema[`~mutationsSchema`];\n item_id: string;\n payload: string;\n};\n\ntype ServerSyncDbEvents<Schema extends SyncDbSchema> = {\n \"event-applied\": TypedPersistedCrdtEvent<Schema>;\n};\n\nexport type ServerSyncDb<Schema extends SyncDbSchema> = Pick<\n KyselyExecutor<Schema[`~serverSchema`]>,\n \"execute\" | \"executeKysely\"\n> &\n CrdtStorageMutator<Schema[`~mutationsSchema`]> &\n Pick<TypedEventTarget<ServerSyncDbEvents<Schema>>, \"addEventListener\" | \"removeEventListener\">;\n\nasync function createDurableObjectCrdtStorage<Schema extends SyncDbSchema>({\n storage,\n syncDbSchema,\n nodeId,\n crdtEventsTable = \"crdt_events\",\n batchSize = 50,\n broadcastPayload,\n}: {\n storage: DurableObjectStorage;\n syncDbSchema: Schema;\n nodeId: string;\n crdtEventsTable: string;\n batchSize?: number;\n broadcastPayload: (payload: string) => void;\n}): Promise<{\n syncDb: ServerSyncDb<Schema>;\n remoteHandler: RemoteHandler;\n}> {\n await xxhash.ensureLoaded();\n\n const dbConfig = createSystemDbConfig({\n eventsTableName: crdtEventsTable,\n updateLogTableName: updateLogTableName,\n });\n\n const eventTarget = createTypedEventTarget<ServerSyncDbEvents<Schema>>();\n const sqlExecutor = createKyselyExecutor<AdapterDb>(storage);\n const crdtStorageDb = createCrdtStorageDb(sqlExecutor);\n\n runSystemMigrations({\n migrations: baseSystemMigrations,\n version: createStoredValue<number>({\n initialValue: storage.kv.get(\"internal-schema-version\") ?? -1,\n saveToStorage: (val) => storage.kv.put(\"internal-schema-version\", val),\n }),\n dbConfig,\n execute: (sql) => sqlExecutor.execute({ sql, parameters: [] }),\n transaction: (callback) => sqlExecutor.transaction(callback),\n });\n\n const migrator = createMigrator(\n storage.kv,\n sqlExecutor,\n syncDbSchema.migrations,\n dbConfig.updateLogTable.fullIdentifier,\n );\n migrator.migrateDbToLatest();\n\n const truncatedNodeId = nodeId.slice(0, 12);\n const hlc = new HLCCounter(truncatedNodeId, () => Date.now());\n\n const crdtStorage = createCrdtStorage({\n nodeId: truncatedNodeId,\n initialLocalSyncId: getLatestSyncId(sqlExecutor),\n hlc,\n migrator: migrator,\n db: crdtStorageDb,\n dbConfig,\n eventHlcAccumulator: createStoredValue<string>({\n initialValue: storage.kv.get(\"crdt.consistency.event_hlc_sum.v3\") ?? \"\",\n saveToStorage: (val) => storage.kv.put(\"crdt.consistency.event_hlc_sum.v3\", val),\n }),\n onEventApplied: (event) => {\n queueMicrotask(() => {\n eventTarget.dispatchEvent(\"event-applied\", event as TypedPersistedCrdtEvent<Schema>);\n });\n },\n });\n\n const remoteHandler = createDurableObjectRemoteHandler({\n bufferSize: batchSize,\n crdtStorage,\n broadcastPayload,\n });\n\n const syncDbMutator = createCrdtStorageMutator<Schema[`~mutationsSchema`]>({\n storage: crdtStorage,\n });\n\n const syncDbExecutor = sqlExecutor as unknown as KyselyExecutor<Schema[`~serverSchema`]>;\n const syncDb: ServerSyncDb<Schema> = {\n ...syncDbExecutor,\n ...syncDbMutator,\n addEventListener: eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener,\n };\n\n return {\n syncDb,\n remoteHandler,\n };\n}\n\ntype MessageResult = { success: true; payload: string } | { success: false; error: unknown };\nexport type RemoteHandler = {\n handleMessage: (message: string) => MessageResult;\n};\n\nfunction createDurableObjectRemoteHandler({\n bufferSize = 50,\n crdtStorage,\n broadcastPayload,\n}: {\n bufferSize?: number;\n crdtStorage: CrdtStorage;\n broadcastPayload: (payload: string) => void;\n}): RemoteHandler {\n createCrdtSyncProducer({\n storage: crdtStorage,\n broadcastEvents: (chunk) => {\n broadcastPayload(\n JSON.stringify({\n type: \"events-applied\",\n newSyncId: chunk.newSyncId,\n eventHlcSum: chunk.eventHlcSum,\n }),\n );\n },\n });\n\n const handleMessage = (message: string): MessageResult => {\n const requestRaw = jsonSafeParse<SyncServerRequest>(message);\n\n if (!requestRaw.success) {\n return { success: false, error: requestRaw.error };\n }\n\n const requestResult = syncServerZodSchema.request.safeParse(requestRaw.data);\n\n if (!requestResult.success) {\n console.log(\"Invalid request\", requestResult.error);\n return { success: false, error: requestResult.error };\n }\n\n const request = requestResult.data;\n\n switch (request.type) {\n case \"pull-events\":\n return handlePullEvents(request);\n case \"push-events\":\n return handlePushEvents(request);\n default:\n request satisfies never;\n return { success: false, error: new Error(\"Invalid request type\") };\n }\n };\n\n const handlePullEvents = (request: ExtractSyncServerRequest<\"pull-events\">): MessageResult => {\n const batch = crdtStorage.getEventsBatch({\n limit: bufferSize,\n status: \"applied\",\n afterSyncId: request.afterSyncId,\n excludeNodeId: request.excludeNodeId ?? \"\",\n });\n\n const eventsPullMessage: SyncServerMessage = {\n type: \"events-pull-response\",\n requestId: request.requestId,\n data: {\n hasMore: batch.hasMore,\n nextSyncId: batch.nextSyncId,\n events: batch.events.map((x) => ({\n schema_version: x.schema_version,\n timestamp: x.timestamp,\n type: x.type,\n dataset: x.dataset,\n item_id: x.item_id,\n payload: x.payload,\n })),\n },\n };\n\n return {\n success: true,\n payload: JSON.stringify(eventsPullMessage),\n };\n };\n\n const handlePushEvents = (request: ExtractSyncServerRequest<\"push-events\">): MessageResult => {\n const { beforeSyncId, afterSyncId } = crdtStorage.enqueueLocalEvents(request.events, request.nodeId);\n const eventsAppliedMessage: SyncServerMessage = {\n type: \"events-push-response\",\n requestId: request.requestId,\n data: {\n ok: true,\n beforeSyncId,\n afterSyncId,\n },\n };\n\n return {\n success: true,\n payload: JSON.stringify(eventsAppliedMessage),\n };\n };\n\n return { handleMessage };\n}\n\nfunction getLatestSyncId(executor: KyselyExecutor<any>) {\n const result = executor.executeKysely((db) =>\n db.selectFrom(\"crdt_events\").select((eb) => eb.fn.max(\"sync_id\").as(\"sync_id\")),\n );\n return result.rows[0]?.sync_id ?? 0;\n}\n\nexport const durableObjectAdapter = {\n createCrdtStorage: createDurableObjectCrdtStorage,\n};\n","import { dummyKysely, type InternalSQLiteWrapper, type KyselyStatementFactory } from \"@sqlite-sync/core\";\nimport type { Compilable, Kysely } from \"kysely\";\n\ntype ExecuteParams = {\n sql: string;\n parameters: readonly unknown[];\n};\n\ntype ExecuteResult<T> = {\n rows: T[];\n};\n\ntype QueryBuilderOutput<QB> = QB extends Compilable<infer O> ? O : never;\n\ntype KyselyQueryFactory<TDatabase, TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>> = (\n kysely: Kysely<TDatabase>,\n) => TQuery;\n\nexport type KyselyExecutor<TDatabase> = {\n execute<TResult = unknown>(query: ExecuteParams): ExecuteResult<TResult>;\n executeKysely<TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n ): ExecuteResult<TResult>;\n transaction: (callback: (tx: Pick<KyselyExecutor<TDatabase>, \"execute\" | \"executeKysely\">) => void) => void;\n};\n\nexport function createKyselyExecutor<TDatabase>(db: DurableObjectStorage): KyselyExecutor<TDatabase> {\n const execute = <TResult = unknown>(query: ExecuteParams): ExecuteResult<TResult> => {\n const rows = db.sql.exec(query.sql, ...query.parameters).toArray();\n return { rows: rows as TResult[] };\n };\n\n const executeKysely = <TQuery extends Compilable<TResult>, TResult = QueryBuilderOutput<TQuery>>(\n factory: KyselyQueryFactory<TDatabase, TQuery, TResult>,\n ): ExecuteResult<TResult> => {\n const query = factory(dummyKysely as any).compile();\n return execute(query);\n };\n\n const transaction = (callback: (tx: Pick<KyselyExecutor<TDatabase>, \"execute\" | \"executeKysely\">) => void) => {\n db.transactionSync(() => callback(executor));\n };\n\n const executor = {\n execute,\n executeKysely,\n transaction,\n };\n\n return executor;\n}\n\nexport function createCrdtStorageDb(executor: KyselyExecutor<any>): InternalSQLiteWrapper<any> {\n const wrapper: Omit<InternalSQLiteWrapper<any>, \"executeTransaction\"> = {\n executePreparedRaw: <TParams extends unknown[], TResult>({\n sql,\n params,\n }: {\n sql: string;\n params?: TParams | undefined;\n }) =>\n executor.execute<TResult>({\n sql,\n parameters: params ?? [],\n }).rows,\n\n executePrepared: <\n TParams extends Record<string, unknown>,\n TQuery extends Compilable<TResult>,\n TResult = QueryBuilderOutput<TQuery>,\n >(\n _: string,\n params: TParams,\n factory: KyselyStatementFactory<TParams, any, TQuery, TResult>,\n ) => {\n const query = factory(dummyKysely as any, (key) => key as any).compile();\n const parameters = query.parameters.map((param) => params[param as keyof TParams]);\n const result = executor.execute<TResult>({\n sql: query.sql,\n parameters,\n });\n return result.rows;\n },\n };\n\n return {\n ...wrapper,\n executeTransaction: (callback) =>\n executor.transaction(() => {\n callback(wrapper);\n }),\n };\n}\n","import { createMigrator as createBaseMigrator, createStoredValue, type Migrations } from \"@sqlite-sync/core\";\nimport type { KyselyExecutor } from \"./kysely-executor\";\n\nexport function createMigrator(\n kv: SyncKvStorage,\n sqlExecutor: KyselyExecutor<any>,\n migrations: Migrations,\n updateLogTableName?: string,\n) {\n const schemaVersion = createStoredValue<number>({\n initialValue: kv.get(\"schema-version\") ?? -1,\n saveToStorage: (val) => kv.put(\"schema-version\", val),\n });\n\n const baseMigrator = createBaseMigrator({\n migrations,\n schemaVersion,\n updateLogTableName,\n });\n\n return {\n ...baseMigrator,\n migrateDbToLatest: () => {\n baseMigrator.migrateDbToLatest({\n startTransaction: (callback) => {\n sqlExecutor.transaction(() => {\n return callback({\n execute: (sql, parameters) =>\n sqlExecutor.execute({\n sql,\n parameters,\n }),\n });\n });\n },\n });\n },\n };\n}\n\nexport type SyncDbMigrator = ReturnType<typeof createMigrator>;\n"],"mappings":"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","names":["createStoredValue","updateLogTableName","createStoredValue"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlite-sync/cloudflare",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Cloudflare utilities for @sqlite-sync/core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@sqlite-sync/core": "0.
|
|
33
|
+
"@sqlite-sync/core": "0.2.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@cloudflare/workers-types": "^4.0.0",
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup",
|
|
47
47
|
"dev": "tsup --watch",
|
|
48
|
-
"typecheck": "
|
|
48
|
+
"typecheck": "tsgo --noEmit"
|
|
49
49
|
}
|
|
50
50
|
}
|