@foretag/tanstack-db-surrealdb 0.1.14 → 0.1.16
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/README.md +1 -1
- package/dist/index.js +99 -39
- package/dist/index.mjs +99 -39
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Note: Please note this Software is Pre-release pending testing and not yet recommended for production use.
|
|
4
4
|
|
|
5
|
-
Add Offline / Local First Caching & Syncing to your SurrealDB app with TanstackDB and Loro (CRDTs). For SurrealDB v3 and SurrealDB JS SDK v2.
|
|
5
|
+
Add Offline / Local First Caching & Syncing to your SurrealDB app with TanstackDB and Loro (CRDTs). For SurrealDB v3.alpha-13 and SurrealDB JS SDK v2 (and above).
|
|
6
6
|
|
|
7
7
|
- Local / Offline first applications with TanstackDB and Loro
|
|
8
8
|
- High performance with Low resource consumption
|
package/dist/index.js
CHANGED
|
@@ -27,28 +27,51 @@ var import_surrealdb2 = require("surrealdb");
|
|
|
27
27
|
|
|
28
28
|
// src/table.ts
|
|
29
29
|
var import_surrealdb = require("surrealdb");
|
|
30
|
-
function manageTable(db, { name, ...args }) {
|
|
30
|
+
function manageTable(db, useLoro, { name, ...args }) {
|
|
31
31
|
const fields = args.fields ?? "*";
|
|
32
32
|
const listAll = async () => {
|
|
33
|
-
|
|
33
|
+
let q = db.select(new import_surrealdb.Table(name));
|
|
34
|
+
if (args.where) q = q.where(args.where);
|
|
35
|
+
return await q.fields(...fields);
|
|
34
36
|
};
|
|
35
37
|
const listActive = async () => {
|
|
38
|
+
if (!useLoro) return listAll();
|
|
36
39
|
return await db.select(new import_surrealdb.Table(name)).where((0, import_surrealdb.and)(args.where, (0, import_surrealdb.eq)("sync_deleted", false))).fields(...fields);
|
|
37
40
|
};
|
|
38
41
|
const create = async (data) => {
|
|
39
42
|
await db.create(new import_surrealdb.Table(name)).content(data);
|
|
40
43
|
};
|
|
41
44
|
const update = async (id, data) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if (!useLoro) {
|
|
46
|
+
await db.update(id).merge({
|
|
47
|
+
...data
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
await db.update(id).merge({
|
|
53
|
+
...data,
|
|
54
|
+
sync_deleted: false,
|
|
55
|
+
updated_at: Date.now()
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.warn(
|
|
59
|
+
`Please ensure the table ${name} has sync_deleted and updated_at fields defined`
|
|
60
|
+
);
|
|
61
|
+
console.error(
|
|
62
|
+
"Failed to update record with Loro (CRDTs) with: ",
|
|
63
|
+
error
|
|
64
|
+
);
|
|
65
|
+
}
|
|
47
66
|
};
|
|
48
67
|
const remove = async (id) => {
|
|
49
68
|
await db.delete(id);
|
|
50
69
|
};
|
|
51
70
|
const softDelete = async (id) => {
|
|
71
|
+
if (!useLoro) {
|
|
72
|
+
await db.delete(id);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
52
75
|
await db.upsert(id).merge({
|
|
53
76
|
sync_deleted: true,
|
|
54
77
|
updated_at: Date.now()
|
|
@@ -58,6 +81,7 @@ function manageTable(db, { name, ...args }) {
|
|
|
58
81
|
let killed = false;
|
|
59
82
|
let live;
|
|
60
83
|
const on = ({ action, value }) => {
|
|
84
|
+
console.debug("[surreal live]", name, action, value);
|
|
61
85
|
if (action === "KILLED") return;
|
|
62
86
|
if (action === "CREATE") cb({ type: "insert", row: value });
|
|
63
87
|
else if (action === "UPDATE")
|
|
@@ -120,11 +144,17 @@ function surrealCollectionOptions({
|
|
|
120
144
|
loro?.doc?.commit?.();
|
|
121
145
|
};
|
|
122
146
|
const pushQueue = [];
|
|
123
|
-
const enqueuePush = (op) =>
|
|
147
|
+
const enqueuePush = (op) => {
|
|
148
|
+
if (!useLoro) return;
|
|
149
|
+
pushQueue.push(op);
|
|
150
|
+
};
|
|
124
151
|
const flushPushQueue = async () => {
|
|
152
|
+
if (!useLoro) return;
|
|
125
153
|
const ops = pushQueue.splice(0, pushQueue.length);
|
|
126
154
|
for (const op of ops) {
|
|
127
|
-
if (op.kind === "create")
|
|
155
|
+
if (op.kind === "create") {
|
|
156
|
+
await table.create(op.row);
|
|
157
|
+
}
|
|
128
158
|
if (op.kind === "update") {
|
|
129
159
|
const rid = new import_surrealdb2.RecordId(
|
|
130
160
|
config.table.name,
|
|
@@ -140,13 +170,17 @@ function surrealCollectionOptions({
|
|
|
140
170
|
};
|
|
141
171
|
const newer = (a, b) => (a?.getTime() ?? -1) > (b?.getTime() ?? -1);
|
|
142
172
|
const reconcileBoot = (serverRows, write) => {
|
|
143
|
-
|
|
173
|
+
if (!useLoro) {
|
|
174
|
+
diffAndEmit(serverRows, write);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const localRows = loroToArray();
|
|
144
178
|
const serverById = new Map(serverRows.map((r) => [getKey(r), r]));
|
|
145
179
|
const localById = new Map(localRows.map((r) => [getKey(r), r]));
|
|
146
180
|
const ids = /* @__PURE__ */ new Set([...serverById.keys(), ...localById.keys()]);
|
|
147
181
|
const current = [];
|
|
148
182
|
const applyLocal = (row) => {
|
|
149
|
-
if (!
|
|
183
|
+
if (!row) return;
|
|
150
184
|
if (row.sync_deleted) loroRemove(getKey(row));
|
|
151
185
|
else loroPut(row);
|
|
152
186
|
};
|
|
@@ -154,18 +188,22 @@ function surrealCollectionOptions({
|
|
|
154
188
|
const s = serverById.get(id2);
|
|
155
189
|
const l = localById.get(id2);
|
|
156
190
|
if (s && l) {
|
|
157
|
-
|
|
191
|
+
const sDeleted = s.sync_deleted ?? false;
|
|
192
|
+
const lDeleted = l.sync_deleted ?? false;
|
|
193
|
+
const sUpdated = s.updated_at;
|
|
194
|
+
const lUpdated = l.updated_at;
|
|
195
|
+
if (sDeleted && lDeleted) {
|
|
158
196
|
applyLocal(s);
|
|
159
197
|
current.push(s);
|
|
160
|
-
} else if (
|
|
198
|
+
} else if (sDeleted && !lDeleted) {
|
|
161
199
|
applyLocal(s);
|
|
162
200
|
current.push(s);
|
|
163
|
-
} else if (!
|
|
164
|
-
if (newer(
|
|
201
|
+
} else if (!sDeleted && lDeleted) {
|
|
202
|
+
if (newer(lUpdated, sUpdated)) {
|
|
165
203
|
enqueuePush({
|
|
166
204
|
kind: "delete",
|
|
167
205
|
id: id2,
|
|
168
|
-
updated_at:
|
|
206
|
+
updated_at: lUpdated ?? /* @__PURE__ */ new Date()
|
|
169
207
|
});
|
|
170
208
|
applyLocal(l);
|
|
171
209
|
current.push(l);
|
|
@@ -174,7 +212,7 @@ function surrealCollectionOptions({
|
|
|
174
212
|
current.push(s);
|
|
175
213
|
}
|
|
176
214
|
} else {
|
|
177
|
-
if (newer(
|
|
215
|
+
if (newer(lUpdated, sUpdated)) {
|
|
178
216
|
enqueuePush({ kind: "update", row: l });
|
|
179
217
|
applyLocal(l);
|
|
180
218
|
current.push(l);
|
|
@@ -187,11 +225,13 @@ function surrealCollectionOptions({
|
|
|
187
225
|
applyLocal(s);
|
|
188
226
|
current.push(s);
|
|
189
227
|
} else if (!s && l) {
|
|
190
|
-
|
|
228
|
+
const lDeleted = l.sync_deleted ?? false;
|
|
229
|
+
const lUpdated = l.updated_at;
|
|
230
|
+
if (lDeleted) {
|
|
191
231
|
enqueuePush({
|
|
192
232
|
kind: "delete",
|
|
193
233
|
id: id2,
|
|
194
|
-
updated_at:
|
|
234
|
+
updated_at: lUpdated ?? /* @__PURE__ */ new Date()
|
|
195
235
|
});
|
|
196
236
|
applyLocal(l);
|
|
197
237
|
current.push(l);
|
|
@@ -206,15 +246,24 @@ function surrealCollectionOptions({
|
|
|
206
246
|
};
|
|
207
247
|
let prevById = /* @__PURE__ */ new Map();
|
|
208
248
|
const buildMap = (rows) => new Map(rows.map((r) => [getKey(r), r]));
|
|
209
|
-
const same = (a, b) =>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
249
|
+
const same = (a, b) => {
|
|
250
|
+
if (useLoro) {
|
|
251
|
+
const aUpdated = a.updated_at;
|
|
252
|
+
const bUpdated = b.updated_at;
|
|
253
|
+
const aDeleted = a.sync_deleted ?? false;
|
|
254
|
+
const bDeleted = b.sync_deleted ?? false;
|
|
255
|
+
return aDeleted === bDeleted && (aUpdated?.getTime() ?? 0) === (bUpdated?.getTime() ?? 0) && JSON.stringify({
|
|
256
|
+
...a,
|
|
257
|
+
updated_at: void 0,
|
|
258
|
+
sync_deleted: void 0
|
|
259
|
+
}) === JSON.stringify({
|
|
260
|
+
...b,
|
|
261
|
+
updated_at: void 0,
|
|
262
|
+
sync_deleted: void 0
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
266
|
+
};
|
|
218
267
|
const diffAndEmit = (currentRows, write) => {
|
|
219
268
|
const currById = buildMap(currentRows);
|
|
220
269
|
for (const [id2, row] of currById) {
|
|
@@ -232,7 +281,8 @@ function surrealCollectionOptions({
|
|
|
232
281
|
}
|
|
233
282
|
prevById = currById;
|
|
234
283
|
};
|
|
235
|
-
const table = manageTable(db, config.table);
|
|
284
|
+
const table = manageTable(db, useLoro, config.table);
|
|
285
|
+
const now = () => /* @__PURE__ */ new Date();
|
|
236
286
|
const sync = ({
|
|
237
287
|
begin,
|
|
238
288
|
write,
|
|
@@ -242,15 +292,18 @@ function surrealCollectionOptions({
|
|
|
242
292
|
let offLive = null;
|
|
243
293
|
const makeTombstone = (id2) => ({
|
|
244
294
|
id: new import_surrealdb2.RecordId(config.table.name, id2).toString(),
|
|
245
|
-
updated_at:
|
|
295
|
+
updated_at: now(),
|
|
246
296
|
sync_deleted: true
|
|
247
297
|
});
|
|
248
298
|
const start = async () => {
|
|
249
299
|
try {
|
|
250
300
|
const serverRows = await table.listAll();
|
|
251
301
|
begin();
|
|
252
|
-
if (useLoro)
|
|
253
|
-
|
|
302
|
+
if (useLoro) {
|
|
303
|
+
reconcileBoot(serverRows, write);
|
|
304
|
+
} else {
|
|
305
|
+
diffAndEmit(serverRows, write);
|
|
306
|
+
}
|
|
254
307
|
commit();
|
|
255
308
|
markReady();
|
|
256
309
|
await flushPushQueue();
|
|
@@ -259,7 +312,8 @@ function surrealCollectionOptions({
|
|
|
259
312
|
try {
|
|
260
313
|
if (evt.type === "insert" || evt.type === "update") {
|
|
261
314
|
const row = evt.row;
|
|
262
|
-
|
|
315
|
+
const deleted = useLoro ? row.sync_deleted ?? false : false;
|
|
316
|
+
if (deleted) {
|
|
263
317
|
if (useLoro) loroRemove(getKey(row));
|
|
264
318
|
const prev = prevById.get(getKey(row)) ?? makeTombstone(getKey(row));
|
|
265
319
|
write({ type: "delete", value: prev });
|
|
@@ -294,16 +348,16 @@ function surrealCollectionOptions({
|
|
|
294
348
|
if (offLive) offLive();
|
|
295
349
|
};
|
|
296
350
|
};
|
|
297
|
-
const now = () => /* @__PURE__ */ new Date();
|
|
298
351
|
const onInsert = async (p) => {
|
|
299
352
|
const resultRows = [];
|
|
300
353
|
for (const m of p.transaction.mutations) {
|
|
301
354
|
if (m.type !== "insert") continue;
|
|
302
|
-
const
|
|
303
|
-
|
|
355
|
+
const base = { ...m.modified };
|
|
356
|
+
const row = useLoro ? {
|
|
357
|
+
...base,
|
|
304
358
|
updated_at: now(),
|
|
305
359
|
sync_deleted: false
|
|
306
|
-
};
|
|
360
|
+
} : base;
|
|
307
361
|
if (useLoro) loroPut(row);
|
|
308
362
|
await table.create(row);
|
|
309
363
|
resultRows.push(row);
|
|
@@ -315,7 +369,11 @@ function surrealCollectionOptions({
|
|
|
315
369
|
for (const m of p.transaction.mutations) {
|
|
316
370
|
if (m.type !== "update") continue;
|
|
317
371
|
const id2 = m.key;
|
|
318
|
-
const
|
|
372
|
+
const base = { ...m.modified, id: id2 };
|
|
373
|
+
const merged = useLoro ? {
|
|
374
|
+
...base,
|
|
375
|
+
updated_at: now()
|
|
376
|
+
} : base;
|
|
319
377
|
if (useLoro) loroPut(merged);
|
|
320
378
|
const rid = new import_surrealdb2.RecordId(config.table.name, keyOf(id2));
|
|
321
379
|
await table.update(rid, merged);
|
|
@@ -328,7 +386,9 @@ function surrealCollectionOptions({
|
|
|
328
386
|
for (const m of p.transaction.mutations) {
|
|
329
387
|
if (m.type !== "delete") continue;
|
|
330
388
|
const id2 = m.key;
|
|
331
|
-
if (useLoro)
|
|
389
|
+
if (useLoro) {
|
|
390
|
+
loroRemove(keyOf(id2));
|
|
391
|
+
}
|
|
332
392
|
await table.softDelete(new import_surrealdb2.RecordId(config.table.name, keyOf(id2)));
|
|
333
393
|
}
|
|
334
394
|
return resultRows;
|
package/dist/index.mjs
CHANGED
|
@@ -9,28 +9,51 @@ import {
|
|
|
9
9
|
Features,
|
|
10
10
|
Table
|
|
11
11
|
} from "surrealdb";
|
|
12
|
-
function manageTable(db, { name, ...args }) {
|
|
12
|
+
function manageTable(db, useLoro, { name, ...args }) {
|
|
13
13
|
const fields = args.fields ?? "*";
|
|
14
14
|
const listAll = async () => {
|
|
15
|
-
|
|
15
|
+
let q = db.select(new Table(name));
|
|
16
|
+
if (args.where) q = q.where(args.where);
|
|
17
|
+
return await q.fields(...fields);
|
|
16
18
|
};
|
|
17
19
|
const listActive = async () => {
|
|
20
|
+
if (!useLoro) return listAll();
|
|
18
21
|
return await db.select(new Table(name)).where(and(args.where, eq("sync_deleted", false))).fields(...fields);
|
|
19
22
|
};
|
|
20
23
|
const create = async (data) => {
|
|
21
24
|
await db.create(new Table(name)).content(data);
|
|
22
25
|
};
|
|
23
26
|
const update = async (id, data) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (!useLoro) {
|
|
28
|
+
await db.update(id).merge({
|
|
29
|
+
...data
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
await db.update(id).merge({
|
|
35
|
+
...data,
|
|
36
|
+
sync_deleted: false,
|
|
37
|
+
updated_at: Date.now()
|
|
38
|
+
});
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(
|
|
41
|
+
`Please ensure the table ${name} has sync_deleted and updated_at fields defined`
|
|
42
|
+
);
|
|
43
|
+
console.error(
|
|
44
|
+
"Failed to update record with Loro (CRDTs) with: ",
|
|
45
|
+
error
|
|
46
|
+
);
|
|
47
|
+
}
|
|
29
48
|
};
|
|
30
49
|
const remove = async (id) => {
|
|
31
50
|
await db.delete(id);
|
|
32
51
|
};
|
|
33
52
|
const softDelete = async (id) => {
|
|
53
|
+
if (!useLoro) {
|
|
54
|
+
await db.delete(id);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
34
57
|
await db.upsert(id).merge({
|
|
35
58
|
sync_deleted: true,
|
|
36
59
|
updated_at: Date.now()
|
|
@@ -40,6 +63,7 @@ function manageTable(db, { name, ...args }) {
|
|
|
40
63
|
let killed = false;
|
|
41
64
|
let live;
|
|
42
65
|
const on = ({ action, value }) => {
|
|
66
|
+
console.debug("[surreal live]", name, action, value);
|
|
43
67
|
if (action === "KILLED") return;
|
|
44
68
|
if (action === "CREATE") cb({ type: "insert", row: value });
|
|
45
69
|
else if (action === "UPDATE")
|
|
@@ -102,11 +126,17 @@ function surrealCollectionOptions({
|
|
|
102
126
|
loro?.doc?.commit?.();
|
|
103
127
|
};
|
|
104
128
|
const pushQueue = [];
|
|
105
|
-
const enqueuePush = (op) =>
|
|
129
|
+
const enqueuePush = (op) => {
|
|
130
|
+
if (!useLoro) return;
|
|
131
|
+
pushQueue.push(op);
|
|
132
|
+
};
|
|
106
133
|
const flushPushQueue = async () => {
|
|
134
|
+
if (!useLoro) return;
|
|
107
135
|
const ops = pushQueue.splice(0, pushQueue.length);
|
|
108
136
|
for (const op of ops) {
|
|
109
|
-
if (op.kind === "create")
|
|
137
|
+
if (op.kind === "create") {
|
|
138
|
+
await table.create(op.row);
|
|
139
|
+
}
|
|
110
140
|
if (op.kind === "update") {
|
|
111
141
|
const rid = new RecordId(
|
|
112
142
|
config.table.name,
|
|
@@ -122,13 +152,17 @@ function surrealCollectionOptions({
|
|
|
122
152
|
};
|
|
123
153
|
const newer = (a, b) => (a?.getTime() ?? -1) > (b?.getTime() ?? -1);
|
|
124
154
|
const reconcileBoot = (serverRows, write) => {
|
|
125
|
-
|
|
155
|
+
if (!useLoro) {
|
|
156
|
+
diffAndEmit(serverRows, write);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const localRows = loroToArray();
|
|
126
160
|
const serverById = new Map(serverRows.map((r) => [getKey(r), r]));
|
|
127
161
|
const localById = new Map(localRows.map((r) => [getKey(r), r]));
|
|
128
162
|
const ids = /* @__PURE__ */ new Set([...serverById.keys(), ...localById.keys()]);
|
|
129
163
|
const current = [];
|
|
130
164
|
const applyLocal = (row) => {
|
|
131
|
-
if (!
|
|
165
|
+
if (!row) return;
|
|
132
166
|
if (row.sync_deleted) loroRemove(getKey(row));
|
|
133
167
|
else loroPut(row);
|
|
134
168
|
};
|
|
@@ -136,18 +170,22 @@ function surrealCollectionOptions({
|
|
|
136
170
|
const s = serverById.get(id2);
|
|
137
171
|
const l = localById.get(id2);
|
|
138
172
|
if (s && l) {
|
|
139
|
-
|
|
173
|
+
const sDeleted = s.sync_deleted ?? false;
|
|
174
|
+
const lDeleted = l.sync_deleted ?? false;
|
|
175
|
+
const sUpdated = s.updated_at;
|
|
176
|
+
const lUpdated = l.updated_at;
|
|
177
|
+
if (sDeleted && lDeleted) {
|
|
140
178
|
applyLocal(s);
|
|
141
179
|
current.push(s);
|
|
142
|
-
} else if (
|
|
180
|
+
} else if (sDeleted && !lDeleted) {
|
|
143
181
|
applyLocal(s);
|
|
144
182
|
current.push(s);
|
|
145
|
-
} else if (!
|
|
146
|
-
if (newer(
|
|
183
|
+
} else if (!sDeleted && lDeleted) {
|
|
184
|
+
if (newer(lUpdated, sUpdated)) {
|
|
147
185
|
enqueuePush({
|
|
148
186
|
kind: "delete",
|
|
149
187
|
id: id2,
|
|
150
|
-
updated_at:
|
|
188
|
+
updated_at: lUpdated ?? /* @__PURE__ */ new Date()
|
|
151
189
|
});
|
|
152
190
|
applyLocal(l);
|
|
153
191
|
current.push(l);
|
|
@@ -156,7 +194,7 @@ function surrealCollectionOptions({
|
|
|
156
194
|
current.push(s);
|
|
157
195
|
}
|
|
158
196
|
} else {
|
|
159
|
-
if (newer(
|
|
197
|
+
if (newer(lUpdated, sUpdated)) {
|
|
160
198
|
enqueuePush({ kind: "update", row: l });
|
|
161
199
|
applyLocal(l);
|
|
162
200
|
current.push(l);
|
|
@@ -169,11 +207,13 @@ function surrealCollectionOptions({
|
|
|
169
207
|
applyLocal(s);
|
|
170
208
|
current.push(s);
|
|
171
209
|
} else if (!s && l) {
|
|
172
|
-
|
|
210
|
+
const lDeleted = l.sync_deleted ?? false;
|
|
211
|
+
const lUpdated = l.updated_at;
|
|
212
|
+
if (lDeleted) {
|
|
173
213
|
enqueuePush({
|
|
174
214
|
kind: "delete",
|
|
175
215
|
id: id2,
|
|
176
|
-
updated_at:
|
|
216
|
+
updated_at: lUpdated ?? /* @__PURE__ */ new Date()
|
|
177
217
|
});
|
|
178
218
|
applyLocal(l);
|
|
179
219
|
current.push(l);
|
|
@@ -188,15 +228,24 @@ function surrealCollectionOptions({
|
|
|
188
228
|
};
|
|
189
229
|
let prevById = /* @__PURE__ */ new Map();
|
|
190
230
|
const buildMap = (rows) => new Map(rows.map((r) => [getKey(r), r]));
|
|
191
|
-
const same = (a, b) =>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
231
|
+
const same = (a, b) => {
|
|
232
|
+
if (useLoro) {
|
|
233
|
+
const aUpdated = a.updated_at;
|
|
234
|
+
const bUpdated = b.updated_at;
|
|
235
|
+
const aDeleted = a.sync_deleted ?? false;
|
|
236
|
+
const bDeleted = b.sync_deleted ?? false;
|
|
237
|
+
return aDeleted === bDeleted && (aUpdated?.getTime() ?? 0) === (bUpdated?.getTime() ?? 0) && JSON.stringify({
|
|
238
|
+
...a,
|
|
239
|
+
updated_at: void 0,
|
|
240
|
+
sync_deleted: void 0
|
|
241
|
+
}) === JSON.stringify({
|
|
242
|
+
...b,
|
|
243
|
+
updated_at: void 0,
|
|
244
|
+
sync_deleted: void 0
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
248
|
+
};
|
|
200
249
|
const diffAndEmit = (currentRows, write) => {
|
|
201
250
|
const currById = buildMap(currentRows);
|
|
202
251
|
for (const [id2, row] of currById) {
|
|
@@ -214,7 +263,8 @@ function surrealCollectionOptions({
|
|
|
214
263
|
}
|
|
215
264
|
prevById = currById;
|
|
216
265
|
};
|
|
217
|
-
const table = manageTable(db, config.table);
|
|
266
|
+
const table = manageTable(db, useLoro, config.table);
|
|
267
|
+
const now = () => /* @__PURE__ */ new Date();
|
|
218
268
|
const sync = ({
|
|
219
269
|
begin,
|
|
220
270
|
write,
|
|
@@ -224,15 +274,18 @@ function surrealCollectionOptions({
|
|
|
224
274
|
let offLive = null;
|
|
225
275
|
const makeTombstone = (id2) => ({
|
|
226
276
|
id: new RecordId(config.table.name, id2).toString(),
|
|
227
|
-
updated_at:
|
|
277
|
+
updated_at: now(),
|
|
228
278
|
sync_deleted: true
|
|
229
279
|
});
|
|
230
280
|
const start = async () => {
|
|
231
281
|
try {
|
|
232
282
|
const serverRows = await table.listAll();
|
|
233
283
|
begin();
|
|
234
|
-
if (useLoro)
|
|
235
|
-
|
|
284
|
+
if (useLoro) {
|
|
285
|
+
reconcileBoot(serverRows, write);
|
|
286
|
+
} else {
|
|
287
|
+
diffAndEmit(serverRows, write);
|
|
288
|
+
}
|
|
236
289
|
commit();
|
|
237
290
|
markReady();
|
|
238
291
|
await flushPushQueue();
|
|
@@ -241,7 +294,8 @@ function surrealCollectionOptions({
|
|
|
241
294
|
try {
|
|
242
295
|
if (evt.type === "insert" || evt.type === "update") {
|
|
243
296
|
const row = evt.row;
|
|
244
|
-
|
|
297
|
+
const deleted = useLoro ? row.sync_deleted ?? false : false;
|
|
298
|
+
if (deleted) {
|
|
245
299
|
if (useLoro) loroRemove(getKey(row));
|
|
246
300
|
const prev = prevById.get(getKey(row)) ?? makeTombstone(getKey(row));
|
|
247
301
|
write({ type: "delete", value: prev });
|
|
@@ -276,16 +330,16 @@ function surrealCollectionOptions({
|
|
|
276
330
|
if (offLive) offLive();
|
|
277
331
|
};
|
|
278
332
|
};
|
|
279
|
-
const now = () => /* @__PURE__ */ new Date();
|
|
280
333
|
const onInsert = async (p) => {
|
|
281
334
|
const resultRows = [];
|
|
282
335
|
for (const m of p.transaction.mutations) {
|
|
283
336
|
if (m.type !== "insert") continue;
|
|
284
|
-
const
|
|
285
|
-
|
|
337
|
+
const base = { ...m.modified };
|
|
338
|
+
const row = useLoro ? {
|
|
339
|
+
...base,
|
|
286
340
|
updated_at: now(),
|
|
287
341
|
sync_deleted: false
|
|
288
|
-
};
|
|
342
|
+
} : base;
|
|
289
343
|
if (useLoro) loroPut(row);
|
|
290
344
|
await table.create(row);
|
|
291
345
|
resultRows.push(row);
|
|
@@ -297,7 +351,11 @@ function surrealCollectionOptions({
|
|
|
297
351
|
for (const m of p.transaction.mutations) {
|
|
298
352
|
if (m.type !== "update") continue;
|
|
299
353
|
const id2 = m.key;
|
|
300
|
-
const
|
|
354
|
+
const base = { ...m.modified, id: id2 };
|
|
355
|
+
const merged = useLoro ? {
|
|
356
|
+
...base,
|
|
357
|
+
updated_at: now()
|
|
358
|
+
} : base;
|
|
301
359
|
if (useLoro) loroPut(merged);
|
|
302
360
|
const rid = new RecordId(config.table.name, keyOf(id2));
|
|
303
361
|
await table.update(rid, merged);
|
|
@@ -310,7 +368,9 @@ function surrealCollectionOptions({
|
|
|
310
368
|
for (const m of p.transaction.mutations) {
|
|
311
369
|
if (m.type !== "delete") continue;
|
|
312
370
|
const id2 = m.key;
|
|
313
|
-
if (useLoro)
|
|
371
|
+
if (useLoro) {
|
|
372
|
+
loroRemove(keyOf(id2));
|
|
373
|
+
}
|
|
314
374
|
await table.softDelete(new RecordId(config.table.name, keyOf(id2)));
|
|
315
375
|
}
|
|
316
376
|
return resultRows;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foretag/tanstack-db-surrealdb",
|
|
3
3
|
"description": "Add Offline / Local First Caching & Syncing to your SurrealDB app with TanstackDB and Loro (CRDTs)",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.16",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
7
7
|
],
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
"build": "tsup src/index.ts --dts --format esm,cjs"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"loro-crdt": "^1.
|
|
43
|
+
"loro-crdt": "^1.10.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@tanstack/db": "
|
|
47
|
-
"@tanstack/query-db-collection": "
|
|
48
|
-
"surrealdb": "2.0.0-alpha.
|
|
46
|
+
"@tanstack/db": "^0.5.10",
|
|
47
|
+
"@tanstack/query-db-collection": "^1.0.0",
|
|
48
|
+
"surrealdb": "2.0.0-alpha.14"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@biomejs/biome": "^2.3.
|
|
52
|
-
"@tanstack/svelte-db": "^0.1.
|
|
53
|
-
"surrealdb": "2.0.0-alpha.
|
|
51
|
+
"@biomejs/biome": "^2.3.8",
|
|
52
|
+
"@tanstack/svelte-db": "^0.1.53",
|
|
53
|
+
"surrealdb": "2.0.0-alpha.14",
|
|
54
54
|
"tsup": "^8.5.1"
|
|
55
55
|
}
|
|
56
56
|
}
|