taladb 0.5.0 → 0.6.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 +313 -0
- package/dist/index.d.mts +64 -6
- package/dist/index.d.ts +64 -6
- package/dist/index.js +85 -7
- package/dist/index.mjs +83 -7
- package/package.json +7 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// src/config.browser.ts
|
|
2
|
+
var ENDPOINT_FIELDS = [
|
|
3
|
+
"endpoint",
|
|
4
|
+
"insert_endpoint",
|
|
5
|
+
"update_endpoint",
|
|
6
|
+
"delete_endpoint"
|
|
7
|
+
];
|
|
8
|
+
function validateConfig(config) {
|
|
9
|
+
const sync = config.sync;
|
|
10
|
+
if (!sync) return;
|
|
11
|
+
for (const key of ENDPOINT_FIELDS) {
|
|
12
|
+
const url = sync[key];
|
|
13
|
+
if (url !== void 0 && !url.startsWith("http://") && !url.startsWith("https://")) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`TalaDB config: invalid endpoint URL "${url}" \u2014 must start with http:// or https://`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function loadConfig(_configPath) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/index.ts
|
|
25
|
+
function detectPlatform() {
|
|
26
|
+
if (globalThis.nativeCallSyncHook !== void 0) {
|
|
27
|
+
return "react-native";
|
|
28
|
+
}
|
|
29
|
+
if (globalThis.window !== void 0 && typeof navigator !== "undefined") {
|
|
30
|
+
return "browser";
|
|
31
|
+
}
|
|
32
|
+
return "node";
|
|
33
|
+
}
|
|
34
|
+
var WorkerProxy = class {
|
|
35
|
+
constructor(port) {
|
|
36
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
37
|
+
this.nextId = 1;
|
|
38
|
+
this.port = port;
|
|
39
|
+
this.port.onmessage = (e) => {
|
|
40
|
+
const { id, result, error } = e.data;
|
|
41
|
+
const p = this.pending.get(id);
|
|
42
|
+
if (p) {
|
|
43
|
+
this.pending.delete(id);
|
|
44
|
+
if (error === void 0) p.resolve(result);
|
|
45
|
+
else p.reject(new Error(error));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
this.port.start?.();
|
|
49
|
+
}
|
|
50
|
+
send(op, args = {}) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const id = this.nextId++;
|
|
53
|
+
this.pending.set(id, { resolve, reject });
|
|
54
|
+
this.port.postMessage({ id, op, ...args });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function makePoller(findFn, callback) {
|
|
59
|
+
let active = true;
|
|
60
|
+
let lastJson = "";
|
|
61
|
+
const poll = async () => {
|
|
62
|
+
if (!active) return;
|
|
63
|
+
try {
|
|
64
|
+
const docs = await findFn();
|
|
65
|
+
const json = JSON.stringify(docs);
|
|
66
|
+
if (json !== lastJson) {
|
|
67
|
+
lastJson = json;
|
|
68
|
+
callback(docs);
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
if (active) setTimeout(poll, 300);
|
|
73
|
+
};
|
|
74
|
+
poll();
|
|
75
|
+
return () => {
|
|
76
|
+
active = false;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function createBrowserDB(dbName, config) {
|
|
80
|
+
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import.meta.url);
|
|
81
|
+
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
82
|
+
const proxy = new WorkerProxy(worker);
|
|
83
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
84
|
+
await proxy.send("init", { dbName, configJson });
|
|
85
|
+
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
86
|
+
let channel = null;
|
|
87
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
88
|
+
channel = new BroadcastChannel(`taladb:${dbName}`);
|
|
89
|
+
channel.onmessage = (e) => {
|
|
90
|
+
if (e.data === "taladb:changed") {
|
|
91
|
+
for (const nudge of nudgeCallbacks) nudge();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function wrapCollection(name) {
|
|
96
|
+
const s = JSON.stringify;
|
|
97
|
+
return {
|
|
98
|
+
insert: (doc) => proxy.send("insert", { collection: name, docJson: s(doc) }),
|
|
99
|
+
insertMany: async (docs) => {
|
|
100
|
+
const json = await proxy.send("insertMany", {
|
|
101
|
+
collection: name,
|
|
102
|
+
docsJson: s(docs)
|
|
103
|
+
});
|
|
104
|
+
return JSON.parse(json);
|
|
105
|
+
},
|
|
106
|
+
find: async (filter) => {
|
|
107
|
+
const json = await proxy.send("find", {
|
|
108
|
+
collection: name,
|
|
109
|
+
filterJson: filter ? s(filter) : "null"
|
|
110
|
+
});
|
|
111
|
+
return JSON.parse(json);
|
|
112
|
+
},
|
|
113
|
+
findOne: async (filter) => {
|
|
114
|
+
const json = await proxy.send("findOne", {
|
|
115
|
+
collection: name,
|
|
116
|
+
filterJson: filter ? s(filter) : "null"
|
|
117
|
+
});
|
|
118
|
+
return JSON.parse(json);
|
|
119
|
+
},
|
|
120
|
+
updateOne: (filter, update) => proxy.send("updateOne", {
|
|
121
|
+
collection: name,
|
|
122
|
+
filterJson: s(filter),
|
|
123
|
+
updateJson: s(update)
|
|
124
|
+
}),
|
|
125
|
+
updateMany: (filter, update) => proxy.send("updateMany", {
|
|
126
|
+
collection: name,
|
|
127
|
+
filterJson: s(filter),
|
|
128
|
+
updateJson: s(update)
|
|
129
|
+
}),
|
|
130
|
+
deleteOne: (filter) => proxy.send("deleteOne", { collection: name, filterJson: s(filter) }),
|
|
131
|
+
deleteMany: (filter) => proxy.send("deleteMany", { collection: name, filterJson: s(filter) }),
|
|
132
|
+
count: (filter) => proxy.send("count", {
|
|
133
|
+
collection: name,
|
|
134
|
+
filterJson: filter ? s(filter) : "null"
|
|
135
|
+
}),
|
|
136
|
+
createIndex: (field) => proxy.send("createIndex", { collection: name, field }),
|
|
137
|
+
dropIndex: (field) => proxy.send("dropIndex", { collection: name, field }),
|
|
138
|
+
createFtsIndex: (field) => proxy.send("createFtsIndex", { collection: name, field }),
|
|
139
|
+
dropFtsIndex: (field) => proxy.send("dropFtsIndex", { collection: name, field }),
|
|
140
|
+
createVectorIndex: (field, options) => {
|
|
141
|
+
if (options.indexType === "hnsw") return Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native."));
|
|
142
|
+
return proxy.send("createVectorIndex", {
|
|
143
|
+
collection: name,
|
|
144
|
+
field,
|
|
145
|
+
dimensions: options.dimensions,
|
|
146
|
+
metric: options.metric,
|
|
147
|
+
indexType: null,
|
|
148
|
+
hnswM: null,
|
|
149
|
+
hnswEfConstruction: null
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
dropVectorIndex: (field) => proxy.send("dropVectorIndex", { collection: name, field }),
|
|
153
|
+
upgradeVectorIndex: (_field) => Promise.reject(new Error("HNSW vector indexes are not available in the browser (requires native threads). Use Node.js or React Native.")),
|
|
154
|
+
listIndexes: async () => {
|
|
155
|
+
const json = await proxy.send("listIndexes", { collection: name });
|
|
156
|
+
return JSON.parse(json);
|
|
157
|
+
},
|
|
158
|
+
findNearest: async (field, vector, topK, filter) => {
|
|
159
|
+
const json = await proxy.send("findNearest", {
|
|
160
|
+
collection: name,
|
|
161
|
+
field,
|
|
162
|
+
queryJson: JSON.stringify(vector),
|
|
163
|
+
topK,
|
|
164
|
+
filterJson: filter ? JSON.stringify(filter) : "null"
|
|
165
|
+
});
|
|
166
|
+
return JSON.parse(json);
|
|
167
|
+
},
|
|
168
|
+
subscribe: (filter, callback) => {
|
|
169
|
+
let active = true;
|
|
170
|
+
let lastJson = "[]";
|
|
171
|
+
let timer = null;
|
|
172
|
+
const poll = async () => {
|
|
173
|
+
if (!active) return;
|
|
174
|
+
if (timer !== null) {
|
|
175
|
+
clearTimeout(timer);
|
|
176
|
+
timer = null;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const json = await proxy.send("find", {
|
|
180
|
+
collection: name,
|
|
181
|
+
filterJson: filter ? s(filter) : "null"
|
|
182
|
+
});
|
|
183
|
+
if (json !== lastJson) {
|
|
184
|
+
lastJson = json;
|
|
185
|
+
callback(JSON.parse(json));
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
if (active) timer = setTimeout(poll, 300);
|
|
190
|
+
};
|
|
191
|
+
nudgeCallbacks.add(poll);
|
|
192
|
+
poll();
|
|
193
|
+
return () => {
|
|
194
|
+
active = false;
|
|
195
|
+
nudgeCallbacks.delete(poll);
|
|
196
|
+
if (timer !== null) {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
timer = null;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
collection: (name) => wrapCollection(name),
|
|
206
|
+
close: async () => {
|
|
207
|
+
channel?.close();
|
|
208
|
+
await proxy.send("close");
|
|
209
|
+
worker.terminate();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
async function createNodeDB(dbName, config) {
|
|
214
|
+
const { TalaDBNode } = await import("@taladb/node");
|
|
215
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : null;
|
|
216
|
+
const db = TalaDBNode.open(dbName, configJson);
|
|
217
|
+
function wrapCollection(name) {
|
|
218
|
+
const col = db.collection(name);
|
|
219
|
+
return {
|
|
220
|
+
insert: async (doc) => col.insert(doc),
|
|
221
|
+
insertMany: async (docs) => col.insertMany(docs),
|
|
222
|
+
find: async (filter) => col.find(filter ?? null),
|
|
223
|
+
findOne: async (filter) => col.findOne(filter) ?? null,
|
|
224
|
+
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
225
|
+
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
226
|
+
deleteOne: async (filter) => col.deleteOne(filter),
|
|
227
|
+
deleteMany: async (filter) => col.deleteMany(filter),
|
|
228
|
+
count: async (filter) => col.count(filter ?? null),
|
|
229
|
+
createIndex: async (field) => col.createIndex(field),
|
|
230
|
+
dropIndex: async (field) => col.dropIndex(field),
|
|
231
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
232
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
233
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
234
|
+
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
235
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
236
|
+
listIndexes: async () => {
|
|
237
|
+
const json = col.listIndexes();
|
|
238
|
+
return JSON.parse(json);
|
|
239
|
+
},
|
|
240
|
+
findNearest: async (field, vector, topK, filter) => {
|
|
241
|
+
const raw = await col.findNearest(field, vector, topK, filter ?? null);
|
|
242
|
+
return raw;
|
|
243
|
+
},
|
|
244
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? null), callback)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
collection: (name) => wrapCollection(name),
|
|
249
|
+
close: async () => {
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async function createNativeDB(_dbName) {
|
|
254
|
+
const maybeNative = globalThis.__TalaDB__;
|
|
255
|
+
if (!maybeNative) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
"@taladb/react-native JSI HostObject not found. Did you call TalaDBModule.initialize() in your app entry point?"
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const native = maybeNative;
|
|
261
|
+
function wrapCollection(name) {
|
|
262
|
+
const col = native.collection(name);
|
|
263
|
+
return {
|
|
264
|
+
insert: async (doc) => col.insert(doc),
|
|
265
|
+
insertMany: async (docs) => col.insertMany(docs),
|
|
266
|
+
find: async (filter) => col.find(filter ?? {}),
|
|
267
|
+
findOne: async (filter) => col.findOne(filter ?? {}),
|
|
268
|
+
updateOne: async (filter, update) => col.updateOne(filter, update),
|
|
269
|
+
updateMany: async (filter, update) => col.updateMany(filter, update),
|
|
270
|
+
deleteOne: async (filter) => col.deleteOne(filter),
|
|
271
|
+
deleteMany: async (filter) => col.deleteMany(filter),
|
|
272
|
+
count: async (filter) => col.count(filter ?? {}),
|
|
273
|
+
createIndex: async (field) => col.createIndex(field),
|
|
274
|
+
dropIndex: async (field) => col.dropIndex(field),
|
|
275
|
+
createFtsIndex: async (field) => col.createFtsIndex(field),
|
|
276
|
+
dropFtsIndex: async (field) => col.dropFtsIndex(field),
|
|
277
|
+
createVectorIndex: async (field, options) => col.createVectorIndex(field, options.dimensions, options.metric ?? null, options.indexType ?? null, options.hnswM ?? null, options.hnswEfConstruction ?? null),
|
|
278
|
+
dropVectorIndex: async (field) => col.dropVectorIndex(field),
|
|
279
|
+
upgradeVectorIndex: async (field) => col.upgradeVectorIndex(field),
|
|
280
|
+
listIndexes: async () => JSON.parse(col.listIndexes()),
|
|
281
|
+
findNearest: async (field, vector, topK, filter) => {
|
|
282
|
+
const raw = col.findNearest(field, vector, topK, filter ?? null);
|
|
283
|
+
return raw;
|
|
284
|
+
},
|
|
285
|
+
subscribe: (filter, callback) => makePoller(async () => col.find(filter ?? {}), callback)
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
collection: (name) => wrapCollection(name),
|
|
290
|
+
close: async () => native.close()
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async function openDB(dbName = "taladb.db", options) {
|
|
294
|
+
let resolvedConfig;
|
|
295
|
+
if (options?.config !== void 0) {
|
|
296
|
+
validateConfig(options.config);
|
|
297
|
+
resolvedConfig = options.config;
|
|
298
|
+
} else {
|
|
299
|
+
resolvedConfig = await loadConfig(options?.configPath);
|
|
300
|
+
}
|
|
301
|
+
const platform = detectPlatform();
|
|
302
|
+
switch (platform) {
|
|
303
|
+
case "browser":
|
|
304
|
+
return createBrowserDB(dbName, resolvedConfig);
|
|
305
|
+
case "react-native":
|
|
306
|
+
return createNativeDB(dbName);
|
|
307
|
+
case "node":
|
|
308
|
+
return createNodeDB(dbName, resolvedConfig);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
export {
|
|
312
|
+
openDB
|
|
313
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -170,17 +170,75 @@ interface TalaDB {
|
|
|
170
170
|
close(): Promise<void>;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/** HTTP push sync settings. */
|
|
174
|
+
interface SyncConfig {
|
|
175
|
+
/**
|
|
176
|
+
* Enable HTTP push sync. Defaults to `false`.
|
|
177
|
+
* Everything is a no-op when disabled, so a config block without
|
|
178
|
+
* `enabled: true` is safe to ship.
|
|
179
|
+
*/
|
|
180
|
+
enabled?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Default endpoint URL that receives all mutation events.
|
|
183
|
+
* Required when `enabled: true`.
|
|
184
|
+
*/
|
|
185
|
+
endpoint?: string;
|
|
186
|
+
/** HTTP headers sent with every outgoing request (e.g. `Authorization`). */
|
|
187
|
+
headers?: Record<string, string>;
|
|
188
|
+
/** Override the endpoint for `insert` events only. */
|
|
189
|
+
insert_endpoint?: string;
|
|
190
|
+
/** Override the endpoint for `update` events only. */
|
|
191
|
+
update_endpoint?: string;
|
|
192
|
+
/** Override the endpoint for `delete` events only. */
|
|
193
|
+
delete_endpoint?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Document fields to omit from every outgoing sync payload.
|
|
196
|
+
*
|
|
197
|
+
* Useful for stripping large computed fields such as embedding vectors
|
|
198
|
+
* that the remote endpoint doesn't need.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* exclude_fields: ['embedding', 'clip_vector']
|
|
202
|
+
*/
|
|
203
|
+
exclude_fields?: string[];
|
|
204
|
+
}
|
|
205
|
+
/** Top-level TalaDB configuration. */
|
|
206
|
+
interface TalaDbConfig {
|
|
207
|
+
/** HTTP push sync configuration. Disabled by default. */
|
|
208
|
+
sync?: SyncConfig;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Options for `openDB`. */
|
|
212
|
+
interface OpenDBOptions {
|
|
213
|
+
/**
|
|
214
|
+
* Explicit path to a `taladb.config.yml` / `taladb.config.json` file.
|
|
215
|
+
* If omitted, TalaDB auto-discovers the file from `process.cwd()` on Node.js.
|
|
216
|
+
* Ignored on browser and React Native — those platforms do not support
|
|
217
|
+
* file-based config discovery. Pass `config` inline instead, or on React Native
|
|
218
|
+
* pass `JSON.stringify(config)` as the second argument to `TalaDBModule.initialize`.
|
|
219
|
+
*/
|
|
220
|
+
configPath?: string;
|
|
221
|
+
/**
|
|
222
|
+
* Inline config object. Takes precedence over any config file when provided.
|
|
223
|
+
* Useful for passing config programmatically without a config file on disk.
|
|
224
|
+
*/
|
|
225
|
+
config?: TalaDbConfig;
|
|
226
|
+
}
|
|
173
227
|
/**
|
|
174
228
|
* Open a TalaDB database.
|
|
175
229
|
*
|
|
176
|
-
* @param dbName
|
|
177
|
-
*
|
|
230
|
+
* @param dbName Name of the database file (used for OPFS and native file paths).
|
|
231
|
+
* @param options Optional config. Pass `{ config }` for inline sync settings or
|
|
232
|
+
* `{ configPath }` to load from a specific file.
|
|
178
233
|
*
|
|
179
234
|
* @example
|
|
180
235
|
* const db = await openDB('myapp.db');
|
|
181
|
-
*
|
|
182
|
-
*
|
|
236
|
+
*
|
|
237
|
+
* @example with inline sync config
|
|
238
|
+
* const db = await openDB('myapp.db', {
|
|
239
|
+
* config: { sync: { enabled: true, endpoint: 'https://api.example.com/events' } },
|
|
240
|
+
* });
|
|
183
241
|
*/
|
|
184
|
-
declare function openDB(dbName?: string): Promise<TalaDB>;
|
|
242
|
+
declare function openDB(dbName?: string, options?: OpenDBOptions): Promise<TalaDB>;
|
|
185
243
|
|
|
186
|
-
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
244
|
+
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type OpenDBOptions, type SyncConfig, type TalaDB, type TalaDbConfig, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
package/dist/index.d.ts
CHANGED
|
@@ -170,17 +170,75 @@ interface TalaDB {
|
|
|
170
170
|
close(): Promise<void>;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/** HTTP push sync settings. */
|
|
174
|
+
interface SyncConfig {
|
|
175
|
+
/**
|
|
176
|
+
* Enable HTTP push sync. Defaults to `false`.
|
|
177
|
+
* Everything is a no-op when disabled, so a config block without
|
|
178
|
+
* `enabled: true` is safe to ship.
|
|
179
|
+
*/
|
|
180
|
+
enabled?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Default endpoint URL that receives all mutation events.
|
|
183
|
+
* Required when `enabled: true`.
|
|
184
|
+
*/
|
|
185
|
+
endpoint?: string;
|
|
186
|
+
/** HTTP headers sent with every outgoing request (e.g. `Authorization`). */
|
|
187
|
+
headers?: Record<string, string>;
|
|
188
|
+
/** Override the endpoint for `insert` events only. */
|
|
189
|
+
insert_endpoint?: string;
|
|
190
|
+
/** Override the endpoint for `update` events only. */
|
|
191
|
+
update_endpoint?: string;
|
|
192
|
+
/** Override the endpoint for `delete` events only. */
|
|
193
|
+
delete_endpoint?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Document fields to omit from every outgoing sync payload.
|
|
196
|
+
*
|
|
197
|
+
* Useful for stripping large computed fields such as embedding vectors
|
|
198
|
+
* that the remote endpoint doesn't need.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* exclude_fields: ['embedding', 'clip_vector']
|
|
202
|
+
*/
|
|
203
|
+
exclude_fields?: string[];
|
|
204
|
+
}
|
|
205
|
+
/** Top-level TalaDB configuration. */
|
|
206
|
+
interface TalaDbConfig {
|
|
207
|
+
/** HTTP push sync configuration. Disabled by default. */
|
|
208
|
+
sync?: SyncConfig;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Options for `openDB`. */
|
|
212
|
+
interface OpenDBOptions {
|
|
213
|
+
/**
|
|
214
|
+
* Explicit path to a `taladb.config.yml` / `taladb.config.json` file.
|
|
215
|
+
* If omitted, TalaDB auto-discovers the file from `process.cwd()` on Node.js.
|
|
216
|
+
* Ignored on browser and React Native — those platforms do not support
|
|
217
|
+
* file-based config discovery. Pass `config` inline instead, or on React Native
|
|
218
|
+
* pass `JSON.stringify(config)` as the second argument to `TalaDBModule.initialize`.
|
|
219
|
+
*/
|
|
220
|
+
configPath?: string;
|
|
221
|
+
/**
|
|
222
|
+
* Inline config object. Takes precedence over any config file when provided.
|
|
223
|
+
* Useful for passing config programmatically without a config file on disk.
|
|
224
|
+
*/
|
|
225
|
+
config?: TalaDbConfig;
|
|
226
|
+
}
|
|
173
227
|
/**
|
|
174
228
|
* Open a TalaDB database.
|
|
175
229
|
*
|
|
176
|
-
* @param dbName
|
|
177
|
-
*
|
|
230
|
+
* @param dbName Name of the database file (used for OPFS and native file paths).
|
|
231
|
+
* @param options Optional config. Pass `{ config }` for inline sync settings or
|
|
232
|
+
* `{ configPath }` to load from a specific file.
|
|
178
233
|
*
|
|
179
234
|
* @example
|
|
180
235
|
* const db = await openDB('myapp.db');
|
|
181
|
-
*
|
|
182
|
-
*
|
|
236
|
+
*
|
|
237
|
+
* @example with inline sync config
|
|
238
|
+
* const db = await openDB('myapp.db', {
|
|
239
|
+
* config: { sync: { enabled: true, endpoint: 'https://api.example.com/events' } },
|
|
240
|
+
* });
|
|
183
241
|
*/
|
|
184
|
-
declare function openDB(dbName?: string): Promise<TalaDB>;
|
|
242
|
+
declare function openDB(dbName?: string, options?: OpenDBOptions): Promise<TalaDB>;
|
|
185
243
|
|
|
186
|
-
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type TalaDB, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
244
|
+
export { type Collection, type CollectionIndexInfo, type Document, type Filter, type OpenDBOptions, type SyncConfig, type TalaDB, type TalaDbConfig, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,75 @@ __export(index_exports, {
|
|
|
33
33
|
openDB: () => openDB
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/config.ts
|
|
38
|
+
var ENDPOINT_FIELDS = [
|
|
39
|
+
"endpoint",
|
|
40
|
+
"insert_endpoint",
|
|
41
|
+
"update_endpoint",
|
|
42
|
+
"delete_endpoint"
|
|
43
|
+
];
|
|
44
|
+
function validateConfig(config) {
|
|
45
|
+
const sync = config.sync;
|
|
46
|
+
if (!sync) return;
|
|
47
|
+
for (const key of ENDPOINT_FIELDS) {
|
|
48
|
+
const url = sync[key];
|
|
49
|
+
if (url !== void 0 && !url.startsWith("http://") && !url.startsWith("https://")) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`TalaDB config: invalid endpoint URL "${url}" \u2014 must start with http:// or https://`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function loadConfig(configPath) {
|
|
57
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
const { join, extname } = await import(
|
|
61
|
+
/* @vite-ignore */
|
|
62
|
+
"path"
|
|
63
|
+
);
|
|
64
|
+
const { readFile, access } = await import(
|
|
65
|
+
/* @vite-ignore */
|
|
66
|
+
"fs/promises"
|
|
67
|
+
);
|
|
68
|
+
async function parseFile(filePath) {
|
|
69
|
+
const content = await readFile(filePath, "utf8");
|
|
70
|
+
const ext = extname(filePath).toLowerCase();
|
|
71
|
+
let raw;
|
|
72
|
+
if (ext === ".json") {
|
|
73
|
+
raw = JSON.parse(content);
|
|
74
|
+
} else if (ext === ".yml" || ext === ".yaml") {
|
|
75
|
+
const yaml = await import(
|
|
76
|
+
/* @vite-ignore */
|
|
77
|
+
"js-yaml"
|
|
78
|
+
);
|
|
79
|
+
raw = yaml.load(content);
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`TalaDB config: unsupported file extension "${ext}" \u2014 use .json, .yml, or .yaml`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const config = raw !== null && typeof raw === "object" ? raw : {};
|
|
86
|
+
validateConfig(config);
|
|
87
|
+
return config;
|
|
88
|
+
}
|
|
89
|
+
if (configPath) {
|
|
90
|
+
return parseFile(configPath);
|
|
91
|
+
}
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
for (const name of ["taladb.config.yml", "taladb.config.yaml", "taladb.config.json"]) {
|
|
94
|
+
const full = join(cwd, name);
|
|
95
|
+
try {
|
|
96
|
+
await access(full);
|
|
97
|
+
return parseFile(full);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/index.ts
|
|
36
105
|
var import_meta = {};
|
|
37
106
|
function detectPlatform() {
|
|
38
107
|
if (globalThis.nativeCallSyncHook !== void 0) {
|
|
@@ -88,11 +157,12 @@ function makePoller(findFn, callback) {
|
|
|
88
157
|
active = false;
|
|
89
158
|
};
|
|
90
159
|
}
|
|
91
|
-
async function createBrowserDB(dbName) {
|
|
160
|
+
async function createBrowserDB(dbName, config) {
|
|
92
161
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import_meta.url);
|
|
93
162
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
94
163
|
const proxy = new WorkerProxy(worker);
|
|
95
|
-
|
|
164
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
165
|
+
await proxy.send("init", { dbName, configJson });
|
|
96
166
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
97
167
|
let channel = null;
|
|
98
168
|
if (typeof BroadcastChannel !== "undefined") {
|
|
@@ -221,9 +291,10 @@ async function createBrowserDB(dbName) {
|
|
|
221
291
|
}
|
|
222
292
|
};
|
|
223
293
|
}
|
|
224
|
-
async function createNodeDB(dbName) {
|
|
294
|
+
async function createNodeDB(dbName, config) {
|
|
225
295
|
const { TalaDBNode } = await import("@taladb/node");
|
|
226
|
-
const
|
|
296
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : null;
|
|
297
|
+
const db = TalaDBNode.open(dbName, configJson);
|
|
227
298
|
function wrapCollection(name) {
|
|
228
299
|
const col = db.collection(name);
|
|
229
300
|
return {
|
|
@@ -300,15 +371,22 @@ async function createNativeDB(_dbName) {
|
|
|
300
371
|
close: async () => native.close()
|
|
301
372
|
};
|
|
302
373
|
}
|
|
303
|
-
async function openDB(dbName = "taladb.db") {
|
|
374
|
+
async function openDB(dbName = "taladb.db", options) {
|
|
375
|
+
let resolvedConfig;
|
|
376
|
+
if (options?.config !== void 0) {
|
|
377
|
+
validateConfig(options.config);
|
|
378
|
+
resolvedConfig = options.config;
|
|
379
|
+
} else {
|
|
380
|
+
resolvedConfig = await loadConfig(options?.configPath);
|
|
381
|
+
}
|
|
304
382
|
const platform = detectPlatform();
|
|
305
383
|
switch (platform) {
|
|
306
384
|
case "browser":
|
|
307
|
-
return createBrowserDB(dbName);
|
|
385
|
+
return createBrowserDB(dbName, resolvedConfig);
|
|
308
386
|
case "react-native":
|
|
309
387
|
return createNativeDB(dbName);
|
|
310
388
|
case "node":
|
|
311
|
-
return createNodeDB(dbName);
|
|
389
|
+
return createNodeDB(dbName, resolvedConfig);
|
|
312
390
|
}
|
|
313
391
|
}
|
|
314
392
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
var ENDPOINT_FIELDS = [
|
|
3
|
+
"endpoint",
|
|
4
|
+
"insert_endpoint",
|
|
5
|
+
"update_endpoint",
|
|
6
|
+
"delete_endpoint"
|
|
7
|
+
];
|
|
8
|
+
function validateConfig(config) {
|
|
9
|
+
const sync = config.sync;
|
|
10
|
+
if (!sync) return;
|
|
11
|
+
for (const key of ENDPOINT_FIELDS) {
|
|
12
|
+
const url = sync[key];
|
|
13
|
+
if (url !== void 0 && !url.startsWith("http://") && !url.startsWith("https://")) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`TalaDB config: invalid endpoint URL "${url}" \u2014 must start with http:// or https://`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function loadConfig(configPath) {
|
|
21
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
const { join, extname } = await import(
|
|
25
|
+
/* @vite-ignore */
|
|
26
|
+
"path"
|
|
27
|
+
);
|
|
28
|
+
const { readFile, access } = await import(
|
|
29
|
+
/* @vite-ignore */
|
|
30
|
+
"fs/promises"
|
|
31
|
+
);
|
|
32
|
+
async function parseFile(filePath) {
|
|
33
|
+
const content = await readFile(filePath, "utf8");
|
|
34
|
+
const ext = extname(filePath).toLowerCase();
|
|
35
|
+
let raw;
|
|
36
|
+
if (ext === ".json") {
|
|
37
|
+
raw = JSON.parse(content);
|
|
38
|
+
} else if (ext === ".yml" || ext === ".yaml") {
|
|
39
|
+
const yaml = await import(
|
|
40
|
+
/* @vite-ignore */
|
|
41
|
+
"js-yaml"
|
|
42
|
+
);
|
|
43
|
+
raw = yaml.load(content);
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`TalaDB config: unsupported file extension "${ext}" \u2014 use .json, .yml, or .yaml`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const config = raw !== null && typeof raw === "object" ? raw : {};
|
|
50
|
+
validateConfig(config);
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
if (configPath) {
|
|
54
|
+
return parseFile(configPath);
|
|
55
|
+
}
|
|
56
|
+
const cwd = process.cwd();
|
|
57
|
+
for (const name of ["taladb.config.yml", "taladb.config.yaml", "taladb.config.json"]) {
|
|
58
|
+
const full = join(cwd, name);
|
|
59
|
+
try {
|
|
60
|
+
await access(full);
|
|
61
|
+
return parseFile(full);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
|
|
1
68
|
// src/index.ts
|
|
2
69
|
function detectPlatform() {
|
|
3
70
|
if (globalThis.nativeCallSyncHook !== void 0) {
|
|
@@ -53,11 +120,12 @@ function makePoller(findFn, callback) {
|
|
|
53
120
|
active = false;
|
|
54
121
|
};
|
|
55
122
|
}
|
|
56
|
-
async function createBrowserDB(dbName) {
|
|
123
|
+
async function createBrowserDB(dbName, config) {
|
|
57
124
|
const workerUrl = new URL("@taladb/web/worker/taladb.worker.js", import.meta.url);
|
|
58
125
|
const worker = new Worker(workerUrl, { type: "module", name: "taladb" });
|
|
59
126
|
const proxy = new WorkerProxy(worker);
|
|
60
|
-
|
|
127
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : void 0;
|
|
128
|
+
await proxy.send("init", { dbName, configJson });
|
|
61
129
|
const nudgeCallbacks = /* @__PURE__ */ new Set();
|
|
62
130
|
let channel = null;
|
|
63
131
|
if (typeof BroadcastChannel !== "undefined") {
|
|
@@ -186,9 +254,10 @@ async function createBrowserDB(dbName) {
|
|
|
186
254
|
}
|
|
187
255
|
};
|
|
188
256
|
}
|
|
189
|
-
async function createNodeDB(dbName) {
|
|
257
|
+
async function createNodeDB(dbName, config) {
|
|
190
258
|
const { TalaDBNode } = await import("@taladb/node");
|
|
191
|
-
const
|
|
259
|
+
const configJson = config !== void 0 ? JSON.stringify(config) : null;
|
|
260
|
+
const db = TalaDBNode.open(dbName, configJson);
|
|
192
261
|
function wrapCollection(name) {
|
|
193
262
|
const col = db.collection(name);
|
|
194
263
|
return {
|
|
@@ -265,15 +334,22 @@ async function createNativeDB(_dbName) {
|
|
|
265
334
|
close: async () => native.close()
|
|
266
335
|
};
|
|
267
336
|
}
|
|
268
|
-
async function openDB(dbName = "taladb.db") {
|
|
337
|
+
async function openDB(dbName = "taladb.db", options) {
|
|
338
|
+
let resolvedConfig;
|
|
339
|
+
if (options?.config !== void 0) {
|
|
340
|
+
validateConfig(options.config);
|
|
341
|
+
resolvedConfig = options.config;
|
|
342
|
+
} else {
|
|
343
|
+
resolvedConfig = await loadConfig(options?.configPath);
|
|
344
|
+
}
|
|
269
345
|
const platform = detectPlatform();
|
|
270
346
|
switch (platform) {
|
|
271
347
|
case "browser":
|
|
272
|
-
return createBrowserDB(dbName);
|
|
348
|
+
return createBrowserDB(dbName, resolvedConfig);
|
|
273
349
|
case "react-native":
|
|
274
350
|
return createNativeDB(dbName);
|
|
275
351
|
case "node":
|
|
276
|
-
return createNodeDB(dbName);
|
|
352
|
+
return createNodeDB(dbName, resolvedConfig);
|
|
277
353
|
}
|
|
278
354
|
}
|
|
279
355
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taladb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Local-first document and vector database for React, React Native, and Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
+
"browser": "./dist/index.browser.mjs",
|
|
11
12
|
"types": "./dist/index.d.ts",
|
|
12
13
|
"import": "./dist/index.mjs",
|
|
13
14
|
"require": "./dist/index.js"
|
|
@@ -49,7 +50,12 @@
|
|
|
49
50
|
"engines": {
|
|
50
51
|
"node": ">=18"
|
|
51
52
|
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"js-yaml": "^4.1.0"
|
|
55
|
+
},
|
|
52
56
|
"devDependencies": {
|
|
57
|
+
"@types/js-yaml": "^4.0.9",
|
|
58
|
+
"@types/node": "^22.0.0",
|
|
53
59
|
"tsup": "^8.0.0",
|
|
54
60
|
"typescript": "^5.9.3",
|
|
55
61
|
"vitest": "^3.2.0"
|