taladb 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.mjs +107 -33
- package/dist/index.d.mts +31 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.js +107 -33
- package/dist/index.mjs +107 -33
- package/dist/index.react-native.mjs +107 -33
- package/package.json +1 -1
|
@@ -39,15 +39,13 @@ function unsupportedSync(runtime) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
async function readCursor(cursorCol, target) {
|
|
42
|
-
const doc = await cursorCol.findOne({
|
|
43
|
-
return doc?.
|
|
42
|
+
const doc = await cursorCol.findOne({ target });
|
|
43
|
+
return { pushMs: doc?.pushMs ?? 0, pullMs: doc?.pullMs ?? 0 };
|
|
44
44
|
}
|
|
45
|
-
async function writeCursor(cursorCol, target,
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
await cursorCol.
|
|
49
|
-
} else {
|
|
50
|
-
await cursorCol.insert({ _id: target, sinceMs });
|
|
45
|
+
async function writeCursor(cursorCol, target, cursor) {
|
|
46
|
+
const updated = await cursorCol.updateOne({ target }, { $set: { ...cursor } });
|
|
47
|
+
if (!updated) {
|
|
48
|
+
await cursorCol.insert({ target, ...cursor });
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
async function runSync(handle, adapter, options) {
|
|
@@ -63,12 +61,11 @@ async function runSync(handle, adapter, options) {
|
|
|
63
61
|
}
|
|
64
62
|
const collections = await resolveCollections(handle, options);
|
|
65
63
|
const cursorCol = handle.collection(CURSOR_COLLECTION);
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const local = doPush ? await handle.exportChanges(collections, sinceMs) : "[]";
|
|
64
|
+
const cursor = await readCursor(cursorCol, target);
|
|
65
|
+
const local = doPush ? await handle.exportChanges(collections, 0) : "[]";
|
|
69
66
|
let pulled = 0;
|
|
70
67
|
if (doPull) {
|
|
71
|
-
const remote = await adapter.pull(
|
|
68
|
+
const remote = await adapter.pull(0);
|
|
72
69
|
if (remote && remote !== "[]") {
|
|
73
70
|
pulled = await handle.importChanges(remote);
|
|
74
71
|
}
|
|
@@ -78,8 +75,11 @@ async function runSync(handle, adapter, options) {
|
|
|
78
75
|
pushed = JSON.parse(local).length;
|
|
79
76
|
await adapter.push(local);
|
|
80
77
|
}
|
|
81
|
-
await writeCursor(cursorCol, target,
|
|
82
|
-
|
|
78
|
+
await writeCursor(cursorCol, target, {
|
|
79
|
+
pushMs: cursor.pushMs,
|
|
80
|
+
pullMs: cursor.pullMs
|
|
81
|
+
});
|
|
82
|
+
return { pushed, pulled, cursor: 0 };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// src/http-adapter.ts
|
|
@@ -87,7 +87,7 @@ var HttpSyncAdapter = class {
|
|
|
87
87
|
constructor(options) {
|
|
88
88
|
this.endpoint = options.endpoint.replace(/\/$/, "");
|
|
89
89
|
this.headers = options.headers ?? {};
|
|
90
|
-
const f = options.fetch ?? globalThis.fetch;
|
|
90
|
+
const f = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
91
91
|
if (!f) {
|
|
92
92
|
throw new Error(
|
|
93
93
|
"HttpSyncAdapter: no fetch available. Pass options.fetch on runtimes without a global fetch."
|
|
@@ -213,28 +213,44 @@ var WorkerProxy = class {
|
|
|
213
213
|
this.pending.clear();
|
|
214
214
|
}
|
|
215
215
|
};
|
|
216
|
-
function makePoller(findFn, callback) {
|
|
216
|
+
function makePoller(findFn, callback, onError) {
|
|
217
217
|
let active = true;
|
|
218
218
|
let lastJson = "";
|
|
219
|
+
let running = false;
|
|
220
|
+
let rerun = false;
|
|
219
221
|
const poll = async () => {
|
|
220
222
|
if (!active) return;
|
|
223
|
+
if (running) {
|
|
224
|
+
rerun = true;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
running = true;
|
|
221
228
|
try {
|
|
222
229
|
const docs = await findFn();
|
|
230
|
+
if (!active) return;
|
|
223
231
|
const json = JSON.stringify(docs);
|
|
224
232
|
if (json !== lastJson) {
|
|
225
233
|
lastJson = json;
|
|
226
234
|
callback(docs);
|
|
227
235
|
}
|
|
228
|
-
} catch {
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (active) onError?.(error);
|
|
238
|
+
} finally {
|
|
239
|
+
running = false;
|
|
240
|
+
if (active) {
|
|
241
|
+
if (rerun) {
|
|
242
|
+
rerun = false;
|
|
243
|
+
void poll();
|
|
244
|
+
} else setTimeout(poll, 300);
|
|
245
|
+
}
|
|
229
246
|
}
|
|
230
|
-
if (active) setTimeout(poll, 300);
|
|
231
247
|
};
|
|
232
248
|
poll();
|
|
233
249
|
return () => {
|
|
234
250
|
active = false;
|
|
235
251
|
};
|
|
236
252
|
}
|
|
237
|
-
async function createBrowserDB(dbName, config) {
|
|
253
|
+
async function createBrowserDB(dbName, config, passphrase) {
|
|
238
254
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", "react-native://unreachable");
|
|
239
255
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
240
256
|
const proxy = new WorkerProxy(worker);
|
|
@@ -242,7 +258,13 @@ async function createBrowserDB(dbName, config) {
|
|
|
242
258
|
proxy.abort(new Error(`taladb worker error: ${e.message ?? "unknown"}`));
|
|
243
259
|
};
|
|
244
260
|
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
245
|
-
|
|
261
|
+
try {
|
|
262
|
+
await proxy.send("init", { dbName, configJson, passphrase });
|
|
263
|
+
} catch (e) {
|
|
264
|
+
proxy.abort(e instanceof Error ? e : new Error(String(e)));
|
|
265
|
+
worker.terminate();
|
|
266
|
+
throw e;
|
|
267
|
+
}
|
|
246
268
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
247
269
|
let channel = null;
|
|
248
270
|
if (typeof BroadcastChannel !== "undefined") {
|
|
@@ -303,6 +325,8 @@ async function createBrowserDB(dbName, config) {
|
|
|
303
325
|
},
|
|
304
326
|
createIndex: (field) => proxy.send("createIndex", { collection: name, field }),
|
|
305
327
|
dropIndex: (field) => proxy.send("dropIndex", { collection: name, field }),
|
|
328
|
+
createCompoundIndex: (fields) => proxy.send("createCompoundIndex", { collection: name, fieldsJson: JSON.stringify(fields) }),
|
|
329
|
+
dropCompoundIndex: (fields) => proxy.send("dropCompoundIndex", { collection: name, fieldsJson: JSON.stringify(fields) }),
|
|
306
330
|
createFtsIndex: (field) => proxy.send("createFtsIndex", { collection: name, field }),
|
|
307
331
|
dropFtsIndex: (field) => proxy.send("dropFtsIndex", { collection: name, field }),
|
|
308
332
|
createVectorIndex: (field, options) => {
|
|
@@ -333,12 +357,19 @@ async function createBrowserDB(dbName, config) {
|
|
|
333
357
|
});
|
|
334
358
|
return JSON.parse(json);
|
|
335
359
|
},
|
|
336
|
-
subscribe: (filter, callback) => {
|
|
360
|
+
subscribe: (filter, callback, onError) => {
|
|
337
361
|
let active = true;
|
|
338
362
|
let lastJson = "";
|
|
339
363
|
let timer = null;
|
|
364
|
+
let running = false;
|
|
365
|
+
let rerun = false;
|
|
340
366
|
const poll = async () => {
|
|
341
367
|
if (!active) return;
|
|
368
|
+
if (running) {
|
|
369
|
+
rerun = true;
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
running = true;
|
|
342
373
|
if (timer !== null) {
|
|
343
374
|
clearTimeout(timer);
|
|
344
375
|
timer = null;
|
|
@@ -348,13 +379,22 @@ async function createBrowserDB(dbName, config) {
|
|
|
348
379
|
collection: name,
|
|
349
380
|
filterJson: filter ? s(filter) : "null"
|
|
350
381
|
});
|
|
382
|
+
if (!active) return;
|
|
351
383
|
if (json !== lastJson) {
|
|
352
384
|
lastJson = json;
|
|
353
385
|
callback(JSON.parse(json));
|
|
354
386
|
}
|
|
355
|
-
} catch {
|
|
387
|
+
} catch (error) {
|
|
388
|
+
if (active) onError?.(error);
|
|
389
|
+
} finally {
|
|
390
|
+
running = false;
|
|
391
|
+
}
|
|
392
|
+
if (active) {
|
|
393
|
+
if (rerun) {
|
|
394
|
+
rerun = false;
|
|
395
|
+
void poll();
|
|
396
|
+
} else timer = setTimeout(poll, 300);
|
|
356
397
|
}
|
|
357
|
-
if (active) timer = setTimeout(poll, 300);
|
|
358
398
|
};
|
|
359
399
|
nudgeCallbacks.add(poll);
|
|
360
400
|
poll();
|
|
@@ -370,9 +410,11 @@ async function createBrowserDB(dbName, config) {
|
|
|
370
410
|
};
|
|
371
411
|
return opts ? applySchema(wrapped, opts) : wrapped;
|
|
372
412
|
}
|
|
373
|
-
|
|
413
|
+
const handle = {
|
|
374
414
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
375
415
|
compact: () => proxy.send("compact"),
|
|
416
|
+
syncStatus: async () => JSON.parse(await proxy.send("syncStatus")),
|
|
417
|
+
flushSync: (timeoutMs = 5e3) => proxy.send("flushSync", { timeoutMs }),
|
|
376
418
|
close: async () => {
|
|
377
419
|
channel?.close();
|
|
378
420
|
try {
|
|
@@ -382,13 +424,21 @@ async function createBrowserDB(dbName, config) {
|
|
|
382
424
|
proxy.abort(new Error("taladb worker closed"));
|
|
383
425
|
}
|
|
384
426
|
},
|
|
385
|
-
|
|
427
|
+
// All engine work (export scan, LWW merge) runs inside the worker, off the
|
|
428
|
+
// main thread — a sync pass never blocks rendering, whatever its size.
|
|
429
|
+
exportChanges: (collections, sinceMs) => proxy.send("exportChangeset", { collectionsJson: JSON.stringify(collections), sinceMs }),
|
|
430
|
+
importChanges: (changeset) => proxy.send("importChangeset", { changesetJson: changeset }),
|
|
431
|
+
listCollectionNames: async () => JSON.parse(await proxy.send("listCollections")),
|
|
432
|
+
sync: (adapter, options) => runSync(handle, adapter, options)
|
|
386
433
|
};
|
|
434
|
+
return handle;
|
|
387
435
|
}
|
|
388
|
-
async function createNodeDB(dbName, config) {
|
|
389
|
-
const
|
|
436
|
+
async function createNodeDB(dbName, config, passphrase) {
|
|
437
|
+
const native = await import("./node-A4LKRSW5.mjs");
|
|
438
|
+
const TalaDBNode = native.TalaDbNode ?? native.TalaDBNode;
|
|
439
|
+
if (!TalaDBNode) throw new Error("@taladb/node loaded but exports no TalaDbNode class \u2014 rebuild the native module");
|
|
390
440
|
const configJson = config !== void 0 ? JSON.stringify(config) : null;
|
|
391
|
-
const db = TalaDBNode.open(dbName, configJson);
|
|
441
|
+
const db = TalaDBNode.open(dbName, configJson, passphrase ?? null);
|
|
392
442
|
function wrapCollection(name, opts) {
|
|
393
443
|
const col = db.collection(name);
|
|
394
444
|
const wrapped = {
|
|
@@ -404,6 +454,8 @@ async function createNodeDB(dbName, config) {
|
|
|
404
454
|
aggregate: async (pipeline) => col.aggregate(pipeline),
|
|
405
455
|
createIndex: async (field) => col.createIndex(field),
|
|
406
456
|
dropIndex: async (field) => col.dropIndex(field),
|
|
457
|
+
createCompoundIndex: async (fields) => col.createCompoundIndex(fields),
|
|
458
|
+
dropCompoundIndex: async (fields) => col.dropCompoundIndex(fields),
|
|
407
459
|
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
408
460
|
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
409
461
|
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
@@ -417,7 +469,7 @@ async function createNodeDB(dbName, config) {
|
|
|
417
469
|
const raw = await col.findNearest(field, vector, topK, filter ?? null);
|
|
418
470
|
return raw;
|
|
419
471
|
},
|
|
420
|
-
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? null), callback)
|
|
472
|
+
subscribe: (filter, callback, onError) => makePoller(async () => col.find(filter ?? null), callback, onError)
|
|
421
473
|
};
|
|
422
474
|
return opts ? applySchema(wrapped, opts) : wrapped;
|
|
423
475
|
}
|
|
@@ -455,6 +507,8 @@ async function createNativeDB(_dbName) {
|
|
|
455
507
|
aggregate: async (pipeline) => native.aggregate(name, pipeline),
|
|
456
508
|
createIndex: async (field) => native.createIndex(name, field),
|
|
457
509
|
dropIndex: async (field) => native.dropIndex(name, field),
|
|
510
|
+
createCompoundIndex: async (fields) => native.createCompoundIndex(name, fields),
|
|
511
|
+
dropCompoundIndex: async (fields) => native.dropCompoundIndex(name, fields),
|
|
458
512
|
createFtsIndex: async (field) => native.createFtsIndex(name, field),
|
|
459
513
|
dropFtsIndex: async (field) => native.dropFtsIndex(name, field),
|
|
460
514
|
createVectorIndex: async (field, options) => {
|
|
@@ -474,18 +528,35 @@ async function createNativeDB(_dbName) {
|
|
|
474
528
|
const raw = native.findNearest(name, field, vector, topK, filter ?? null);
|
|
475
529
|
return raw;
|
|
476
530
|
},
|
|
477
|
-
subscribe: (filter, callback) => makePoller(async () => native.find(name, filter ?? {}), callback)
|
|
531
|
+
subscribe: (filter, callback, onError) => makePoller(async () => native.find(name, filter ?? {}), callback, onError)
|
|
478
532
|
};
|
|
479
533
|
return opts ? applySchema(wrapped, opts) : wrapped;
|
|
480
534
|
}
|
|
535
|
+
const syncSurface = typeof native.exportChanges === "function" && typeof native.importChanges === "function" && typeof native.listCollectionNames === "function" ? (() => {
|
|
536
|
+
const handle = {
|
|
537
|
+
collection: (name, opts) => wrapCollection(name, opts),
|
|
538
|
+
exportChanges: async (collections, sinceMs) => native.exportChanges(collections, sinceMs),
|
|
539
|
+
importChanges: async (changeset) => native.importChanges(changeset),
|
|
540
|
+
listCollectionNames: async () => native.listCollectionNames(),
|
|
541
|
+
sync: (adapter, options) => runSync(handle, adapter, options)
|
|
542
|
+
};
|
|
543
|
+
return {
|
|
544
|
+
exportChanges: handle.exportChanges,
|
|
545
|
+
importChanges: handle.importChanges,
|
|
546
|
+
sync: handle.sync
|
|
547
|
+
};
|
|
548
|
+
})() : unsupportedSync("react-native");
|
|
481
549
|
return {
|
|
482
550
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
483
551
|
compact: async () => native.compact(),
|
|
484
552
|
close: async () => native.close(),
|
|
485
|
-
...
|
|
553
|
+
...syncSurface
|
|
486
554
|
};
|
|
487
555
|
}
|
|
488
556
|
async function openDB(dbName = "taladb.db", options) {
|
|
557
|
+
if (options?.passphrase !== void 0 && options.passphrase.length === 0) {
|
|
558
|
+
throw new Error("TalaDB encryption passphrase must not be empty");
|
|
559
|
+
}
|
|
489
560
|
let resolvedConfig;
|
|
490
561
|
if (options?.config !== void 0) {
|
|
491
562
|
validateConfig(options.config);
|
|
@@ -496,11 +567,14 @@ async function openDB(dbName = "taladb.db", options) {
|
|
|
496
567
|
const platform = detectPlatform();
|
|
497
568
|
switch (platform) {
|
|
498
569
|
case "browser":
|
|
499
|
-
return createBrowserDB(dbName, resolvedConfig);
|
|
570
|
+
return createBrowserDB(dbName, resolvedConfig, options?.passphrase);
|
|
500
571
|
case "react-native":
|
|
572
|
+
if (options?.passphrase !== void 0) {
|
|
573
|
+
throw new Error("On React Native, pass the passphrase in the config JSON to TalaDBModule.initialize(); refusing to assume the already-open native database is encrypted");
|
|
574
|
+
}
|
|
501
575
|
return createNativeDB(dbName);
|
|
502
576
|
case "node":
|
|
503
|
-
return createNodeDB(dbName, resolvedConfig);
|
|
577
|
+
return createNodeDB(dbName, resolvedConfig, options?.passphrase);
|
|
504
578
|
}
|
|
505
579
|
}
|
|
506
580
|
export {
|