taladb 0.5.0 → 0.6.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.
@@ -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 Name of the database file (used for OPFS and native file paths).
177
- * Ignored for in-memory databases.
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
- * const users = db.collection<User>('users');
182
- * const id = await users.insert({ name: 'Alice', age: 30 });
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 Name of the database file (used for OPFS and native file paths).
177
- * Ignored for in-memory databases.
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
- * const users = db.collection<User>('users');
182
- * const id = await users.insert({ name: 'Alice', age: 30 });
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
- await proxy.send("init", { dbName });
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 db = TalaDBNode.open(dbName);
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
- await proxy.send("init", { dbName });
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 db = TalaDBNode.open(dbName);
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.5.0",
3
+ "version": "0.6.1",
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"