@taladb/web 0.8.4 → 0.9.1
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/package.json +1 -1
- package/pkg/package.json +1 -1
- package/pkg/taladb_web.d.ts +35 -4
- package/pkg/taladb_web.js +153 -7
- package/pkg/taladb_web_bg.wasm +0 -0
- package/pkg/taladb_web_bg.wasm.d.ts +11 -3
- package/worker/taladb.worker.js +147 -19
package/package.json
CHANGED
package/pkg/package.json
CHANGED
package/pkg/taladb_web.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export class CollectionWasm {
|
|
|
14
14
|
* Count documents matching the filter.
|
|
15
15
|
*/
|
|
16
16
|
count(filter: any): number;
|
|
17
|
+
/**
|
|
18
|
+
* Create a compound index. `fields_json` is a JSON array of field names.
|
|
19
|
+
*/
|
|
20
|
+
createCompoundIndex(fields_json: string): void;
|
|
17
21
|
/**
|
|
18
22
|
* Create a secondary index on a field.
|
|
19
23
|
*/
|
|
@@ -36,6 +40,10 @@ export class CollectionWasm {
|
|
|
36
40
|
* Delete the first matching document. Returns true if deleted.
|
|
37
41
|
*/
|
|
38
42
|
deleteOne(filter: any): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Drop a compound index by its ordered field list (`fields_json`).
|
|
45
|
+
*/
|
|
46
|
+
dropCompoundIndex(fields_json: string): void;
|
|
39
47
|
/**
|
|
40
48
|
* Drop a secondary index.
|
|
41
49
|
*/
|
|
@@ -110,6 +118,11 @@ export class TalaDBWasm {
|
|
|
110
118
|
* database via Last-Write-Wins. Returns the number of documents changed.
|
|
111
119
|
*/
|
|
112
120
|
importChanges(changeset_json: string): number;
|
|
121
|
+
/**
|
|
122
|
+
* User collection names (reserved `_`-prefixed collections excluded).
|
|
123
|
+
* Backs the sync orchestration's "sync all collections" default.
|
|
124
|
+
*/
|
|
125
|
+
listCollectionNames(): string[];
|
|
113
126
|
/**
|
|
114
127
|
* Open an in-memory database (suitable for tests and environments without OPFS).
|
|
115
128
|
*/
|
|
@@ -169,6 +182,10 @@ export class WorkerDB {
|
|
|
169
182
|
* Count matching documents.
|
|
170
183
|
*/
|
|
171
184
|
count(collection: string, filter_json: string): number;
|
|
185
|
+
/**
|
|
186
|
+
* Create a compound index. `fields_json` is a JSON array of field names.
|
|
187
|
+
*/
|
|
188
|
+
createCompoundIndex(collection: string, fields_json: string): void;
|
|
172
189
|
createFtsIndex(collection: string, field: string): void;
|
|
173
190
|
createIndex(collection: string, field: string): void;
|
|
174
191
|
/**
|
|
@@ -188,6 +205,10 @@ export class WorkerDB {
|
|
|
188
205
|
* Delete the first matching document. Returns `true` / `false`.
|
|
189
206
|
*/
|
|
190
207
|
deleteOne(collection: string, filter_json: string): boolean;
|
|
208
|
+
/**
|
|
209
|
+
* Drop a compound index by its ordered field list (`fields_json`).
|
|
210
|
+
*/
|
|
211
|
+
dropCompoundIndex(collection: string, fields_json: string): void;
|
|
191
212
|
dropFtsIndex(collection: string, field: string): void;
|
|
192
213
|
dropIndex(collection: string, field: string): void;
|
|
193
214
|
/**
|
|
@@ -272,7 +293,7 @@ export class WorkerDB {
|
|
|
272
293
|
* const db = WorkerDB.openWithConfigAndOpfs(handle, JSON.stringify(config));
|
|
273
294
|
* ```
|
|
274
295
|
*/
|
|
275
|
-
static openWithConfigAndOpfs(sync_handle: FileSystemSyncAccessHandle, config_json?: string | null): WorkerDB;
|
|
296
|
+
static openWithConfigAndOpfs(sync_handle: FileSystemSyncAccessHandle, config_json?: string | null, passphrase?: string | null, salt?: Uint8Array | null): WorkerDB;
|
|
276
297
|
/**
|
|
277
298
|
* Open a database from an optional snapshot with HTTP push sync config.
|
|
278
299
|
*
|
|
@@ -307,6 +328,8 @@ export class WorkerDB {
|
|
|
307
328
|
* ```
|
|
308
329
|
*/
|
|
309
330
|
static openWithSnapshot(data?: Uint8Array | null): WorkerDB;
|
|
331
|
+
syncPending(): bigint;
|
|
332
|
+
syncStatus(): string;
|
|
310
333
|
/**
|
|
311
334
|
* Update all matching documents. Returns the count updated.
|
|
312
335
|
*/
|
|
@@ -382,11 +405,13 @@ export interface InitOutput {
|
|
|
382
405
|
readonly workerdb_compact: (a: number) => [number, number];
|
|
383
406
|
readonly workerdb_compactTombstones: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
384
407
|
readonly workerdb_count: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
408
|
+
readonly workerdb_createCompoundIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
385
409
|
readonly workerdb_createFtsIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
386
410
|
readonly workerdb_createIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
387
411
|
readonly workerdb_createVectorIndex: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number) => [number, number];
|
|
388
412
|
readonly workerdb_deleteMany: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
389
413
|
readonly workerdb_deleteOne: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
414
|
+
readonly workerdb_dropCompoundIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
390
415
|
readonly workerdb_dropFtsIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
391
416
|
readonly workerdb_dropIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
392
417
|
readonly workerdb_dropVectorIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
@@ -401,10 +426,12 @@ export interface InitOutput {
|
|
|
401
426
|
readonly workerdb_listCollections: (a: number) => [number, number, number, number];
|
|
402
427
|
readonly workerdb_listIndexes: (a: number, b: number, c: number) => [number, number, number, number];
|
|
403
428
|
readonly workerdb_openInMemory: () => [number, number, number];
|
|
404
|
-
readonly workerdb_openWithConfigAndOpfs: (a: any, b: number, c: number) => [number, number, number];
|
|
429
|
+
readonly workerdb_openWithConfigAndOpfs: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
405
430
|
readonly workerdb_openWithConfigAndSnapshot: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
406
431
|
readonly workerdb_openWithOpfs: (a: any) => [number, number, number];
|
|
407
432
|
readonly workerdb_openWithSnapshot: (a: number, b: number) => [number, number, number];
|
|
433
|
+
readonly workerdb_syncPending: (a: number) => bigint;
|
|
434
|
+
readonly workerdb_syncStatus: (a: number) => [number, number];
|
|
408
435
|
readonly workerdb_updateMany: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
409
436
|
readonly workerdb_updateOne: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
410
437
|
readonly workerdb_upgradeVectorIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
@@ -412,10 +439,12 @@ export interface InitOutput {
|
|
|
412
439
|
readonly __wbg_taladbwasm_free: (a: number, b: number) => void;
|
|
413
440
|
readonly collectionwasm_aggregate: (a: number, b: any) => [number, number, number];
|
|
414
441
|
readonly collectionwasm_count: (a: number, b: any) => [number, number, number];
|
|
442
|
+
readonly collectionwasm_createCompoundIndex: (a: number, b: number, c: number) => [number, number];
|
|
415
443
|
readonly collectionwasm_createIndex: (a: number, b: number, c: number) => [number, number];
|
|
416
444
|
readonly collectionwasm_createVectorIndex: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => [number, number];
|
|
417
445
|
readonly collectionwasm_deleteMany: (a: number, b: any) => [number, number, number];
|
|
418
446
|
readonly collectionwasm_deleteOne: (a: number, b: any) => [number, number, number];
|
|
447
|
+
readonly collectionwasm_dropCompoundIndex: (a: number, b: number, c: number) => [number, number];
|
|
419
448
|
readonly collectionwasm_dropIndex: (a: number, b: number, c: number) => [number, number];
|
|
420
449
|
readonly collectionwasm_dropVectorIndex: (a: number, b: number, c: number) => [number, number];
|
|
421
450
|
readonly collectionwasm_find: (a: number, b: any) => [number, number, number];
|
|
@@ -430,16 +459,17 @@ export interface InitOutput {
|
|
|
430
459
|
readonly taladbwasm_exportChanges: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
431
460
|
readonly taladbwasm_exportSnapshot: (a: number) => [number, number, number, number];
|
|
432
461
|
readonly taladbwasm_importChanges: (a: number, b: number, c: number) => [number, number, number];
|
|
462
|
+
readonly taladbwasm_listCollectionNames: (a: number) => [number, number, number, number];
|
|
433
463
|
readonly taladbwasm_openInMemory: () => [number, number, number];
|
|
434
464
|
readonly taladbwasm_openWithSnapshot: (a: number, b: number) => [number, number, number];
|
|
435
465
|
readonly init: () => void;
|
|
436
|
-
readonly idb_load_snapshot: (a: number, b: number) => any;
|
|
437
|
-
readonly idb_save_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
438
466
|
readonly opfs_open_backend: (a: number, b: number) => any;
|
|
439
467
|
readonly is_opfs_available: () => any;
|
|
440
468
|
readonly opfs_delete_snapshot: (a: number, b: number) => any;
|
|
441
469
|
readonly opfs_flush_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
442
470
|
readonly opfs_load_snapshot: (a: number, b: number) => any;
|
|
471
|
+
readonly idb_load_snapshot: (a: number, b: number) => any;
|
|
472
|
+
readonly idb_save_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
443
473
|
readonly wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut_____Output_______: (a: number, b: number) => void;
|
|
444
474
|
readonly wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output___core_7d5f0a2ba6a62c33___result__Result_____wasm_bindgen_60ce93de3f474933___JsError___: (a: number, b: number) => void;
|
|
445
475
|
readonly wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output_______: (a: number, b: number) => void;
|
|
@@ -454,6 +484,7 @@ export interface InitOutput {
|
|
|
454
484
|
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
455
485
|
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
456
486
|
readonly __externref_table_dealloc: (a: number) => void;
|
|
487
|
+
readonly __externref_drop_slice: (a: number, b: number) => void;
|
|
457
488
|
readonly __wbindgen_start: () => void;
|
|
458
489
|
}
|
|
459
490
|
|
package/pkg/taladb_web.js
CHANGED
|
@@ -43,6 +43,18 @@ export class CollectionWasm {
|
|
|
43
43
|
}
|
|
44
44
|
return ret[0] >>> 0;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a compound index. `fields_json` is a JSON array of field names.
|
|
48
|
+
* @param {string} fields_json
|
|
49
|
+
*/
|
|
50
|
+
createCompoundIndex(fields_json) {
|
|
51
|
+
const ptr0 = passStringToWasm0(fields_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
52
|
+
const len0 = WASM_VECTOR_LEN;
|
|
53
|
+
const ret = wasm.collectionwasm_createCompoundIndex(this.__wbg_ptr, ptr0, len0);
|
|
54
|
+
if (ret[1]) {
|
|
55
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
/**
|
|
47
59
|
* Create a secondary index on a field.
|
|
48
60
|
* @param {string} field
|
|
@@ -106,6 +118,18 @@ export class CollectionWasm {
|
|
|
106
118
|
}
|
|
107
119
|
return ret[0] !== 0;
|
|
108
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Drop a compound index by its ordered field list (`fields_json`).
|
|
123
|
+
* @param {string} fields_json
|
|
124
|
+
*/
|
|
125
|
+
dropCompoundIndex(fields_json) {
|
|
126
|
+
const ptr0 = passStringToWasm0(fields_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
127
|
+
const len0 = WASM_VECTOR_LEN;
|
|
128
|
+
const ret = wasm.collectionwasm_dropCompoundIndex(this.__wbg_ptr, ptr0, len0);
|
|
129
|
+
if (ret[1]) {
|
|
130
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
109
133
|
/**
|
|
110
134
|
* Drop a secondary index.
|
|
111
135
|
* @param {string} field
|
|
@@ -346,6 +370,20 @@ export class TalaDBWasm {
|
|
|
346
370
|
}
|
|
347
371
|
return ret[0] >>> 0;
|
|
348
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* User collection names (reserved `_`-prefixed collections excluded).
|
|
375
|
+
* Backs the sync orchestration's "sync all collections" default.
|
|
376
|
+
* @returns {string[]}
|
|
377
|
+
*/
|
|
378
|
+
listCollectionNames() {
|
|
379
|
+
const ret = wasm.taladbwasm_listCollectionNames(this.__wbg_ptr);
|
|
380
|
+
if (ret[3]) {
|
|
381
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
382
|
+
}
|
|
383
|
+
var v1 = getArrayJsValueFromWasm0(ret[0], ret[1]).slice();
|
|
384
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
|
|
385
|
+
return v1;
|
|
386
|
+
}
|
|
349
387
|
/**
|
|
350
388
|
* Open an in-memory database (suitable for tests and environments without OPFS).
|
|
351
389
|
* @returns {TalaDBWasm}
|
|
@@ -490,6 +528,21 @@ export class WorkerDB {
|
|
|
490
528
|
}
|
|
491
529
|
return ret[0] >>> 0;
|
|
492
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Create a compound index. `fields_json` is a JSON array of field names.
|
|
533
|
+
* @param {string} collection
|
|
534
|
+
* @param {string} fields_json
|
|
535
|
+
*/
|
|
536
|
+
createCompoundIndex(collection, fields_json) {
|
|
537
|
+
const ptr0 = passStringToWasm0(collection, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
538
|
+
const len0 = WASM_VECTOR_LEN;
|
|
539
|
+
const ptr1 = passStringToWasm0(fields_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
540
|
+
const len1 = WASM_VECTOR_LEN;
|
|
541
|
+
const ret = wasm.workerdb_createCompoundIndex(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
|
542
|
+
if (ret[1]) {
|
|
543
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
493
546
|
/**
|
|
494
547
|
* @param {string} collection
|
|
495
548
|
* @param {string} field
|
|
@@ -581,6 +634,21 @@ export class WorkerDB {
|
|
|
581
634
|
}
|
|
582
635
|
return ret[0] !== 0;
|
|
583
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Drop a compound index by its ordered field list (`fields_json`).
|
|
639
|
+
* @param {string} collection
|
|
640
|
+
* @param {string} fields_json
|
|
641
|
+
*/
|
|
642
|
+
dropCompoundIndex(collection, fields_json) {
|
|
643
|
+
const ptr0 = passStringToWasm0(collection, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
644
|
+
const len0 = WASM_VECTOR_LEN;
|
|
645
|
+
const ptr1 = passStringToWasm0(fields_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
646
|
+
const len1 = WASM_VECTOR_LEN;
|
|
647
|
+
const ret = wasm.workerdb_dropCompoundIndex(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
|
648
|
+
if (ret[1]) {
|
|
649
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
584
652
|
/**
|
|
585
653
|
* @param {string} collection
|
|
586
654
|
* @param {string} field
|
|
@@ -917,12 +985,18 @@ export class WorkerDB {
|
|
|
917
985
|
* ```
|
|
918
986
|
* @param {FileSystemSyncAccessHandle} sync_handle
|
|
919
987
|
* @param {string | null} [config_json]
|
|
988
|
+
* @param {string | null} [passphrase]
|
|
989
|
+
* @param {Uint8Array | null} [salt]
|
|
920
990
|
* @returns {WorkerDB}
|
|
921
991
|
*/
|
|
922
|
-
static openWithConfigAndOpfs(sync_handle, config_json) {
|
|
992
|
+
static openWithConfigAndOpfs(sync_handle, config_json, passphrase, salt) {
|
|
923
993
|
var ptr0 = isLikeNone(config_json) ? 0 : passStringToWasm0(config_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
924
994
|
var len0 = WASM_VECTOR_LEN;
|
|
925
|
-
|
|
995
|
+
var ptr1 = isLikeNone(passphrase) ? 0 : passStringToWasm0(passphrase, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
996
|
+
var len1 = WASM_VECTOR_LEN;
|
|
997
|
+
var ptr2 = isLikeNone(salt) ? 0 : passArray8ToWasm0(salt, wasm.__wbindgen_malloc);
|
|
998
|
+
var len2 = WASM_VECTOR_LEN;
|
|
999
|
+
const ret = wasm.workerdb_openWithConfigAndOpfs(sync_handle, ptr0, len0, ptr1, len1, ptr2, len2);
|
|
926
1000
|
if (ret[2]) {
|
|
927
1001
|
throw takeFromExternrefTable0(ret[1]);
|
|
928
1002
|
}
|
|
@@ -993,6 +1067,28 @@ export class WorkerDB {
|
|
|
993
1067
|
}
|
|
994
1068
|
return WorkerDB.__wrap(ret[0]);
|
|
995
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* @returns {bigint}
|
|
1072
|
+
*/
|
|
1073
|
+
syncPending() {
|
|
1074
|
+
const ret = wasm.workerdb_syncPending(this.__wbg_ptr);
|
|
1075
|
+
return BigInt.asUintN(64, ret);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* @returns {string}
|
|
1079
|
+
*/
|
|
1080
|
+
syncStatus() {
|
|
1081
|
+
let deferred1_0;
|
|
1082
|
+
let deferred1_1;
|
|
1083
|
+
try {
|
|
1084
|
+
const ret = wasm.workerdb_syncStatus(this.__wbg_ptr);
|
|
1085
|
+
deferred1_0 = ret[0];
|
|
1086
|
+
deferred1_1 = ret[1];
|
|
1087
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
1088
|
+
} finally {
|
|
1089
|
+
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
996
1092
|
/**
|
|
997
1093
|
* Update all matching documents. Returns the count updated.
|
|
998
1094
|
* @param {string} collection
|
|
@@ -1291,6 +1387,10 @@ function __wbg_get_imports() {
|
|
|
1291
1387
|
const ret = arg0.createWritable();
|
|
1292
1388
|
return ret;
|
|
1293
1389
|
},
|
|
1390
|
+
__wbg_crypto_38df2bab126b63dc: function(arg0) {
|
|
1391
|
+
const ret = arg0.crypto;
|
|
1392
|
+
return ret;
|
|
1393
|
+
},
|
|
1294
1394
|
__wbg_done_08ce71ee07e3bd17: function(arg0) {
|
|
1295
1395
|
const ret = arg0.done;
|
|
1296
1396
|
return ret;
|
|
@@ -1340,6 +1440,9 @@ function __wbg_get_imports() {
|
|
|
1340
1440
|
__wbg_getRandomValues_3f44b700395062e5: function() { return handleError(function (arg0, arg1) {
|
|
1341
1441
|
globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
|
1342
1442
|
}, arguments); },
|
|
1443
|
+
__wbg_getRandomValues_c44a50d8cfdaebeb: function() { return handleError(function (arg0, arg1) {
|
|
1444
|
+
arg0.getRandomValues(arg1);
|
|
1445
|
+
}, arguments); },
|
|
1343
1446
|
__wbg_getSize_0a16c5e2524d34aa: function() { return handleError(function (arg0) {
|
|
1344
1447
|
const ret = arg0.getSize();
|
|
1345
1448
|
return ret;
|
|
@@ -1498,6 +1601,10 @@ function __wbg_get_imports() {
|
|
|
1498
1601
|
const ret = arg0.length;
|
|
1499
1602
|
return ret;
|
|
1500
1603
|
},
|
|
1604
|
+
__wbg_msCrypto_bd5a034af96bcba6: function(arg0) {
|
|
1605
|
+
const ret = arg0.msCrypto;
|
|
1606
|
+
return ret;
|
|
1607
|
+
},
|
|
1501
1608
|
__wbg_navigator_9cebf56f28aa719b: function(arg0) {
|
|
1502
1609
|
const ret = arg0.navigator;
|
|
1503
1610
|
return ret;
|
|
@@ -1586,6 +1693,10 @@ function __wbg_get_imports() {
|
|
|
1586
1693
|
const ret = arg0.next;
|
|
1587
1694
|
return ret;
|
|
1588
1695
|
},
|
|
1696
|
+
__wbg_node_84ea875411254db1: function(arg0) {
|
|
1697
|
+
const ret = arg0.node;
|
|
1698
|
+
return ret;
|
|
1699
|
+
},
|
|
1589
1700
|
__wbg_now_16f0c993d5dd6c27: function() {
|
|
1590
1701
|
const ret = Date.now();
|
|
1591
1702
|
return ret;
|
|
@@ -1594,6 +1705,10 @@ function __wbg_get_imports() {
|
|
|
1594
1705
|
const ret = Array.of(arg0, arg1);
|
|
1595
1706
|
return ret;
|
|
1596
1707
|
},
|
|
1708
|
+
__wbg_process_44c7a14e11e9f69e: function(arg0) {
|
|
1709
|
+
const ret = arg0.process;
|
|
1710
|
+
return ret;
|
|
1711
|
+
},
|
|
1597
1712
|
__wbg_prototypesetcall_d62e5099504357e6: function(arg0, arg1, arg2) {
|
|
1598
1713
|
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
1599
1714
|
},
|
|
@@ -1608,10 +1723,17 @@ function __wbg_get_imports() {
|
|
|
1608
1723
|
__wbg_queueMicrotask_a082d78ce798393e: function(arg0) {
|
|
1609
1724
|
queueMicrotask(arg0);
|
|
1610
1725
|
},
|
|
1726
|
+
__wbg_randomFillSync_6c25eac9869eb53c: function() { return handleError(function (arg0, arg1) {
|
|
1727
|
+
arg0.randomFillSync(arg1);
|
|
1728
|
+
}, arguments); },
|
|
1611
1729
|
__wbg_removeEntry_b2a4d6d0ee2040c5: function(arg0, arg1, arg2) {
|
|
1612
1730
|
const ret = arg0.removeEntry(getStringFromWasm0(arg1, arg2));
|
|
1613
1731
|
return ret;
|
|
1614
1732
|
},
|
|
1733
|
+
__wbg_require_b4edbdcf3e2a1ef0: function() { return handleError(function () {
|
|
1734
|
+
const ret = module.require;
|
|
1735
|
+
return ret;
|
|
1736
|
+
}, arguments); },
|
|
1615
1737
|
__wbg_resolve_ae8d83246e5bcc12: function(arg0) {
|
|
1616
1738
|
const ret = Promise.resolve(arg0);
|
|
1617
1739
|
return ret;
|
|
@@ -1697,6 +1819,10 @@ function __wbg_get_imports() {
|
|
|
1697
1819
|
const ret = JSON.stringify(arg0);
|
|
1698
1820
|
return ret;
|
|
1699
1821
|
}, arguments); },
|
|
1822
|
+
__wbg_subarray_a068d24e39478a8a: function(arg0, arg1, arg2) {
|
|
1823
|
+
const ret = arg0.subarray(arg1 >>> 0, arg2 >>> 0);
|
|
1824
|
+
return ret;
|
|
1825
|
+
},
|
|
1700
1826
|
__wbg_then_098abe61755d12f6: function(arg0, arg1) {
|
|
1701
1827
|
const ret = arg0.then(arg1);
|
|
1702
1828
|
return ret;
|
|
@@ -1719,18 +1845,22 @@ function __wbg_get_imports() {
|
|
|
1719
1845
|
const ret = arg0.value;
|
|
1720
1846
|
return ret;
|
|
1721
1847
|
},
|
|
1848
|
+
__wbg_versions_276b2795b1c6a219: function(arg0) {
|
|
1849
|
+
const ret = arg0.versions;
|
|
1850
|
+
return ret;
|
|
1851
|
+
},
|
|
1722
1852
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
1723
|
-
// Cast intrinsic for `Closure(Closure { dtor_idx:
|
|
1853
|
+
// Cast intrinsic for `Closure(Closure { dtor_idx: 215, function: Function { arguments: [], shim_idx: 216, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
1724
1854
|
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut_____Output_______, wasm_bindgen_60ce93de3f474933___convert__closures_____invoke_______true_);
|
|
1725
1855
|
return ret;
|
|
1726
1856
|
},
|
|
1727
1857
|
__wbindgen_cast_0000000000000002: function(arg0, arg1) {
|
|
1728
|
-
// Cast intrinsic for `Closure(Closure { dtor_idx:
|
|
1858
|
+
// Cast intrinsic for `Closure(Closure { dtor_idx: 692, function: Function { arguments: [Externref], shim_idx: 693, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
1729
1859
|
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output___core_7d5f0a2ba6a62c33___result__Result_____wasm_bindgen_60ce93de3f474933___JsError___, wasm_bindgen_60ce93de3f474933___convert__closures_____invoke___wasm_bindgen_60ce93de3f474933___JsValue__core_7d5f0a2ba6a62c33___result__Result_____wasm_bindgen_60ce93de3f474933___JsError___true_);
|
|
1730
1860
|
return ret;
|
|
1731
1861
|
},
|
|
1732
1862
|
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
1733
|
-
// Cast intrinsic for `Closure(Closure { dtor_idx:
|
|
1863
|
+
// Cast intrinsic for `Closure(Closure { dtor_idx: 71, function: Function { arguments: [Externref], shim_idx: 72, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
1734
1864
|
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output_______, wasm_bindgen_60ce93de3f474933___convert__closures_____invoke___wasm_bindgen_60ce93de3f474933___JsValue______true_);
|
|
1735
1865
|
return ret;
|
|
1736
1866
|
},
|
|
@@ -1745,16 +1875,21 @@ function __wbg_get_imports() {
|
|
|
1745
1875
|
return ret;
|
|
1746
1876
|
},
|
|
1747
1877
|
__wbindgen_cast_0000000000000006: function(arg0, arg1) {
|
|
1878
|
+
// Cast intrinsic for `Ref(Slice(U8)) -> NamedExternref("Uint8Array")`.
|
|
1879
|
+
const ret = getArrayU8FromWasm0(arg0, arg1);
|
|
1880
|
+
return ret;
|
|
1881
|
+
},
|
|
1882
|
+
__wbindgen_cast_0000000000000007: function(arg0, arg1) {
|
|
1748
1883
|
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
1749
1884
|
const ret = getStringFromWasm0(arg0, arg1);
|
|
1750
1885
|
return ret;
|
|
1751
1886
|
},
|
|
1752
|
-
|
|
1887
|
+
__wbindgen_cast_0000000000000008: function(arg0) {
|
|
1753
1888
|
// Cast intrinsic for `U64 -> Externref`.
|
|
1754
1889
|
const ret = BigInt.asUintN(64, arg0);
|
|
1755
1890
|
return ret;
|
|
1756
1891
|
},
|
|
1757
|
-
|
|
1892
|
+
__wbindgen_cast_0000000000000009: function(arg0, arg1) {
|
|
1758
1893
|
var v0 = getArrayU8FromWasm0(arg0, arg1).slice();
|
|
1759
1894
|
wasm.__wbindgen_free(arg0, arg1 * 1, 1);
|
|
1760
1895
|
// Cast intrinsic for `Vector(U8) -> Externref`.
|
|
@@ -1889,6 +2024,17 @@ function debugString(val) {
|
|
|
1889
2024
|
return className;
|
|
1890
2025
|
}
|
|
1891
2026
|
|
|
2027
|
+
function getArrayJsValueFromWasm0(ptr, len) {
|
|
2028
|
+
ptr = ptr >>> 0;
|
|
2029
|
+
const mem = getDataViewMemory0();
|
|
2030
|
+
const result = [];
|
|
2031
|
+
for (let i = ptr; i < ptr + 4 * len; i += 4) {
|
|
2032
|
+
result.push(wasm.__wbindgen_externrefs.get(mem.getUint32(i, true)));
|
|
2033
|
+
}
|
|
2034
|
+
wasm.__externref_drop_slice(ptr, len);
|
|
2035
|
+
return result;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
1892
2038
|
function getArrayU8FromWasm0(ptr, len) {
|
|
1893
2039
|
ptr = ptr >>> 0;
|
|
1894
2040
|
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
package/pkg/taladb_web_bg.wasm
CHANGED
|
Binary file
|
|
@@ -6,11 +6,13 @@ export const workerdb_aggregate: (a: number, b: number, c: number, d: number, e:
|
|
|
6
6
|
export const workerdb_compact: (a: number) => [number, number];
|
|
7
7
|
export const workerdb_compactTombstones: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
8
8
|
export const workerdb_count: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
9
|
+
export const workerdb_createCompoundIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
9
10
|
export const workerdb_createFtsIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
10
11
|
export const workerdb_createIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
11
12
|
export const workerdb_createVectorIndex: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number) => [number, number];
|
|
12
13
|
export const workerdb_deleteMany: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
13
14
|
export const workerdb_deleteOne: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
|
|
15
|
+
export const workerdb_dropCompoundIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
14
16
|
export const workerdb_dropFtsIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
15
17
|
export const workerdb_dropIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
16
18
|
export const workerdb_dropVectorIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
@@ -25,10 +27,12 @@ export const workerdb_insertMany: (a: number, b: number, c: number, d: number, e
|
|
|
25
27
|
export const workerdb_listCollections: (a: number) => [number, number, number, number];
|
|
26
28
|
export const workerdb_listIndexes: (a: number, b: number, c: number) => [number, number, number, number];
|
|
27
29
|
export const workerdb_openInMemory: () => [number, number, number];
|
|
28
|
-
export const workerdb_openWithConfigAndOpfs: (a: any, b: number, c: number) => [number, number, number];
|
|
30
|
+
export const workerdb_openWithConfigAndOpfs: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
29
31
|
export const workerdb_openWithConfigAndSnapshot: (a: number, b: number, c: number, d: number) => [number, number, number];
|
|
30
32
|
export const workerdb_openWithOpfs: (a: any) => [number, number, number];
|
|
31
33
|
export const workerdb_openWithSnapshot: (a: number, b: number) => [number, number, number];
|
|
34
|
+
export const workerdb_syncPending: (a: number) => bigint;
|
|
35
|
+
export const workerdb_syncStatus: (a: number) => [number, number];
|
|
32
36
|
export const workerdb_updateMany: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
33
37
|
export const workerdb_updateOne: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
34
38
|
export const workerdb_upgradeVectorIndex: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
@@ -36,10 +40,12 @@ export const __wbg_collectionwasm_free: (a: number, b: number) => void;
|
|
|
36
40
|
export const __wbg_taladbwasm_free: (a: number, b: number) => void;
|
|
37
41
|
export const collectionwasm_aggregate: (a: number, b: any) => [number, number, number];
|
|
38
42
|
export const collectionwasm_count: (a: number, b: any) => [number, number, number];
|
|
43
|
+
export const collectionwasm_createCompoundIndex: (a: number, b: number, c: number) => [number, number];
|
|
39
44
|
export const collectionwasm_createIndex: (a: number, b: number, c: number) => [number, number];
|
|
40
45
|
export const collectionwasm_createVectorIndex: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => [number, number];
|
|
41
46
|
export const collectionwasm_deleteMany: (a: number, b: any) => [number, number, number];
|
|
42
47
|
export const collectionwasm_deleteOne: (a: number, b: any) => [number, number, number];
|
|
48
|
+
export const collectionwasm_dropCompoundIndex: (a: number, b: number, c: number) => [number, number];
|
|
43
49
|
export const collectionwasm_dropIndex: (a: number, b: number, c: number) => [number, number];
|
|
44
50
|
export const collectionwasm_dropVectorIndex: (a: number, b: number, c: number) => [number, number];
|
|
45
51
|
export const collectionwasm_find: (a: number, b: any) => [number, number, number];
|
|
@@ -54,16 +60,17 @@ export const taladbwasm_collection: (a: number, b: number, c: number) => [number
|
|
|
54
60
|
export const taladbwasm_exportChanges: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
55
61
|
export const taladbwasm_exportSnapshot: (a: number) => [number, number, number, number];
|
|
56
62
|
export const taladbwasm_importChanges: (a: number, b: number, c: number) => [number, number, number];
|
|
63
|
+
export const taladbwasm_listCollectionNames: (a: number) => [number, number, number, number];
|
|
57
64
|
export const taladbwasm_openInMemory: () => [number, number, number];
|
|
58
65
|
export const taladbwasm_openWithSnapshot: (a: number, b: number) => [number, number, number];
|
|
59
66
|
export const init: () => void;
|
|
60
|
-
export const idb_load_snapshot: (a: number, b: number) => any;
|
|
61
|
-
export const idb_save_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
62
67
|
export const opfs_open_backend: (a: number, b: number) => any;
|
|
63
68
|
export const is_opfs_available: () => any;
|
|
64
69
|
export const opfs_delete_snapshot: (a: number, b: number) => any;
|
|
65
70
|
export const opfs_flush_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
66
71
|
export const opfs_load_snapshot: (a: number, b: number) => any;
|
|
72
|
+
export const idb_load_snapshot: (a: number, b: number) => any;
|
|
73
|
+
export const idb_save_snapshot: (a: number, b: number, c: number, d: number) => any;
|
|
67
74
|
export const wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut_____Output_______: (a: number, b: number) => void;
|
|
68
75
|
export const wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output___core_7d5f0a2ba6a62c33___result__Result_____wasm_bindgen_60ce93de3f474933___JsError___: (a: number, b: number) => void;
|
|
69
76
|
export const wasm_bindgen_60ce93de3f474933___closure__destroy___dyn_core_7d5f0a2ba6a62c33___ops__function__FnMut__wasm_bindgen_60ce93de3f474933___JsValue____Output_______: (a: number, b: number) => void;
|
|
@@ -78,4 +85,5 @@ export const __externref_table_alloc: () => number;
|
|
|
78
85
|
export const __wbindgen_externrefs: WebAssembly.Table;
|
|
79
86
|
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
80
87
|
export const __externref_table_dealloc: (a: number) => void;
|
|
88
|
+
export const __externref_drop_slice: (a: number, b: number) => void;
|
|
81
89
|
export const __wbindgen_start: () => void;
|
package/worker/taladb.worker.js
CHANGED
|
@@ -162,14 +162,26 @@ let broadcastChannel = null;
|
|
|
162
162
|
*/
|
|
163
163
|
let idbFallback = false;
|
|
164
164
|
|
|
165
|
+
/** Whether this worker is allowed to publish the authoritative IDB snapshot. */
|
|
166
|
+
let snapshotWriter = false;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* True when the database is opened with a passphrase (encrypted at rest in the
|
|
170
|
+
* OPFS file). Encrypted databases NEVER write a snapshot to IndexedDB — an
|
|
171
|
+
* exported snapshot is decrypted plaintext, so persisting it would defeat
|
|
172
|
+
* encryption. Encrypted mode therefore requires exclusive OPFS and is
|
|
173
|
+
* single-tab: the multi-tab IDB-snapshot fallback is refused, not silently
|
|
174
|
+
* downgraded to plaintext.
|
|
175
|
+
* @type {boolean}
|
|
176
|
+
*/
|
|
177
|
+
let encrypted = false;
|
|
178
|
+
|
|
165
179
|
/**
|
|
166
180
|
* Timestamp (ms) of the last changeset we exported and broadcast to the
|
|
167
181
|
* primary tab. Used as `sinceMs` for the next export so we only send the
|
|
168
182
|
* delta, not the entire database on every write.
|
|
169
183
|
* @type {number}
|
|
170
184
|
*/
|
|
171
|
-
let lastSecondaryPushMs = 0;
|
|
172
|
-
|
|
173
185
|
// ---------------------------------------------------------------------------
|
|
174
186
|
// IndexedDB helpers (used only when OPFS is unavailable)
|
|
175
187
|
// ---------------------------------------------------------------------------
|
|
@@ -260,10 +272,11 @@ async function flushSnapshot() {
|
|
|
260
272
|
clearTimeout(snapshotTimer);
|
|
261
273
|
snapshotTimer = null;
|
|
262
274
|
lastSnapshotMs = Date.now();
|
|
263
|
-
if (db && activeDbName) {
|
|
275
|
+
if (db && activeDbName && snapshotWriter) {
|
|
264
276
|
try {
|
|
265
277
|
const bytes = db.exportSnapshot();
|
|
266
278
|
await idbSaveSnapshot(activeDbName, bytes);
|
|
279
|
+
broadcastChannel?.postMessage('taladb:snapshot-ready');
|
|
267
280
|
} catch { /* best-effort — ignore failures */ }
|
|
268
281
|
}
|
|
269
282
|
}
|
|
@@ -300,11 +313,13 @@ function pushChangesetToPrimary() {
|
|
|
300
313
|
// Collect all collection names from the current in-memory state.
|
|
301
314
|
// exportChangeset accepts a JSON array of collection names.
|
|
302
315
|
const collections = db.listCollections();
|
|
303
|
-
|
|
316
|
+
// Wall-clock timestamps are not safe incremental cursors: a write can be
|
|
317
|
+
// stamped before an export but commit after its snapshot. Replay is
|
|
318
|
+
// idempotent under LWW and cannot skip such a write.
|
|
319
|
+
const changeset = db.exportChangeset(collections, 0);
|
|
304
320
|
// Only broadcast if there is actually something to send.
|
|
305
321
|
const parsed = JSON.parse(changeset);
|
|
306
322
|
if (parsed.length === 0) return;
|
|
307
|
-
lastSecondaryPushMs = Date.now();
|
|
308
323
|
broadcastChannel.postMessage({ type: 'taladb:secondary-write', token: SESSION_TOKEN, changeset });
|
|
309
324
|
log(`Broadcast ${parsed.length} change(s) to primary tab`);
|
|
310
325
|
} catch (err) {
|
|
@@ -323,7 +338,7 @@ function onWriteCommitted() {
|
|
|
323
338
|
pushChangesetToPrimary();
|
|
324
339
|
// Debounced IDB flush — keeps other tabs' fallback instances in sync via
|
|
325
340
|
// BroadcastChannel + snapshotDirty reload without writing to IDB on every op.
|
|
326
|
-
scheduleSnapshot();
|
|
341
|
+
if (snapshotWriter) scheduleSnapshot();
|
|
327
342
|
}
|
|
328
343
|
|
|
329
344
|
// ---------------------------------------------------------------------------
|
|
@@ -366,7 +381,7 @@ async function dispatch(op, args) {
|
|
|
366
381
|
}
|
|
367
382
|
|
|
368
383
|
if (op === 'init') {
|
|
369
|
-
const { dbName, configJson } = args;
|
|
384
|
+
const { dbName, configJson, passphrase } = args;
|
|
370
385
|
|
|
371
386
|
if (activeDbName !== null && activeDbName !== dbName) {
|
|
372
387
|
throw new Error(
|
|
@@ -378,7 +393,7 @@ async function dispatch(op, args) {
|
|
|
378
393
|
if (!initPromises.has(dbName)) {
|
|
379
394
|
activeDbName = dbName;
|
|
380
395
|
activeConfigJson = configJson ?? null;
|
|
381
|
-
initPromises.set(dbName, doInit(dbName, configJson ?? null));
|
|
396
|
+
initPromises.set(dbName, doInit(dbName, configJson ?? null, passphrase ?? null));
|
|
382
397
|
}
|
|
383
398
|
await initPromises.get(dbName);
|
|
384
399
|
return null;
|
|
@@ -443,6 +458,14 @@ async function dispatch(op, args) {
|
|
|
443
458
|
db.dropIndex(args.collection, args.field);
|
|
444
459
|
return null;
|
|
445
460
|
|
|
461
|
+
case 'createCompoundIndex':
|
|
462
|
+
db.createCompoundIndex(args.collection, args.fieldsJson);
|
|
463
|
+
return null;
|
|
464
|
+
|
|
465
|
+
case 'dropCompoundIndex':
|
|
466
|
+
db.dropCompoundIndex(args.collection, args.fieldsJson);
|
|
467
|
+
return null;
|
|
468
|
+
|
|
446
469
|
case 'createFtsIndex':
|
|
447
470
|
db.createFtsIndex(args.collection, args.field);
|
|
448
471
|
return null;
|
|
@@ -491,6 +514,17 @@ async function dispatch(op, args) {
|
|
|
491
514
|
db.compact();
|
|
492
515
|
return null;
|
|
493
516
|
|
|
517
|
+
case 'syncStatus':
|
|
518
|
+
return db.syncStatus();
|
|
519
|
+
|
|
520
|
+
case 'flushSync': {
|
|
521
|
+
const deadline = Date.now() + (args.timeoutMs ?? 5000);
|
|
522
|
+
while (db.syncPending() > 0 && Date.now() < deadline) {
|
|
523
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
524
|
+
}
|
|
525
|
+
return db.syncPending() === 0;
|
|
526
|
+
}
|
|
527
|
+
|
|
494
528
|
case 'compactTombstones':
|
|
495
529
|
// Prune tombstones older than beforeMs from a collection.
|
|
496
530
|
// Returns the count of tombstones removed.
|
|
@@ -510,6 +544,13 @@ async function dispatch(op, args) {
|
|
|
510
544
|
}
|
|
511
545
|
|
|
512
546
|
case 'close':
|
|
547
|
+
// Give accepted HTTP push events a bounded opportunity to finish.
|
|
548
|
+
{
|
|
549
|
+
const deadline = Date.now() + 5000;
|
|
550
|
+
while (db.syncPending() > 0 && Date.now() < deadline) {
|
|
551
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
513
554
|
// Flush any pending debounced snapshot before releasing the lock so
|
|
514
555
|
// no writes are lost when the tab closes or navigates away.
|
|
515
556
|
await flushSnapshot();
|
|
@@ -518,6 +559,7 @@ async function dispatch(op, args) {
|
|
|
518
559
|
broadcastChannel?.close();
|
|
519
560
|
broadcastChannel = null;
|
|
520
561
|
idbFallback = false;
|
|
562
|
+
snapshotWriter = false;
|
|
521
563
|
db = null;
|
|
522
564
|
return null;
|
|
523
565
|
|
|
@@ -526,26 +568,58 @@ async function dispatch(op, args) {
|
|
|
526
568
|
}
|
|
527
569
|
}
|
|
528
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Load the 16-byte key-derivation salt from an OPFS sidecar file, or create it
|
|
573
|
+
* on first open. The salt is not secret (it defends against precomputed-hash
|
|
574
|
+
* attacks) but must be stable across opens, so it lives beside the DB file.
|
|
575
|
+
* @returns {Promise<Uint8Array>} the 16-byte salt
|
|
576
|
+
*/
|
|
577
|
+
async function loadOrCreateSalt(root, saltFileName) {
|
|
578
|
+
const fh = await root.getFileHandle(saltFileName, { create: true });
|
|
579
|
+
const h = await fh.createSyncAccessHandle();
|
|
580
|
+
try {
|
|
581
|
+
const size = h.getSize();
|
|
582
|
+
if (size === 16) {
|
|
583
|
+
const salt = new Uint8Array(16);
|
|
584
|
+
h.read(salt, { at: 0 });
|
|
585
|
+
return salt;
|
|
586
|
+
}
|
|
587
|
+
if (size === 0) {
|
|
588
|
+
const salt = new Uint8Array(16);
|
|
589
|
+
self.crypto.getRandomValues(salt);
|
|
590
|
+
h.write(salt, { at: 0 });
|
|
591
|
+
h.flush();
|
|
592
|
+
return salt;
|
|
593
|
+
}
|
|
594
|
+
throw new Error(`invalid TalaDB salt file (${size} bytes, expected 16)`);
|
|
595
|
+
} finally {
|
|
596
|
+
h.close();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
529
600
|
// ---------------------------------------------------------------------------
|
|
530
601
|
// Initialisation — load WASM, acquire lock, open OPFS file
|
|
531
602
|
// ---------------------------------------------------------------------------
|
|
532
603
|
|
|
533
|
-
async function doInit(dbName, configJson) {
|
|
604
|
+
async function doInit(dbName, configJson, passphrase = null) {
|
|
534
605
|
const wasm = await import(/* @vite-ignore */ '../pkg/taladb_web.js');
|
|
535
606
|
await wasm.default();
|
|
536
607
|
|
|
537
608
|
// Hoist to module scope so snapshot reloads in dispatch() can use it.
|
|
538
609
|
WorkerDB = wasm.WorkerDB;
|
|
539
610
|
|
|
611
|
+
encrypted = typeof passphrase === 'string' && passphrase.length > 0;
|
|
612
|
+
|
|
540
613
|
// Open the BroadcastChannel now that we know the db name.
|
|
541
614
|
if (typeof BroadcastChannel !== 'undefined') {
|
|
542
615
|
broadcastChannel = new BroadcastChannel(`taladb:${dbName}`);
|
|
543
616
|
broadcastChannel.onmessage = async (e) => {
|
|
544
617
|
if (e.data === 'taladb:changed' && idbFallback) {
|
|
545
|
-
//
|
|
546
|
-
|
|
547
|
-
} else if (e.data === 'taladb:request-snapshot' && !idbFallback && db && activeDbName) {
|
|
618
|
+
// Wait for snapshot-ready: changed is emitted before the primary's
|
|
619
|
+
// asynchronous IDB transaction has committed.
|
|
620
|
+
} else if (e.data === 'taladb:request-snapshot' && !idbFallback && !encrypted && db && activeDbName) {
|
|
548
621
|
// Primary (OPFS) tab: a new tab asked for a snapshot — export and save it.
|
|
622
|
+
// Never for encrypted DBs: an exported snapshot is decrypted plaintext.
|
|
549
623
|
try {
|
|
550
624
|
const bytes = db.exportSnapshot();
|
|
551
625
|
await idbSaveSnapshot(activeDbName, bytes);
|
|
@@ -557,8 +631,15 @@ async function doInit(dbName, configJson) {
|
|
|
557
631
|
const resolve = pendingSnapshotResolve;
|
|
558
632
|
pendingSnapshotResolve = null;
|
|
559
633
|
resolve();
|
|
560
|
-
} else if (e.data
|
|
561
|
-
|
|
634
|
+
} else if (e.data === 'taladb:snapshot-ready' && idbFallback) {
|
|
635
|
+
snapshotDirty = true;
|
|
636
|
+
} else if (e.data?.type === 'taladb:secondary-write' && typeof e.data.token === 'string' && e.data.token.length > 0 && db && !encrypted) {
|
|
637
|
+
// `!encrypted`: encrypted databases are single-tab, so there are no
|
|
638
|
+
// legitimate secondary-tab writes — accepting them would let any
|
|
639
|
+
// same-origin script inject documents into an encrypted DB without
|
|
640
|
+
// knowing the passphrase.
|
|
641
|
+
// Merge peer writes in every mode. This also makes multiple tabs
|
|
642
|
+
// converge when OPFS is entirely unavailable and every tab uses IDB.
|
|
562
643
|
// The token requirement rejects unauthenticated injection attempts that
|
|
563
644
|
// omit the SESSION_TOKEN field (not a guarantee against same-origin attackers
|
|
564
645
|
// who observe the token, but defence-in-depth against naive injection).
|
|
@@ -583,17 +664,27 @@ async function doInit(dbName, configJson) {
|
|
|
583
664
|
if (configJson) return WorkerDB.openWithConfigAndSnapshot(snapshot, configJson);
|
|
584
665
|
return WorkerDB.openWithSnapshot(snapshot);
|
|
585
666
|
}
|
|
667
|
+
// Salt for key derivation, loaded/created in an OPFS sidecar (encrypted mode
|
|
668
|
+
// only). Passed to the WASM open so the derived key is stable across opens.
|
|
669
|
+
let salt = null;
|
|
586
670
|
function openWithOpfs(syncHandle) {
|
|
587
|
-
|
|
588
|
-
return WorkerDB.
|
|
671
|
+
// openWithConfigAndOpfs(handle, configJson, passphrase?, salt?)
|
|
672
|
+
return WorkerDB.openWithConfigAndOpfs(syncHandle, configJson ?? null, passphrase, salt);
|
|
589
673
|
}
|
|
590
674
|
|
|
591
675
|
const opfsAvailable = await checkOpfs();
|
|
592
676
|
if (!opfsAvailable) {
|
|
677
|
+
if (encrypted) {
|
|
678
|
+
throw new Error(
|
|
679
|
+
'TalaDB encryption requires OPFS, which is unavailable in this browser context. ' +
|
|
680
|
+
'Refusing to open — the in-memory/IndexedDB fallback cannot encrypt at rest.'
|
|
681
|
+
);
|
|
682
|
+
}
|
|
593
683
|
warn('OPFS unavailable — falling back to IndexedDB-backed in-memory');
|
|
594
684
|
const snapshot = await idbLoadSnapshot(dbName);
|
|
595
685
|
db = openWithSnapshot(snapshot);
|
|
596
686
|
idbFallback = true;
|
|
687
|
+
snapshotWriter = true;
|
|
597
688
|
if (snapshot) {
|
|
598
689
|
log(`Restored from IndexedDB snapshot (${snapshot.byteLength} bytes)`);
|
|
599
690
|
} else {
|
|
@@ -606,11 +697,26 @@ async function doInit(dbName, configJson) {
|
|
|
606
697
|
const fileName = `taladb_${dbName.replaceAll(/[/\\:]/g, '_')}.redb`;
|
|
607
698
|
const fileHandle = await root.getFileHandle(fileName, { create: true });
|
|
608
699
|
|
|
700
|
+
// Encrypted mode: the 16-byte key-derivation salt lives in an OPFS sidecar
|
|
701
|
+
// file next to the DB. It is loaded/created only AFTER this tab has won the
|
|
702
|
+
// exclusive lock (or determined no locking applies), so two tabs racing to
|
|
703
|
+
// open never collide on the salt file's exclusive access handle.
|
|
704
|
+
|
|
609
705
|
if (!('locks' in navigator)) {
|
|
610
706
|
// Web Locks not available — open directly (single-tab safe only).
|
|
611
707
|
warn('Web Locks unavailable — multi-tab write safety disabled');
|
|
708
|
+
if (encrypted) salt = await loadOrCreateSalt(root, `${fileName}.salt`);
|
|
612
709
|
const syncHandle = await fileHandle.createSyncAccessHandle();
|
|
613
|
-
|
|
710
|
+
try {
|
|
711
|
+
db = openWithOpfs(syncHandle);
|
|
712
|
+
} catch (e) {
|
|
713
|
+
// A failed open (e.g. wrong passphrase) must release the exclusive OPFS
|
|
714
|
+
// access handle, or every retry fails with "Access Handles cannot be
|
|
715
|
+
// created" until the page reloads.
|
|
716
|
+
try { syncHandle.close(); } catch { /* best-effort */ }
|
|
717
|
+
throw e;
|
|
718
|
+
}
|
|
719
|
+
snapshotWriter = !encrypted; // encrypted DBs never write a plaintext IDB snapshot
|
|
614
720
|
log(`Opened "${fileName}" via OPFS`);
|
|
615
721
|
return;
|
|
616
722
|
}
|
|
@@ -625,7 +731,18 @@ async function doInit(dbName, configJson) {
|
|
|
625
731
|
await new Promise((resolve, reject) => {
|
|
626
732
|
navigator.locks.request(lockName, { ifAvailable: true }, async (lock) => {
|
|
627
733
|
if (lock === null) {
|
|
628
|
-
// Lock is held by another tab
|
|
734
|
+
// Lock is held by another tab.
|
|
735
|
+
if (encrypted) {
|
|
736
|
+
// The IDB-snapshot fallback stores decrypted plaintext, so it's
|
|
737
|
+
// refused for encrypted databases — they're single-tab. Reject
|
|
738
|
+
// rather than downgrade.
|
|
739
|
+
reject(new Error(
|
|
740
|
+
'This encrypted TalaDB database is already open in another tab. ' +
|
|
741
|
+
'Encrypted browser databases are single-tab (the multi-tab fallback would store plaintext).'
|
|
742
|
+
));
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
// Unencrypted: use IDB snapshot so this tab loads immediately.
|
|
629
746
|
warn('OPFS lock held by another tab — falling back to IndexedDB snapshot (live-sync via BroadcastChannel)');
|
|
630
747
|
let snapshot = await idbLoadSnapshot(dbName);
|
|
631
748
|
|
|
@@ -642,6 +759,7 @@ async function doInit(dbName, configJson) {
|
|
|
642
759
|
|
|
643
760
|
db = openWithSnapshot(snapshot ?? null);
|
|
644
761
|
idbFallback = true;
|
|
762
|
+
snapshotWriter = false;
|
|
645
763
|
if (snapshot) {
|
|
646
764
|
log(`Restored from IDB snapshot (${snapshot.byteLength} bytes)`);
|
|
647
765
|
} else {
|
|
@@ -652,9 +770,12 @@ async function doInit(dbName, configJson) {
|
|
|
652
770
|
}
|
|
653
771
|
|
|
654
772
|
// Acquired the lock — use OPFS.
|
|
773
|
+
let syncHandle = null;
|
|
655
774
|
try {
|
|
656
|
-
|
|
775
|
+
if (encrypted) salt = await loadOrCreateSalt(root, `${fileName}.salt`);
|
|
776
|
+
syncHandle = await fileHandle.createSyncAccessHandle();
|
|
657
777
|
db = openWithOpfs(syncHandle);
|
|
778
|
+
snapshotWriter = !encrypted; // encrypted DBs never write a plaintext IDB snapshot
|
|
658
779
|
log(`Opened "${fileName}" via OPFS (Web Locks)`);
|
|
659
780
|
resolve(); // signal doInit complete — caller can proceed
|
|
660
781
|
|
|
@@ -665,6 +786,13 @@ async function doInit(dbName, configJson) {
|
|
|
665
786
|
syncHandle.close();
|
|
666
787
|
db = null;
|
|
667
788
|
} catch (e) {
|
|
789
|
+
// A failed open (e.g. wrong passphrase) must release the exclusive
|
|
790
|
+
// OPFS access handle, or every retry fails with "Access Handles
|
|
791
|
+
// cannot be created" until the page reloads. Returning from this
|
|
792
|
+
// callback also releases the Web Lock.
|
|
793
|
+
if (syncHandle) {
|
|
794
|
+
try { syncHandle.close(); } catch { /* best-effort */ }
|
|
795
|
+
}
|
|
668
796
|
reject(e);
|
|
669
797
|
}
|
|
670
798
|
});
|