@firtoz/db-helpers 2.1.1 → 2.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/README.md +7 -2
- package/dist/chunk-72YXZ3BK.js +445 -0
- package/dist/chunk-72YXZ3BK.js.map +1 -0
- package/dist/chunk-E6ZONR5D.js +170 -0
- package/dist/chunk-E6ZONR5D.js.map +1 -0
- package/dist/chunk-EIDXVFD3.js +133 -0
- package/dist/chunk-EIDXVFD3.js.map +1 -0
- package/dist/chunk-HMLY7DHA.js +12 -0
- package/dist/chunk-HMLY7DHA.js.map +1 -0
- package/dist/chunk-Y64WBRON.js +118 -0
- package/dist/chunk-Y64WBRON.js.map +1 -0
- package/dist/deferred-write-queue.d.ts +37 -0
- package/dist/deferred-write-queue.js +4 -0
- package/dist/deferred-write-queue.js.map +1 -0
- package/dist/generic-sync.d.ts +89 -0
- package/dist/generic-sync.js +5 -0
- package/dist/generic-sync.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/ir-evaluator.d.ts +14 -0
- package/dist/ir-evaluator.js +4 -0
- package/dist/ir-evaluator.js.map +1 -0
- package/dist/memoryCollection.d.ts +26 -0
- package/dist/memoryCollection.js +4 -0
- package/dist/memoryCollection.js.map +1 -0
- package/dist/sync-types.d.ts +75 -0
- package/dist/sync-types.js +3 -0
- package/dist/sync-types.js.map +1 -0
- package/package.json +13 -8
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# @firtoz/db-helpers
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@firtoz/db-helpers)
|
|
4
|
+
[](https://www.npmjs.com/package/@firtoz/db-helpers)
|
|
5
|
+
[](https://github.com/firtoz/fullstack-toolkit/blob/main/LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://tanstack.com/db)
|
|
9
|
+
|
|
10
|
+
**Small helpers for [TanStack DB](https://tanstack.com/db)** — published as compiled `dist/` with types on npm. Memory collections, sync adapters, and more as the toolkit grows.
|
|
6
11
|
|
|
7
12
|
## Current helpers
|
|
8
13
|
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { DeferredWriteQueue } from './chunk-E6ZONR5D.js';
|
|
2
|
+
import { exhaustiveGuard } from '@firtoz/maybe-error';
|
|
3
|
+
import { BasicIndex, DeduplicatedLoadSubset } from '@tanstack/db';
|
|
4
|
+
|
|
5
|
+
var USE_DEDUPE = true;
|
|
6
|
+
function createGenericSyncFunction(config, backend) {
|
|
7
|
+
let insertListener;
|
|
8
|
+
let updateListener;
|
|
9
|
+
let deleteListener;
|
|
10
|
+
let syncBegin = null;
|
|
11
|
+
let syncWrite = null;
|
|
12
|
+
let syncCommit = null;
|
|
13
|
+
let syncTruncate = null;
|
|
14
|
+
let initialSyncDone = null;
|
|
15
|
+
let syncLayerSerial = Promise.resolve();
|
|
16
|
+
const enqueueSyncLayer = (run) => {
|
|
17
|
+
const next = syncLayerSerial.catch(() => {
|
|
18
|
+
}).then(run);
|
|
19
|
+
syncLayerSerial = next;
|
|
20
|
+
return next;
|
|
21
|
+
};
|
|
22
|
+
function resolveDeferLocalPersistence(opts) {
|
|
23
|
+
if (opts === true) return { enabled: true, flushIntervalMs: 100 };
|
|
24
|
+
if (typeof opts === "object" && opts !== null) {
|
|
25
|
+
return { enabled: true, flushIntervalMs: opts.flushIntervalMs ?? 100 };
|
|
26
|
+
}
|
|
27
|
+
return { enabled: false, flushIntervalMs: 100 };
|
|
28
|
+
}
|
|
29
|
+
const deferOpts = resolveDeferLocalPersistence(config.deferLocalPersistence);
|
|
30
|
+
const resolveDeferredPersistKey = (item) => {
|
|
31
|
+
if (config.getSyncPersistKey !== void 0) {
|
|
32
|
+
return config.getSyncPersistKey(item);
|
|
33
|
+
}
|
|
34
|
+
if (item !== null && typeof item === "object" && "id" in item) {
|
|
35
|
+
const id = item.id;
|
|
36
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
37
|
+
return String(id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw new Error(
|
|
41
|
+
"[deferLocalPersistence] Persist key missing: set GenericBaseSyncConfig.getSyncPersistKey or use items with string/number `id`"
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
let deferQueue = null;
|
|
45
|
+
if (deferOpts.enabled) {
|
|
46
|
+
deferQueue = new DeferredWriteQueue({
|
|
47
|
+
backend,
|
|
48
|
+
getPersistKey: resolveDeferredPersistKey,
|
|
49
|
+
flushIntervalMs: deferOpts.flushIntervalMs
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const syncFn = (params) => {
|
|
53
|
+
const { begin, write, commit, markReady, truncate } = params;
|
|
54
|
+
syncBegin = begin;
|
|
55
|
+
syncWrite = write;
|
|
56
|
+
syncCommit = commit;
|
|
57
|
+
syncTruncate = truncate;
|
|
58
|
+
const initialSync = async () => {
|
|
59
|
+
await enqueueSyncLayer(async () => {
|
|
60
|
+
await config.readyPromise;
|
|
61
|
+
try {
|
|
62
|
+
const items = await backend.initialLoad();
|
|
63
|
+
begin();
|
|
64
|
+
for (const item of items) {
|
|
65
|
+
write({
|
|
66
|
+
type: "insert",
|
|
67
|
+
value: item
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
commit();
|
|
71
|
+
} finally {
|
|
72
|
+
markReady();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
if (config.syncMode === "eager" || !config.syncMode) {
|
|
77
|
+
initialSyncDone = initialSync();
|
|
78
|
+
} else {
|
|
79
|
+
markReady();
|
|
80
|
+
initialSyncDone = Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
insertListener = async (params2) => {
|
|
83
|
+
await enqueueSyncLayer(async () => {
|
|
84
|
+
const items = params2.transaction.mutations.map((m) => m.modified);
|
|
85
|
+
if (deferQueue !== null) {
|
|
86
|
+
begin();
|
|
87
|
+
for (const item of items) {
|
|
88
|
+
write({
|
|
89
|
+
type: "insert",
|
|
90
|
+
value: item
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
commit();
|
|
94
|
+
deferQueue.enqueueInsert(items);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const results = await backend.handleInsert(items);
|
|
98
|
+
begin();
|
|
99
|
+
for (const result of results) {
|
|
100
|
+
write({
|
|
101
|
+
type: "insert",
|
|
102
|
+
value: result
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
commit();
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
updateListener = async (params2) => {
|
|
109
|
+
await enqueueSyncLayer(async () => {
|
|
110
|
+
if (deferQueue !== null) {
|
|
111
|
+
const mutations = params2.transaction.mutations.map((m) => ({
|
|
112
|
+
key: String(m.key),
|
|
113
|
+
changes: m.changes,
|
|
114
|
+
original: m.original
|
|
115
|
+
}));
|
|
116
|
+
const results2 = mutations.map(
|
|
117
|
+
(m) => ({ ...m.original, ...m.changes })
|
|
118
|
+
);
|
|
119
|
+
begin();
|
|
120
|
+
for (const result of results2) {
|
|
121
|
+
write({
|
|
122
|
+
type: "update",
|
|
123
|
+
value: result
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
commit();
|
|
127
|
+
deferQueue.enqueueUpdate(mutations);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const results = await backend.handleUpdate(
|
|
131
|
+
params2.transaction.mutations
|
|
132
|
+
);
|
|
133
|
+
begin();
|
|
134
|
+
for (const result of results) {
|
|
135
|
+
write({
|
|
136
|
+
type: "update",
|
|
137
|
+
value: result
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
commit();
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
deleteListener = async (params2) => {
|
|
144
|
+
await enqueueSyncLayer(async () => {
|
|
145
|
+
if (deferQueue !== null) {
|
|
146
|
+
const mutations = params2.transaction.mutations.map((m) => ({
|
|
147
|
+
key: String(m.key),
|
|
148
|
+
modified: m.modified,
|
|
149
|
+
original: m.original
|
|
150
|
+
}));
|
|
151
|
+
begin();
|
|
152
|
+
for (const item of mutations) {
|
|
153
|
+
write({
|
|
154
|
+
type: "delete",
|
|
155
|
+
value: item.modified
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
commit();
|
|
159
|
+
deferQueue.enqueueDelete(mutations);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await backend.handleDelete(params2.transaction.mutations);
|
|
163
|
+
begin();
|
|
164
|
+
for (const item of params2.transaction.mutations) {
|
|
165
|
+
write({
|
|
166
|
+
type: "delete",
|
|
167
|
+
value: item.modified
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
commit();
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const loadSubset = async (options) => {
|
|
174
|
+
await enqueueSyncLayer(async () => {
|
|
175
|
+
await config.readyPromise;
|
|
176
|
+
if (deferQueue !== null) {
|
|
177
|
+
await deferQueue.flush();
|
|
178
|
+
}
|
|
179
|
+
const items = await backend.loadSubset(options);
|
|
180
|
+
begin();
|
|
181
|
+
for (const item of items) {
|
|
182
|
+
write({
|
|
183
|
+
type: "insert",
|
|
184
|
+
value: item
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
commit();
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
let loadSubsetDedupe = null;
|
|
191
|
+
{
|
|
192
|
+
loadSubsetDedupe = new DeduplicatedLoadSubset({
|
|
193
|
+
loadSubset
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
cleanup: () => {
|
|
198
|
+
deferQueue?.dispose();
|
|
199
|
+
deferQueue = null;
|
|
200
|
+
insertListener = void 0;
|
|
201
|
+
updateListener = void 0;
|
|
202
|
+
deleteListener = void 0;
|
|
203
|
+
loadSubsetDedupe?.reset();
|
|
204
|
+
},
|
|
205
|
+
loadSubset: loadSubsetDedupe?.loadSubset ?? loadSubset
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const resolveReceiveSyncPersistKey = (item) => {
|
|
209
|
+
if (config.getSyncPersistKey !== void 0) {
|
|
210
|
+
return config.getSyncPersistKey(item);
|
|
211
|
+
}
|
|
212
|
+
if (item !== null && typeof item === "object" && "id" in item) {
|
|
213
|
+
const id = item.id;
|
|
214
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
215
|
+
return String(id);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw new Error(
|
|
219
|
+
"[receiveSync] Persist key missing: set GenericBaseSyncConfig.getSyncPersistKey or use items with string/number `id`"
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
const shallowRecordDiff = (previous, next) => {
|
|
223
|
+
const out = {};
|
|
224
|
+
if (previous !== null && typeof previous === "object" && next !== null && typeof next === "object") {
|
|
225
|
+
const prevRec = previous;
|
|
226
|
+
const nextRec = next;
|
|
227
|
+
for (const k of Object.keys(nextRec)) {
|
|
228
|
+
if (prevRec[k] !== nextRec[k]) {
|
|
229
|
+
out[k] = nextRec[k];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
};
|
|
235
|
+
const toReceiveSyncDurableOps = (messages) => {
|
|
236
|
+
const out = [];
|
|
237
|
+
for (const msg of messages) {
|
|
238
|
+
switch (msg.type) {
|
|
239
|
+
case "insert":
|
|
240
|
+
out.push({ type: "insert", value: msg.value });
|
|
241
|
+
break;
|
|
242
|
+
case "update":
|
|
243
|
+
out.push({
|
|
244
|
+
type: "update",
|
|
245
|
+
key: resolveReceiveSyncPersistKey(msg.value),
|
|
246
|
+
changes: shallowRecordDiff(
|
|
247
|
+
msg.previousValue,
|
|
248
|
+
msg.value
|
|
249
|
+
),
|
|
250
|
+
original: msg.previousValue
|
|
251
|
+
});
|
|
252
|
+
break;
|
|
253
|
+
case "delete":
|
|
254
|
+
out.push({ type: "delete", key: String(msg.key) });
|
|
255
|
+
break;
|
|
256
|
+
case "truncate":
|
|
257
|
+
out.push({ type: "truncate" });
|
|
258
|
+
break;
|
|
259
|
+
default:
|
|
260
|
+
exhaustiveGuard(msg);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return out;
|
|
264
|
+
};
|
|
265
|
+
const receiveSync = async (messages) => {
|
|
266
|
+
if (messages.length === 0) return;
|
|
267
|
+
await enqueueSyncLayer(async () => {
|
|
268
|
+
if (initialSyncDone) {
|
|
269
|
+
await initialSyncDone;
|
|
270
|
+
}
|
|
271
|
+
if (!syncBegin || !syncWrite || !syncCommit || !syncTruncate) {
|
|
272
|
+
if (config.debug) {
|
|
273
|
+
console.warn(
|
|
274
|
+
"[receiveSync] Sync functions not initialized yet - messages will be dropped",
|
|
275
|
+
messages.length
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (deferQueue !== null) {
|
|
281
|
+
await deferQueue.flush();
|
|
282
|
+
}
|
|
283
|
+
syncBegin();
|
|
284
|
+
try {
|
|
285
|
+
const applyBatch = backend.applyReceiveSyncDurableWrites;
|
|
286
|
+
if (applyBatch !== void 0) {
|
|
287
|
+
await applyBatch(toReceiveSyncDurableOps(messages));
|
|
288
|
+
for (const msg of messages) {
|
|
289
|
+
switch (msg.type) {
|
|
290
|
+
case "insert":
|
|
291
|
+
syncWrite({ type: "insert", value: msg.value });
|
|
292
|
+
break;
|
|
293
|
+
case "update":
|
|
294
|
+
syncWrite({ type: "update", value: msg.value });
|
|
295
|
+
break;
|
|
296
|
+
case "delete":
|
|
297
|
+
syncWrite({
|
|
298
|
+
type: "delete",
|
|
299
|
+
value: { id: msg.key }
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
case "truncate":
|
|
303
|
+
syncTruncate();
|
|
304
|
+
break;
|
|
305
|
+
default:
|
|
306
|
+
exhaustiveGuard(msg);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
for (const msg of messages) {
|
|
311
|
+
switch (msg.type) {
|
|
312
|
+
case "insert":
|
|
313
|
+
await backend.handleInsert([msg.value]);
|
|
314
|
+
syncWrite({ type: "insert", value: msg.value });
|
|
315
|
+
break;
|
|
316
|
+
case "update": {
|
|
317
|
+
const key = resolveReceiveSyncPersistKey(msg.value);
|
|
318
|
+
await backend.handleUpdate([
|
|
319
|
+
{
|
|
320
|
+
key,
|
|
321
|
+
changes: shallowRecordDiff(
|
|
322
|
+
msg.previousValue,
|
|
323
|
+
msg.value
|
|
324
|
+
),
|
|
325
|
+
original: msg.previousValue
|
|
326
|
+
}
|
|
327
|
+
]);
|
|
328
|
+
syncWrite({ type: "update", value: msg.value });
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case "delete":
|
|
332
|
+
await backend.handleDelete([
|
|
333
|
+
{
|
|
334
|
+
key: String(msg.key),
|
|
335
|
+
modified: { id: msg.key },
|
|
336
|
+
original: { id: msg.key }
|
|
337
|
+
}
|
|
338
|
+
]);
|
|
339
|
+
syncWrite({
|
|
340
|
+
type: "delete",
|
|
341
|
+
value: { id: msg.key }
|
|
342
|
+
});
|
|
343
|
+
break;
|
|
344
|
+
case "truncate":
|
|
345
|
+
if (backend.handleTruncate) {
|
|
346
|
+
await backend.handleTruncate();
|
|
347
|
+
}
|
|
348
|
+
syncTruncate();
|
|
349
|
+
break;
|
|
350
|
+
default:
|
|
351
|
+
exhaustiveGuard(msg);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error(
|
|
357
|
+
"[receiveSync] error during sync writes, committing partial batch to avoid leaving transaction open",
|
|
358
|
+
err
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
syncCommit();
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
const utils = {
|
|
365
|
+
truncate: async () => {
|
|
366
|
+
const handleTruncate = backend.handleTruncate;
|
|
367
|
+
if (!handleTruncate) {
|
|
368
|
+
throw new Error("Truncate not supported by this backend");
|
|
369
|
+
}
|
|
370
|
+
if (!syncBegin || !syncTruncate || !syncCommit) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
"Sync functions not initialized - sync function may not have been called yet"
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
await enqueueSyncLayer(async () => {
|
|
376
|
+
if (deferQueue !== null) {
|
|
377
|
+
await deferQueue.flush();
|
|
378
|
+
}
|
|
379
|
+
await handleTruncate();
|
|
380
|
+
const begin = syncBegin;
|
|
381
|
+
const trunc = syncTruncate;
|
|
382
|
+
const commit = syncCommit;
|
|
383
|
+
if (!begin || !trunc || !commit) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
"Sync functions not initialized - sync function may not have been called yet"
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
begin();
|
|
389
|
+
trunc();
|
|
390
|
+
commit();
|
|
391
|
+
});
|
|
392
|
+
},
|
|
393
|
+
receiveSync
|
|
394
|
+
};
|
|
395
|
+
return {
|
|
396
|
+
sync: syncFn,
|
|
397
|
+
onInsert: async (params) => {
|
|
398
|
+
if (!insertListener) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
"insertListener not initialized - sync function may not have been called yet"
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return insertListener(params);
|
|
404
|
+
},
|
|
405
|
+
onUpdate: async (params) => {
|
|
406
|
+
if (!updateListener) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
"updateListener not initialized - sync function may not have been called yet"
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
return updateListener(params);
|
|
412
|
+
},
|
|
413
|
+
onDelete: async (params) => {
|
|
414
|
+
if (!deleteListener) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
"deleteListener not initialized - sync function may not have been called yet"
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return deleteListener(params);
|
|
420
|
+
},
|
|
421
|
+
utils
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
function createGenericCollectionConfig(config) {
|
|
425
|
+
return {
|
|
426
|
+
schema: config.schema,
|
|
427
|
+
getKey: config.getKey,
|
|
428
|
+
sync: {
|
|
429
|
+
sync: config.syncResult.sync
|
|
430
|
+
},
|
|
431
|
+
onInsert: config.onInsert ?? config.syncResult.onInsert,
|
|
432
|
+
onUpdate: config.onUpdate ?? config.syncResult.onUpdate,
|
|
433
|
+
onDelete: config.onDelete ?? config.syncResult.onDelete,
|
|
434
|
+
syncMode: config.syncMode,
|
|
435
|
+
// TanStack DB 0.6+: indexing is opt-in; eager BasicIndex restores pre-0.6 behavior for
|
|
436
|
+
// orderBy/limit live queries (playground pagination, usePredicateFilteredRows, etc.).
|
|
437
|
+
defaultIndexType: BasicIndex,
|
|
438
|
+
autoIndex: "eager",
|
|
439
|
+
utils: config.syncResult.utils
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export { USE_DEDUPE, createGenericCollectionConfig, createGenericSyncFunction };
|
|
444
|
+
//# sourceMappingURL=chunk-72YXZ3BK.js.map
|
|
445
|
+
//# sourceMappingURL=chunk-72YXZ3BK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generic-sync.ts"],"names":["params","results"],"mappings":"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","file":"chunk-72YXZ3BK.js","sourcesContent":["import type {\n\tCollectionUtils,\n\tReceiveSyncDurableOp,\n\tSyncMessage,\n} from \"./sync-types\";\nimport { DeferredWriteQueue } from \"./deferred-write-queue\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type {\n\tCollectionConfig,\n\tInferSchemaOutput,\n\tSyncConfig,\n\tSyncConfigRes,\n\tSyncMode,\n\tLoadSubsetOptions,\n} from \"@tanstack/db\";\nimport { BasicIndex, DeduplicatedLoadSubset } from \"@tanstack/db\";\n\n// DeduplicatedLoadSubset dedupes overlapping loadSubset calls. Previously disabled for TanStack/db#828\n// (invalid OR expressions when toggling isNull/isNotNull). Re-enabled with @tanstack/db 0.6.4; if\n// regressions appear, set back to false and add a regression test.\nexport const USE_DEDUPE = true as boolean;\n\n/**\n * Base configuration for sync lifecycle management (generic, no Drizzle dependency).\n */\nexport interface GenericBaseSyncConfig<TItem extends object = object> {\n\treadyPromise: Promise<void>;\n\tsyncMode?: SyncMode;\n\tdebug?: boolean;\n\t/**\n\t * Row key for durable storage when applying {@link CollectionUtils.receiveSync} updates.\n\t * If omitted, `id` on the item (string or number) is used.\n\t */\n\tgetSyncPersistKey?: (item: TItem) => string;\n\t/**\n\t * When set, local `onInsert` / `onUpdate` / `onDelete` confirm TanStack sync state immediately\n\t * and enqueue durable backend writes (coalesced, flushed on an interval). `receiveSync`,\n\t * `loadSubset`, and `truncate` flush the queue first so reads stay consistent.\n\t */\n\tdeferLocalPersistence?: boolean | { flushIntervalMs?: number };\n}\n\n/**\n * Backend-specific implementations required for sync (generic, no Drizzle dependency).\n */\nexport interface GenericSyncBackend<TItem extends object> {\n\tinitialLoad: () => Promise<Array<TItem>>;\n\tloadSubset: (options: LoadSubsetOptions) => Promise<Array<TItem>>;\n\thandleInsert: (items: Array<TItem>) => Promise<Array<TItem>>;\n\thandleUpdate: (\n\t\tmutations: Array<{\n\t\t\tkey: string;\n\t\t\tchanges: Partial<TItem>;\n\t\t\toriginal: TItem;\n\t\t}>,\n\t) => Promise<Array<TItem>>;\n\thandleDelete: (\n\t\tmutations: Array<{\n\t\t\tkey: string;\n\t\t\tmodified: TItem;\n\t\t\toriginal: TItem;\n\t\t}>,\n\t) => Promise<void>;\n\thandleTruncate?: () => Promise<void>;\n\t/**\n\t * When set, {@link CollectionUtils.receiveSync} persists an entire message batch with one call\n\t * (e.g. one SQLite transaction) instead of awaiting {@link handleInsert}/handleUpdate per\n\t * message. TanStack `syncWrite`/`syncTruncate` still run once per message in order.\n\t */\n\tapplyReceiveSyncDurableWrites?: (\n\t\tops: ReceiveSyncDurableOp<TItem>[],\n\t) => Promise<void>;\n\t/**\n\t * Optional batch upsert for deferred local persistence flushes (e.g. IndexedDB `put` in one tx).\n\t */\n\thandleBatchPut?: (items: Array<TItem>) => Promise<void>;\n}\n\n/**\n * Return type for createGenericSyncFunction.\n */\nexport type GenericSyncFunctionResult<TItem extends object> = {\n\tsync: SyncConfig<TItem, string>[\"sync\"];\n\tonInsert: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onInsert\"];\n\tonUpdate: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onUpdate\"];\n\tonDelete: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onDelete\"];\n\tutils: CollectionUtils<TItem>;\n};\n\n/**\n * Creates the sync function with common lifecycle management.\n * Generic version -- no Drizzle dependency.\n */\nexport function createGenericSyncFunction<TItem extends object>(\n\tconfig: GenericBaseSyncConfig<TItem>,\n\tbackend: GenericSyncBackend<TItem>,\n): GenericSyncFunctionResult<TItem> {\n\ttype CollectionType = CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>;\n\n\tlet insertListener: CollectionType[\"onInsert\"];\n\tlet updateListener: CollectionType[\"onUpdate\"];\n\tlet deleteListener: CollectionType[\"onDelete\"];\n\n\tlet syncBegin: (() => void) | null = null;\n\tlet syncWrite:\n\t\t| ((op: { type: \"insert\" | \"update\" | \"delete\"; value: TItem }) => void)\n\t\t| null = null;\n\tlet syncCommit: (() => void) | null = null;\n\tlet syncTruncate: (() => void) | null = null;\n\t/** Resolves when eager `initialSync` has finished (or immediately in on-demand mode). Used so `receiveSync` cannot interleave with initial inserts. */\n\tlet initialSyncDone: Promise<void> | null = null;\n\t/**\n\t * TanStack DB allows only one pending sync transaction per collection. Every path that calls\n\t * `begin`/`commit` — `initialSync`, `loadSubset`, `onInsert`/`onUpdate`/`onDelete`, `receiveSync`,\n\t * and `truncate` — must run through this queue so async backends (e.g. SQLite WASM) cannot\n\t * leave a transaction open across an `await` while another path starts a second transaction.\n\t */\n\tlet syncLayerSerial: Promise<void> = Promise.resolve();\n\n\tconst enqueueSyncLayer = (run: () => void | Promise<void>): Promise<void> => {\n\t\tconst next = syncLayerSerial.catch(() => {}).then(run);\n\t\tsyncLayerSerial = next;\n\t\treturn next;\n\t};\n\n\tfunction resolveDeferLocalPersistence(\n\t\topts: GenericBaseSyncConfig<TItem>[\"deferLocalPersistence\"],\n\t): { enabled: boolean; flushIntervalMs: number } {\n\t\tif (opts === true) return { enabled: true, flushIntervalMs: 100 };\n\t\tif (typeof opts === \"object\" && opts !== null) {\n\t\t\treturn { enabled: true, flushIntervalMs: opts.flushIntervalMs ?? 100 };\n\t\t}\n\t\treturn { enabled: false, flushIntervalMs: 100 };\n\t}\n\n\tconst deferOpts = resolveDeferLocalPersistence(config.deferLocalPersistence);\n\n\tconst resolveDeferredPersistKey = (item: TItem): string => {\n\t\tif (config.getSyncPersistKey !== undefined) {\n\t\t\treturn config.getSyncPersistKey(item);\n\t\t}\n\t\tif (item !== null && typeof item === \"object\" && \"id\" in item) {\n\t\t\tconst id = (item as { id: unknown }).id;\n\t\t\tif (typeof id === \"string\" || typeof id === \"number\") {\n\t\t\t\treturn String(id);\n\t\t\t}\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"[deferLocalPersistence] Persist key missing: set GenericBaseSyncConfig.getSyncPersistKey or use items with string/number `id`\",\n\t\t);\n\t};\n\n\tlet deferQueue: DeferredWriteQueue<TItem> | null = null;\n\tif (deferOpts.enabled) {\n\t\tdeferQueue = new DeferredWriteQueue({\n\t\t\tbackend,\n\t\t\tgetPersistKey: resolveDeferredPersistKey,\n\t\t\tflushIntervalMs: deferOpts.flushIntervalMs,\n\t\t});\n\t}\n\n\tconst syncFn: SyncConfig<TItem, string>[\"sync\"] = (params) => {\n\t\tconst { begin, write, commit, markReady, truncate } = params;\n\n\t\tsyncBegin = begin;\n\t\tsyncWrite = write;\n\t\tsyncCommit = commit;\n\t\tsyncTruncate = truncate;\n\n\t\tconst initialSync = async () => {\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tawait config.readyPromise;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst items = await backend.initialLoad();\n\n\t\t\t\t\tbegin();\n\n\t\t\t\t\tfor (const item of items) {\n\t\t\t\t\t\twrite({\n\t\t\t\t\t\t\ttype: \"insert\",\n\t\t\t\t\t\t\tvalue: item,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tcommit();\n\t\t\t\t} finally {\n\t\t\t\t\tmarkReady();\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\tif (config.syncMode === \"eager\" || !config.syncMode) {\n\t\t\tinitialSyncDone = initialSync();\n\t\t} else {\n\t\t\tmarkReady();\n\t\t\tinitialSyncDone = Promise.resolve();\n\t\t}\n\n\t\tinsertListener = async (params) => {\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tconst items = params.transaction.mutations.map((m) => m.modified);\n\t\t\t\tif (deferQueue !== null) {\n\t\t\t\t\tbegin();\n\t\t\t\t\tfor (const item of items) {\n\t\t\t\t\t\twrite({\n\t\t\t\t\t\t\ttype: \"insert\",\n\t\t\t\t\t\t\tvalue: item,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tcommit();\n\t\t\t\t\tdeferQueue.enqueueInsert(items);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst results = await backend.handleInsert(items);\n\n\t\t\t\tbegin();\n\t\t\t\tfor (const result of results) {\n\t\t\t\t\twrite({\n\t\t\t\t\t\ttype: \"insert\",\n\t\t\t\t\t\tvalue: result,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcommit();\n\t\t\t});\n\t\t};\n\n\t\tupdateListener = async (params) => {\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tif (deferQueue !== null) {\n\t\t\t\t\tconst mutations = params.transaction.mutations.map((m) => ({\n\t\t\t\t\t\tkey: String(m.key),\n\t\t\t\t\t\tchanges: m.changes as Partial<TItem>,\n\t\t\t\t\t\toriginal: m.original as TItem,\n\t\t\t\t\t}));\n\t\t\t\t\tconst results = mutations.map(\n\t\t\t\t\t\t(m) => ({ ...m.original, ...m.changes }) as TItem,\n\t\t\t\t\t);\n\t\t\t\t\tbegin();\n\t\t\t\t\tfor (const result of results) {\n\t\t\t\t\t\twrite({\n\t\t\t\t\t\t\ttype: \"update\",\n\t\t\t\t\t\t\tvalue: result,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tcommit();\n\t\t\t\t\tdeferQueue.enqueueUpdate(mutations);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst results = await backend.handleUpdate(\n\t\t\t\t\tparams.transaction.mutations,\n\t\t\t\t);\n\n\t\t\t\tbegin();\n\t\t\t\tfor (const result of results) {\n\t\t\t\t\twrite({\n\t\t\t\t\t\ttype: \"update\",\n\t\t\t\t\t\tvalue: result,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcommit();\n\t\t\t});\n\t\t};\n\n\t\tdeleteListener = async (params) => {\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tif (deferQueue !== null) {\n\t\t\t\t\tconst mutations = params.transaction.mutations.map((m) => ({\n\t\t\t\t\t\tkey: String(m.key),\n\t\t\t\t\t\tmodified: m.modified as TItem,\n\t\t\t\t\t\toriginal: m.original as TItem,\n\t\t\t\t\t}));\n\t\t\t\t\tbegin();\n\t\t\t\t\tfor (const item of mutations) {\n\t\t\t\t\t\twrite({\n\t\t\t\t\t\t\ttype: \"delete\",\n\t\t\t\t\t\t\tvalue: item.modified,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tcommit();\n\t\t\t\t\tdeferQueue.enqueueDelete(mutations);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait backend.handleDelete(params.transaction.mutations);\n\n\t\t\t\tbegin();\n\t\t\t\tfor (const item of params.transaction.mutations) {\n\t\t\t\t\twrite({\n\t\t\t\t\t\ttype: \"delete\",\n\t\t\t\t\t\tvalue: item.modified,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tcommit();\n\t\t\t});\n\t\t};\n\n\t\tconst loadSubset = async (options: LoadSubsetOptions) => {\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tawait config.readyPromise;\n\n\t\t\t\tif (deferQueue !== null) {\n\t\t\t\t\tawait deferQueue.flush();\n\t\t\t\t}\n\n\t\t\t\tconst items = await backend.loadSubset(options);\n\n\t\t\t\tbegin();\n\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\twrite({\n\t\t\t\t\t\ttype: \"insert\",\n\t\t\t\t\t\tvalue: item,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tcommit();\n\t\t\t});\n\t\t};\n\n\t\tlet loadSubsetDedupe: DeduplicatedLoadSubset | null = null;\n\t\tif (USE_DEDUPE) {\n\t\t\tloadSubsetDedupe = new DeduplicatedLoadSubset({\n\t\t\t\tloadSubset,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tcleanup: () => {\n\t\t\t\tdeferQueue?.dispose();\n\t\t\t\tdeferQueue = null;\n\t\t\t\tinsertListener = undefined;\n\t\t\t\tupdateListener = undefined;\n\t\t\t\tdeleteListener = undefined;\n\t\t\t\tloadSubsetDedupe?.reset();\n\t\t\t},\n\t\t\tloadSubset: loadSubsetDedupe?.loadSubset ?? loadSubset,\n\t\t} satisfies SyncConfigRes;\n\t};\n\n\tconst resolveReceiveSyncPersistKey = (item: TItem): string => {\n\t\tif (config.getSyncPersistKey !== undefined) {\n\t\t\treturn config.getSyncPersistKey(item);\n\t\t}\n\t\tif (item !== null && typeof item === \"object\" && \"id\" in item) {\n\t\t\tconst id = (item as { id: unknown }).id;\n\t\t\tif (typeof id === \"string\" || typeof id === \"number\") {\n\t\t\t\treturn String(id);\n\t\t\t}\n\t\t}\n\t\tthrow new Error(\n\t\t\t\"[receiveSync] Persist key missing: set GenericBaseSyncConfig.getSyncPersistKey or use items with string/number `id`\",\n\t\t);\n\t};\n\n\tconst shallowRecordDiff = (previous: TItem, next: TItem): Partial<TItem> => {\n\t\tconst out: Partial<TItem> = {};\n\t\tif (\n\t\t\tprevious !== null &&\n\t\t\ttypeof previous === \"object\" &&\n\t\t\tnext !== null &&\n\t\t\ttypeof next === \"object\"\n\t\t) {\n\t\t\tconst prevRec = previous as Record<string, unknown>;\n\t\t\tconst nextRec = next as Record<string, unknown>;\n\t\t\tfor (const k of Object.keys(nextRec)) {\n\t\t\t\tif (prevRec[k] !== nextRec[k]) {\n\t\t\t\t\t(out as Record<string, unknown>)[k] = nextRec[k];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn out;\n\t};\n\n\tconst toReceiveSyncDurableOps = (\n\t\tmessages: SyncMessage<TItem>[],\n\t): ReceiveSyncDurableOp<TItem>[] => {\n\t\tconst out: ReceiveSyncDurableOp<TItem>[] = [];\n\t\tfor (const msg of messages) {\n\t\t\tswitch (msg.type) {\n\t\t\t\tcase \"insert\":\n\t\t\t\t\tout.push({ type: \"insert\", value: msg.value });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"update\":\n\t\t\t\t\tout.push({\n\t\t\t\t\t\ttype: \"update\",\n\t\t\t\t\t\tkey: resolveReceiveSyncPersistKey(msg.value),\n\t\t\t\t\t\tchanges: shallowRecordDiff(\n\t\t\t\t\t\t\tmsg.previousValue,\n\t\t\t\t\t\t\tmsg.value,\n\t\t\t\t\t\t) as Partial<TItem>,\n\t\t\t\t\t\toriginal: msg.previousValue,\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\tout.push({ type: \"delete\", key: String(msg.key) });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"truncate\":\n\t\t\t\t\tout.push({ type: \"truncate\" });\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\texhaustiveGuard(msg);\n\t\t\t}\n\t\t}\n\t\treturn out;\n\t};\n\n\tconst receiveSync = async (messages: SyncMessage<TItem>[]) => {\n\t\tif (messages.length === 0) return;\n\n\t\tawait enqueueSyncLayer(async () => {\n\t\t\tif (initialSyncDone) {\n\t\t\t\tawait initialSyncDone;\n\t\t\t}\n\t\t\tif (!syncBegin || !syncWrite || !syncCommit || !syncTruncate) {\n\t\t\t\tif (config.debug) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"[receiveSync] Sync functions not initialized yet - messages will be dropped\",\n\t\t\t\t\t\tmessages.length,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (deferQueue !== null) {\n\t\t\t\tawait deferQueue.flush();\n\t\t\t}\n\t\t\tsyncBegin();\n\n\t\t\ttry {\n\t\t\t\tconst applyBatch = backend.applyReceiveSyncDurableWrites;\n\t\t\t\tif (applyBatch !== undefined) {\n\t\t\t\t\tawait applyBatch(toReceiveSyncDurableOps(messages));\n\t\t\t\t\tfor (const msg of messages) {\n\t\t\t\t\t\tswitch (msg.type) {\n\t\t\t\t\t\t\tcase \"insert\":\n\t\t\t\t\t\t\t\tsyncWrite({ type: \"insert\", value: msg.value });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"update\":\n\t\t\t\t\t\t\t\tsyncWrite({ type: \"update\", value: msg.value });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"delete\":\n\t\t\t\t\t\t\t\tsyncWrite({\n\t\t\t\t\t\t\t\t\ttype: \"delete\",\n\t\t\t\t\t\t\t\t\tvalue: { id: msg.key } as TItem,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"truncate\":\n\t\t\t\t\t\t\t\tsyncTruncate();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\texhaustiveGuard(msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor (const msg of messages) {\n\t\t\t\t\t\tswitch (msg.type) {\n\t\t\t\t\t\t\tcase \"insert\":\n\t\t\t\t\t\t\t\tawait backend.handleInsert([msg.value]);\n\t\t\t\t\t\t\t\tsyncWrite({ type: \"insert\", value: msg.value });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"update\": {\n\t\t\t\t\t\t\t\tconst key = resolveReceiveSyncPersistKey(msg.value);\n\t\t\t\t\t\t\t\tawait backend.handleUpdate([\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\t\tchanges: shallowRecordDiff(\n\t\t\t\t\t\t\t\t\t\t\tmsg.previousValue,\n\t\t\t\t\t\t\t\t\t\t\tmsg.value,\n\t\t\t\t\t\t\t\t\t\t) as Partial<TItem>,\n\t\t\t\t\t\t\t\t\t\toriginal: msg.previousValue,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\tsyncWrite({ type: \"update\", value: msg.value });\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"delete\":\n\t\t\t\t\t\t\t\tawait backend.handleDelete([\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tkey: String(msg.key),\n\t\t\t\t\t\t\t\t\t\tmodified: { id: msg.key } as TItem,\n\t\t\t\t\t\t\t\t\t\toriginal: { id: msg.key } as TItem,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\tsyncWrite({\n\t\t\t\t\t\t\t\t\ttype: \"delete\",\n\t\t\t\t\t\t\t\t\tvalue: { id: msg.key } as TItem,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"truncate\":\n\t\t\t\t\t\t\t\tif (backend.handleTruncate) {\n\t\t\t\t\t\t\t\t\tawait backend.handleTruncate();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsyncTruncate();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\texhaustiveGuard(msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[receiveSync] error during sync writes, committing partial batch to avoid leaving transaction open\",\n\t\t\t\t\terr,\n\t\t\t\t);\n\t\t\t}\n\t\t\tsyncCommit();\n\t\t});\n\t};\n\n\tconst utils: CollectionUtils<TItem> = {\n\t\ttruncate: async () => {\n\t\t\tconst handleTruncate = backend.handleTruncate;\n\t\t\tif (!handleTruncate) {\n\t\t\t\tthrow new Error(\"Truncate not supported by this backend\");\n\t\t\t}\n\t\t\tif (!syncBegin || !syncTruncate || !syncCommit) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Sync functions not initialized - sync function may not have been called yet\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait enqueueSyncLayer(async () => {\n\t\t\t\tif (deferQueue !== null) {\n\t\t\t\t\tawait deferQueue.flush();\n\t\t\t\t}\n\t\t\t\tawait handleTruncate();\n\t\t\t\tconst begin = syncBegin;\n\t\t\t\tconst trunc = syncTruncate;\n\t\t\t\tconst commit = syncCommit;\n\t\t\t\tif (!begin || !trunc || !commit) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Sync functions not initialized - sync function may not have been called yet\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbegin();\n\t\t\t\ttrunc();\n\t\t\t\tcommit();\n\t\t\t});\n\t\t},\n\t\treceiveSync,\n\t};\n\n\treturn {\n\t\tsync: syncFn,\n\t\tonInsert: async (params) => {\n\t\t\tif (!insertListener) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"insertListener not initialized - sync function may not have been called yet\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn insertListener(params);\n\t\t},\n\t\tonUpdate: async (params) => {\n\t\t\tif (!updateListener) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"updateListener not initialized - sync function may not have been called yet\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn updateListener(params);\n\t\t},\n\t\tonDelete: async (params) => {\n\t\t\tif (!deleteListener) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"deleteListener not initialized - sync function may not have been called yet\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn deleteListener(params);\n\t\t},\n\t\tutils,\n\t};\n}\n\n/**\n * Generic collection config factory.\n * Combines schema, sync, and event handlers into a collection config.\n * No Drizzle dependency -- uses StandardSchemaV1 directly.\n */\nexport function createGenericCollectionConfig<\n\tTItem extends object,\n\tTSchema extends StandardSchemaV1,\n>(config: {\n\tschema: TSchema;\n\tgetKey: (item: TItem) => string;\n\tsyncResult: GenericSyncFunctionResult<TItem>;\n\tonInsert?: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onInsert\"];\n\tonUpdate?: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onUpdate\"];\n\tonDelete?: CollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Schema type parameter needs to be flexible\n\t\tany\n\t>[\"onDelete\"];\n\tsyncMode?: SyncMode;\n}): Omit<\n\tCollectionConfig<\n\t\tTItem,\n\t\tstring,\n\t\tTSchema,\n\t\tCollectionUtils<InferSchemaOutput<TSchema>>\n\t>,\n\t\"utils\"\n> & {\n\tschema: TSchema;\n\tutils: CollectionUtils<InferSchemaOutput<TSchema>>;\n} {\n\treturn {\n\t\tschema: config.schema,\n\t\tgetKey: config.getKey,\n\t\tsync: {\n\t\t\tsync: config.syncResult.sync,\n\t\t},\n\t\tonInsert: config.onInsert ?? config.syncResult.onInsert,\n\t\tonUpdate: config.onUpdate ?? config.syncResult.onUpdate,\n\t\tonDelete: config.onDelete ?? config.syncResult.onDelete,\n\t\tsyncMode: config.syncMode,\n\t\t// TanStack DB 0.6+: indexing is opt-in; eager BasicIndex restores pre-0.6 behavior for\n\t\t// orderBy/limit live queries (playground pagination, usePredicateFilteredRows, etc.).\n\t\tdefaultIndexType: BasicIndex,\n\t\tautoIndex: \"eager\",\n\t\tutils: config.syncResult.utils as CollectionUtils<\n\t\t\tInferSchemaOutput<TSchema>\n\t\t>,\n\t};\n}\n"]}
|