taladb 0.7.10 → 0.8.2
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 +34 -13
- package/dist/index.js +47 -13
- package/dist/index.mjs +47 -13
- package/dist/index.react-native.mjs +34 -13
- package/package.json +1 -1
package/dist/index.browser.mjs
CHANGED
|
@@ -84,6 +84,7 @@ var WorkerProxy = class {
|
|
|
84
84
|
constructor(port) {
|
|
85
85
|
this.pending = /* @__PURE__ */ new Map();
|
|
86
86
|
this.nextId = 1;
|
|
87
|
+
this.dead = null;
|
|
87
88
|
this.port = port;
|
|
88
89
|
this.port.onmessage = (e) => {
|
|
89
90
|
const { id, result, error } = e.data;
|
|
@@ -97,12 +98,23 @@ var WorkerProxy = class {
|
|
|
97
98
|
this.port.start?.();
|
|
98
99
|
}
|
|
99
100
|
send(op, args = {}) {
|
|
101
|
+
if (this.dead) return Promise.reject(this.dead);
|
|
100
102
|
return new Promise((resolve, reject) => {
|
|
101
103
|
const id = this.nextId++;
|
|
102
104
|
this.pending.set(id, { resolve, reject });
|
|
103
105
|
this.port.postMessage({ id, op, ...args });
|
|
104
106
|
});
|
|
105
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Reject every in-flight request and refuse new ones. Called when the
|
|
110
|
+
* worker errors or is terminated — without this, pending promises would
|
|
111
|
+
* hang forever (awaiting callers deadlock).
|
|
112
|
+
*/
|
|
113
|
+
abort(reason) {
|
|
114
|
+
this.dead = reason;
|
|
115
|
+
for (const [, p] of this.pending) p.reject(reason);
|
|
116
|
+
this.pending.clear();
|
|
117
|
+
}
|
|
106
118
|
};
|
|
107
119
|
function makePoller(findFn, callback) {
|
|
108
120
|
let active = true;
|
|
@@ -129,6 +141,9 @@ async function createBrowserDB(dbName, config) {
|
|
|
129
141
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import.meta.url);
|
|
130
142
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
131
143
|
const proxy = new WorkerProxy(worker);
|
|
144
|
+
worker.onerror = (e) => {
|
|
145
|
+
proxy.abort(new Error(`taladb worker error: ${e.message ?? "unknown"}`));
|
|
146
|
+
};
|
|
132
147
|
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
133
148
|
await proxy.send("init", { dbName, configJson });
|
|
134
149
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -216,7 +231,7 @@ async function createBrowserDB(dbName, config) {
|
|
|
216
231
|
},
|
|
217
232
|
subscribe: (filter, callback) => {
|
|
218
233
|
let active = true;
|
|
219
|
-
let lastJson = "
|
|
234
|
+
let lastJson = "";
|
|
220
235
|
let timer = null;
|
|
221
236
|
const poll = async () => {
|
|
222
237
|
if (!active) return;
|
|
@@ -256,8 +271,12 @@ async function createBrowserDB(dbName, config) {
|
|
|
256
271
|
compact: () => proxy.send("compact"),
|
|
257
272
|
close: async () => {
|
|
258
273
|
channel?.close();
|
|
259
|
-
|
|
260
|
-
|
|
274
|
+
try {
|
|
275
|
+
await proxy.send("close");
|
|
276
|
+
} finally {
|
|
277
|
+
worker.terminate();
|
|
278
|
+
proxy.abort(new Error("taladb worker closed"));
|
|
279
|
+
}
|
|
261
280
|
}
|
|
262
281
|
};
|
|
263
282
|
}
|
|
@@ -268,14 +287,14 @@ async function createNodeDB(dbName, config) {
|
|
|
268
287
|
function wrapCollection(name, opts) {
|
|
269
288
|
const col = db.collection(name);
|
|
270
289
|
const wrapped = {
|
|
271
|
-
insert: async (doc) => col.insert(doc),
|
|
272
|
-
insertMany: async (docs) => col.insertMany(docs),
|
|
273
|
-
find: async (filter) => col.find(filter ?? null),
|
|
290
|
+
insert: async (doc) => col.insertAsync ? col.insertAsync(doc) : col.insert(doc),
|
|
291
|
+
insertMany: async (docs) => col.insertManyAsync ? col.insertManyAsync(docs) : col.insertMany(docs),
|
|
292
|
+
find: async (filter) => col.findAsync ? col.findAsync(filter ?? null) : col.find(filter ?? null),
|
|
274
293
|
findOne: async (filter) => col.findOne(filter) ?? null,
|
|
275
|
-
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
276
|
-
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
277
|
-
deleteOne: async (filter) => col.deleteOne(filter),
|
|
278
|
-
deleteMany: async (filter) => col.deleteMany(filter),
|
|
294
|
+
updateOne: async (filter, update) => col.updateOneAsync ? col.updateOneAsync(filter, update) : col.updateOne(filter, update),
|
|
295
|
+
updateMany: async (filter, update) => col.updateManyAsync ? col.updateManyAsync(filter, update) : col.updateMany(filter, update),
|
|
296
|
+
deleteOne: async (filter) => col.deleteOneAsync ? col.deleteOneAsync(filter) : col.deleteOne(filter),
|
|
297
|
+
deleteMany: async (filter) => col.deleteManyAsync ? col.deleteManyAsync(filter) : col.deleteMany(filter),
|
|
279
298
|
count: async (filter) => col.count(filter ?? null),
|
|
280
299
|
createIndex: async (field) => col.createIndex(field),
|
|
281
300
|
dropIndex: async (field) => col.dropIndex(field),
|
|
@@ -299,8 +318,8 @@ async function createNodeDB(dbName, config) {
|
|
|
299
318
|
return {
|
|
300
319
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
301
320
|
compact: async () => db.compact(),
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
// Releases the native file handle/lock (no-op on older .node binaries).
|
|
322
|
+
close: async () => db.close?.()
|
|
304
323
|
};
|
|
305
324
|
}
|
|
306
325
|
async function createNativeDB(_dbName) {
|
|
@@ -336,7 +355,9 @@ async function createNativeDB(_dbName) {
|
|
|
336
355
|
},
|
|
337
356
|
dropVectorIndex: async (field) => native.dropVectorIndex(name, field),
|
|
338
357
|
upgradeVectorIndex: async (field) => native.upgradeVectorIndex(name, field),
|
|
339
|
-
|
|
358
|
+
// The JSI HostObject does not expose index introspection yet; return a
|
|
359
|
+
// correctly-shaped empty result rather than `{}` cast to the interface.
|
|
360
|
+
listIndexes: async () => ({ btree: [], fts: [], vector: [] }),
|
|
340
361
|
findNearest: async (field, vector, topK, filter) => {
|
|
341
362
|
const raw = native.findNearest(name, field, vector, topK, filter ?? null);
|
|
342
363
|
return raw;
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,14 @@ var ENDPOINT_FIELDS = [
|
|
|
42
42
|
"update_endpoint",
|
|
43
43
|
"delete_endpoint"
|
|
44
44
|
];
|
|
45
|
+
var LOCALHOST_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
46
|
+
function isLocalhostUrl(url) {
|
|
47
|
+
try {
|
|
48
|
+
return LOCALHOST_HOSTS.has(new URL(url).hostname);
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
45
53
|
function validateConfig(config) {
|
|
46
54
|
const sync = config.sync;
|
|
47
55
|
if (!sync) return;
|
|
@@ -52,6 +60,11 @@ function validateConfig(config) {
|
|
|
52
60
|
`TalaDB config: invalid endpoint URL "${url}" \u2014 must start with http:// or https://`
|
|
53
61
|
);
|
|
54
62
|
}
|
|
63
|
+
if (url?.startsWith("http://") && !isLocalhostUrl(url)) {
|
|
64
|
+
console.warn(
|
|
65
|
+
`[TalaDB] sync endpoint "${url}" uses plaintext HTTP \u2014 use HTTPS in production to prevent changeset interception`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
70
|
async function loadConfig(configPath) {
|
|
@@ -166,6 +179,7 @@ var WorkerProxy = class {
|
|
|
166
179
|
constructor(port) {
|
|
167
180
|
this.pending = /* @__PURE__ */ new Map();
|
|
168
181
|
this.nextId = 1;
|
|
182
|
+
this.dead = null;
|
|
169
183
|
this.port = port;
|
|
170
184
|
this.port.onmessage = (e) => {
|
|
171
185
|
const { id, result, error } = e.data;
|
|
@@ -179,12 +193,23 @@ var WorkerProxy = class {
|
|
|
179
193
|
this.port.start?.();
|
|
180
194
|
}
|
|
181
195
|
send(op, args = {}) {
|
|
196
|
+
if (this.dead) return Promise.reject(this.dead);
|
|
182
197
|
return new Promise((resolve, reject) => {
|
|
183
198
|
const id = this.nextId++;
|
|
184
199
|
this.pending.set(id, { resolve, reject });
|
|
185
200
|
this.port.postMessage({ id, op, ...args });
|
|
186
201
|
});
|
|
187
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Reject every in-flight request and refuse new ones. Called when the
|
|
205
|
+
* worker errors or is terminated — without this, pending promises would
|
|
206
|
+
* hang forever (awaiting callers deadlock).
|
|
207
|
+
*/
|
|
208
|
+
abort(reason) {
|
|
209
|
+
this.dead = reason;
|
|
210
|
+
for (const [, p] of this.pending) p.reject(reason);
|
|
211
|
+
this.pending.clear();
|
|
212
|
+
}
|
|
188
213
|
};
|
|
189
214
|
function makePoller(findFn, callback) {
|
|
190
215
|
let active = true;
|
|
@@ -211,6 +236,9 @@ async function createBrowserDB(dbName, config) {
|
|
|
211
236
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import_meta.url);
|
|
212
237
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
213
238
|
const proxy = new WorkerProxy(worker);
|
|
239
|
+
worker.onerror = (e) => {
|
|
240
|
+
proxy.abort(new Error(`taladb worker error: ${e.message ?? "unknown"}`));
|
|
241
|
+
};
|
|
214
242
|
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
215
243
|
await proxy.send("init", { dbName, configJson });
|
|
216
244
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -298,7 +326,7 @@ async function createBrowserDB(dbName, config) {
|
|
|
298
326
|
},
|
|
299
327
|
subscribe: (filter, callback) => {
|
|
300
328
|
let active = true;
|
|
301
|
-
let lastJson = "
|
|
329
|
+
let lastJson = "";
|
|
302
330
|
let timer = null;
|
|
303
331
|
const poll = async () => {
|
|
304
332
|
if (!active) return;
|
|
@@ -338,8 +366,12 @@ async function createBrowserDB(dbName, config) {
|
|
|
338
366
|
compact: () => proxy.send("compact"),
|
|
339
367
|
close: async () => {
|
|
340
368
|
channel?.close();
|
|
341
|
-
|
|
342
|
-
|
|
369
|
+
try {
|
|
370
|
+
await proxy.send("close");
|
|
371
|
+
} finally {
|
|
372
|
+
worker.terminate();
|
|
373
|
+
proxy.abort(new Error("taladb worker closed"));
|
|
374
|
+
}
|
|
343
375
|
}
|
|
344
376
|
};
|
|
345
377
|
}
|
|
@@ -350,14 +382,14 @@ async function createNodeDB(dbName, config) {
|
|
|
350
382
|
function wrapCollection(name, opts) {
|
|
351
383
|
const col = db.collection(name);
|
|
352
384
|
const wrapped = {
|
|
353
|
-
insert: async (doc) => col.insert(doc),
|
|
354
|
-
insertMany: async (docs) => col.insertMany(docs),
|
|
355
|
-
find: async (filter) => col.find(filter ?? null),
|
|
385
|
+
insert: async (doc) => col.insertAsync ? col.insertAsync(doc) : col.insert(doc),
|
|
386
|
+
insertMany: async (docs) => col.insertManyAsync ? col.insertManyAsync(docs) : col.insertMany(docs),
|
|
387
|
+
find: async (filter) => col.findAsync ? col.findAsync(filter ?? null) : col.find(filter ?? null),
|
|
356
388
|
findOne: async (filter) => col.findOne(filter) ?? null,
|
|
357
|
-
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
358
|
-
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
359
|
-
deleteOne: async (filter) => col.deleteOne(filter),
|
|
360
|
-
deleteMany: async (filter) => col.deleteMany(filter),
|
|
389
|
+
updateOne: async (filter, update) => col.updateOneAsync ? col.updateOneAsync(filter, update) : col.updateOne(filter, update),
|
|
390
|
+
updateMany: async (filter, update) => col.updateManyAsync ? col.updateManyAsync(filter, update) : col.updateMany(filter, update),
|
|
391
|
+
deleteOne: async (filter) => col.deleteOneAsync ? col.deleteOneAsync(filter) : col.deleteOne(filter),
|
|
392
|
+
deleteMany: async (filter) => col.deleteManyAsync ? col.deleteManyAsync(filter) : col.deleteMany(filter),
|
|
361
393
|
count: async (filter) => col.count(filter ?? null),
|
|
362
394
|
createIndex: async (field) => col.createIndex(field),
|
|
363
395
|
dropIndex: async (field) => col.dropIndex(field),
|
|
@@ -381,8 +413,8 @@ async function createNodeDB(dbName, config) {
|
|
|
381
413
|
return {
|
|
382
414
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
383
415
|
compact: async () => db.compact(),
|
|
384
|
-
|
|
385
|
-
|
|
416
|
+
// Releases the native file handle/lock (no-op on older .node binaries).
|
|
417
|
+
close: async () => db.close?.()
|
|
386
418
|
};
|
|
387
419
|
}
|
|
388
420
|
async function createNativeDB(_dbName) {
|
|
@@ -418,7 +450,9 @@ async function createNativeDB(_dbName) {
|
|
|
418
450
|
},
|
|
419
451
|
dropVectorIndex: async (field) => native.dropVectorIndex(name, field),
|
|
420
452
|
upgradeVectorIndex: async (field) => native.upgradeVectorIndex(name, field),
|
|
421
|
-
|
|
453
|
+
// The JSI HostObject does not expose index introspection yet; return a
|
|
454
|
+
// correctly-shaped empty result rather than `{}` cast to the interface.
|
|
455
|
+
listIndexes: async () => ({ btree: [], fts: [], vector: [] }),
|
|
422
456
|
findNearest: async (field, vector, topK, filter) => {
|
|
423
457
|
const raw = native.findNearest(name, field, vector, topK, filter ?? null);
|
|
424
458
|
return raw;
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,14 @@ var ENDPOINT_FIELDS = [
|
|
|
5
5
|
"update_endpoint",
|
|
6
6
|
"delete_endpoint"
|
|
7
7
|
];
|
|
8
|
+
var LOCALHOST_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
9
|
+
function isLocalhostUrl(url) {
|
|
10
|
+
try {
|
|
11
|
+
return LOCALHOST_HOSTS.has(new URL(url).hostname);
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
8
16
|
function validateConfig(config) {
|
|
9
17
|
const sync = config.sync;
|
|
10
18
|
if (!sync) return;
|
|
@@ -15,6 +23,11 @@ function validateConfig(config) {
|
|
|
15
23
|
`TalaDB config: invalid endpoint URL "${url}" \u2014 must start with http:// or https://`
|
|
16
24
|
);
|
|
17
25
|
}
|
|
26
|
+
if (url?.startsWith("http://") && !isLocalhostUrl(url)) {
|
|
27
|
+
console.warn(
|
|
28
|
+
`[TalaDB] sync endpoint "${url}" uses plaintext HTTP \u2014 use HTTPS in production to prevent changeset interception`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
18
31
|
}
|
|
19
32
|
}
|
|
20
33
|
async function loadConfig(configPath) {
|
|
@@ -128,6 +141,7 @@ var WorkerProxy = class {
|
|
|
128
141
|
constructor(port) {
|
|
129
142
|
this.pending = /* @__PURE__ */ new Map();
|
|
130
143
|
this.nextId = 1;
|
|
144
|
+
this.dead = null;
|
|
131
145
|
this.port = port;
|
|
132
146
|
this.port.onmessage = (e) => {
|
|
133
147
|
const { id, result, error } = e.data;
|
|
@@ -141,12 +155,23 @@ var WorkerProxy = class {
|
|
|
141
155
|
this.port.start?.();
|
|
142
156
|
}
|
|
143
157
|
send(op, args = {}) {
|
|
158
|
+
if (this.dead) return Promise.reject(this.dead);
|
|
144
159
|
return new Promise((resolve, reject) => {
|
|
145
160
|
const id = this.nextId++;
|
|
146
161
|
this.pending.set(id, { resolve, reject });
|
|
147
162
|
this.port.postMessage({ id, op, ...args });
|
|
148
163
|
});
|
|
149
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Reject every in-flight request and refuse new ones. Called when the
|
|
167
|
+
* worker errors or is terminated — without this, pending promises would
|
|
168
|
+
* hang forever (awaiting callers deadlock).
|
|
169
|
+
*/
|
|
170
|
+
abort(reason) {
|
|
171
|
+
this.dead = reason;
|
|
172
|
+
for (const [, p] of this.pending) p.reject(reason);
|
|
173
|
+
this.pending.clear();
|
|
174
|
+
}
|
|
150
175
|
};
|
|
151
176
|
function makePoller(findFn, callback) {
|
|
152
177
|
let active = true;
|
|
@@ -173,6 +198,9 @@ async function createBrowserDB(dbName, config) {
|
|
|
173
198
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import.meta.url);
|
|
174
199
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
175
200
|
const proxy = new WorkerProxy(worker);
|
|
201
|
+
worker.onerror = (e) => {
|
|
202
|
+
proxy.abort(new Error(`taladb worker error: ${e.message ?? "unknown"}`));
|
|
203
|
+
};
|
|
176
204
|
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
177
205
|
await proxy.send("init", { dbName, configJson });
|
|
178
206
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -260,7 +288,7 @@ async function createBrowserDB(dbName, config) {
|
|
|
260
288
|
},
|
|
261
289
|
subscribe: (filter, callback) => {
|
|
262
290
|
let active = true;
|
|
263
|
-
let lastJson = "
|
|
291
|
+
let lastJson = "";
|
|
264
292
|
let timer = null;
|
|
265
293
|
const poll = async () => {
|
|
266
294
|
if (!active) return;
|
|
@@ -300,8 +328,12 @@ async function createBrowserDB(dbName, config) {
|
|
|
300
328
|
compact: () => proxy.send("compact"),
|
|
301
329
|
close: async () => {
|
|
302
330
|
channel?.close();
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
try {
|
|
332
|
+
await proxy.send("close");
|
|
333
|
+
} finally {
|
|
334
|
+
worker.terminate();
|
|
335
|
+
proxy.abort(new Error("taladb worker closed"));
|
|
336
|
+
}
|
|
305
337
|
}
|
|
306
338
|
};
|
|
307
339
|
}
|
|
@@ -312,14 +344,14 @@ async function createNodeDB(dbName, config) {
|
|
|
312
344
|
function wrapCollection(name, opts) {
|
|
313
345
|
const col = db.collection(name);
|
|
314
346
|
const wrapped = {
|
|
315
|
-
insert: async (doc) => col.insert(doc),
|
|
316
|
-
insertMany: async (docs) => col.insertMany(docs),
|
|
317
|
-
find: async (filter) => col.find(filter ?? null),
|
|
347
|
+
insert: async (doc) => col.insertAsync ? col.insertAsync(doc) : col.insert(doc),
|
|
348
|
+
insertMany: async (docs) => col.insertManyAsync ? col.insertManyAsync(docs) : col.insertMany(docs),
|
|
349
|
+
find: async (filter) => col.findAsync ? col.findAsync(filter ?? null) : col.find(filter ?? null),
|
|
318
350
|
findOne: async (filter) => col.findOne(filter) ?? null,
|
|
319
|
-
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
320
|
-
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
321
|
-
deleteOne: async (filter) => col.deleteOne(filter),
|
|
322
|
-
deleteMany: async (filter) => col.deleteMany(filter),
|
|
351
|
+
updateOne: async (filter, update) => col.updateOneAsync ? col.updateOneAsync(filter, update) : col.updateOne(filter, update),
|
|
352
|
+
updateMany: async (filter, update) => col.updateManyAsync ? col.updateManyAsync(filter, update) : col.updateMany(filter, update),
|
|
353
|
+
deleteOne: async (filter) => col.deleteOneAsync ? col.deleteOneAsync(filter) : col.deleteOne(filter),
|
|
354
|
+
deleteMany: async (filter) => col.deleteManyAsync ? col.deleteManyAsync(filter) : col.deleteMany(filter),
|
|
323
355
|
count: async (filter) => col.count(filter ?? null),
|
|
324
356
|
createIndex: async (field) => col.createIndex(field),
|
|
325
357
|
dropIndex: async (field) => col.dropIndex(field),
|
|
@@ -343,8 +375,8 @@ async function createNodeDB(dbName, config) {
|
|
|
343
375
|
return {
|
|
344
376
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
345
377
|
compact: async () => db.compact(),
|
|
346
|
-
|
|
347
|
-
|
|
378
|
+
// Releases the native file handle/lock (no-op on older .node binaries).
|
|
379
|
+
close: async () => db.close?.()
|
|
348
380
|
};
|
|
349
381
|
}
|
|
350
382
|
async function createNativeDB(_dbName) {
|
|
@@ -380,7 +412,9 @@ async function createNativeDB(_dbName) {
|
|
|
380
412
|
},
|
|
381
413
|
dropVectorIndex: async (field) => native.dropVectorIndex(name, field),
|
|
382
414
|
upgradeVectorIndex: async (field) => native.upgradeVectorIndex(name, field),
|
|
383
|
-
|
|
415
|
+
// The JSI HostObject does not expose index introspection yet; return a
|
|
416
|
+
// correctly-shaped empty result rather than `{}` cast to the interface.
|
|
417
|
+
listIndexes: async () => ({ btree: [], fts: [], vector: [] }),
|
|
384
418
|
findNearest: async (field, vector, topK, filter) => {
|
|
385
419
|
const raw = native.findNearest(name, field, vector, topK, filter ?? null);
|
|
386
420
|
return raw;
|
|
@@ -84,6 +84,7 @@ var WorkerProxy = class {
|
|
|
84
84
|
constructor(port) {
|
|
85
85
|
this.pending = /* @__PURE__ */ new Map();
|
|
86
86
|
this.nextId = 1;
|
|
87
|
+
this.dead = null;
|
|
87
88
|
this.port = port;
|
|
88
89
|
this.port.onmessage = (e) => {
|
|
89
90
|
const { id, result, error } = e.data;
|
|
@@ -97,12 +98,23 @@ var WorkerProxy = class {
|
|
|
97
98
|
this.port.start?.();
|
|
98
99
|
}
|
|
99
100
|
send(op, args = {}) {
|
|
101
|
+
if (this.dead) return Promise.reject(this.dead);
|
|
100
102
|
return new Promise((resolve, reject) => {
|
|
101
103
|
const id = this.nextId++;
|
|
102
104
|
this.pending.set(id, { resolve, reject });
|
|
103
105
|
this.port.postMessage({ id, op, ...args });
|
|
104
106
|
});
|
|
105
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Reject every in-flight request and refuse new ones. Called when the
|
|
110
|
+
* worker errors or is terminated — without this, pending promises would
|
|
111
|
+
* hang forever (awaiting callers deadlock).
|
|
112
|
+
*/
|
|
113
|
+
abort(reason) {
|
|
114
|
+
this.dead = reason;
|
|
115
|
+
for (const [, p] of this.pending) p.reject(reason);
|
|
116
|
+
this.pending.clear();
|
|
117
|
+
}
|
|
106
118
|
};
|
|
107
119
|
function makePoller(findFn, callback) {
|
|
108
120
|
let active = true;
|
|
@@ -129,6 +141,9 @@ async function createBrowserDB(dbName, config) {
|
|
|
129
141
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", "react-native://unreachable");
|
|
130
142
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
131
143
|
const proxy = new WorkerProxy(worker);
|
|
144
|
+
worker.onerror = (e) => {
|
|
145
|
+
proxy.abort(new Error(`taladb worker error: ${e.message ?? "unknown"}`));
|
|
146
|
+
};
|
|
132
147
|
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
133
148
|
await proxy.send("init", { dbName, configJson });
|
|
134
149
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -216,7 +231,7 @@ async function createBrowserDB(dbName, config) {
|
|
|
216
231
|
},
|
|
217
232
|
subscribe: (filter, callback) => {
|
|
218
233
|
let active = true;
|
|
219
|
-
let lastJson = "
|
|
234
|
+
let lastJson = "";
|
|
220
235
|
let timer = null;
|
|
221
236
|
const poll = async () => {
|
|
222
237
|
if (!active) return;
|
|
@@ -256,8 +271,12 @@ async function createBrowserDB(dbName, config) {
|
|
|
256
271
|
compact: () => proxy.send("compact"),
|
|
257
272
|
close: async () => {
|
|
258
273
|
channel?.close();
|
|
259
|
-
|
|
260
|
-
|
|
274
|
+
try {
|
|
275
|
+
await proxy.send("close");
|
|
276
|
+
} finally {
|
|
277
|
+
worker.terminate();
|
|
278
|
+
proxy.abort(new Error("taladb worker closed"));
|
|
279
|
+
}
|
|
261
280
|
}
|
|
262
281
|
};
|
|
263
282
|
}
|
|
@@ -268,14 +287,14 @@ async function createNodeDB(dbName, config) {
|
|
|
268
287
|
function wrapCollection(name, opts) {
|
|
269
288
|
const col = db.collection(name);
|
|
270
289
|
const wrapped = {
|
|
271
|
-
insert: async (doc) => col.insert(doc),
|
|
272
|
-
insertMany: async (docs) => col.insertMany(docs),
|
|
273
|
-
find: async (filter) => col.find(filter ?? null),
|
|
290
|
+
insert: async (doc) => col.insertAsync ? col.insertAsync(doc) : col.insert(doc),
|
|
291
|
+
insertMany: async (docs) => col.insertManyAsync ? col.insertManyAsync(docs) : col.insertMany(docs),
|
|
292
|
+
find: async (filter) => col.findAsync ? col.findAsync(filter ?? null) : col.find(filter ?? null),
|
|
274
293
|
findOne: async (filter) => col.findOne(filter) ?? null,
|
|
275
|
-
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
276
|
-
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
277
|
-
deleteOne: async (filter) => col.deleteOne(filter),
|
|
278
|
-
deleteMany: async (filter) => col.deleteMany(filter),
|
|
294
|
+
updateOne: async (filter, update) => col.updateOneAsync ? col.updateOneAsync(filter, update) : col.updateOne(filter, update),
|
|
295
|
+
updateMany: async (filter, update) => col.updateManyAsync ? col.updateManyAsync(filter, update) : col.updateMany(filter, update),
|
|
296
|
+
deleteOne: async (filter) => col.deleteOneAsync ? col.deleteOneAsync(filter) : col.deleteOne(filter),
|
|
297
|
+
deleteMany: async (filter) => col.deleteManyAsync ? col.deleteManyAsync(filter) : col.deleteMany(filter),
|
|
279
298
|
count: async (filter) => col.count(filter ?? null),
|
|
280
299
|
createIndex: async (field) => col.createIndex(field),
|
|
281
300
|
dropIndex: async (field) => col.dropIndex(field),
|
|
@@ -299,8 +318,8 @@ async function createNodeDB(dbName, config) {
|
|
|
299
318
|
return {
|
|
300
319
|
collection: (name, opts) => wrapCollection(name, opts),
|
|
301
320
|
compact: async () => db.compact(),
|
|
302
|
-
|
|
303
|
-
|
|
321
|
+
// Releases the native file handle/lock (no-op on older .node binaries).
|
|
322
|
+
close: async () => db.close?.()
|
|
304
323
|
};
|
|
305
324
|
}
|
|
306
325
|
async function createNativeDB(_dbName) {
|
|
@@ -336,7 +355,9 @@ async function createNativeDB(_dbName) {
|
|
|
336
355
|
},
|
|
337
356
|
dropVectorIndex: async (field) => native.dropVectorIndex(name, field),
|
|
338
357
|
upgradeVectorIndex: async (field) => native.upgradeVectorIndex(name, field),
|
|
339
|
-
|
|
358
|
+
// The JSI HostObject does not expose index introspection yet; return a
|
|
359
|
+
// correctly-shaped empty result rather than `{}` cast to the interface.
|
|
360
|
+
listIndexes: async () => ({ btree: [], fts: [], vector: [] }),
|
|
340
361
|
findNearest: async (field, vector, topK, filter) => {
|
|
341
362
|
const raw = native.findNearest(name, field, vector, topK, filter ?? null);
|
|
342
363
|
return raw;
|