@signaldb/core 2.0.0-beta.4 → 2.0.0-beta.5

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.
Files changed (60) hide show
  1. package/dist/.vite/manifest.json +27 -21
  2. package/dist/Collection/Observer.d.ts +2 -0
  3. package/dist/Collection/index.d.ts +11 -10
  4. package/dist/Collection/types.d.ts +7 -0
  5. package/dist/DefaultDataAdapter.d.ts +2 -0
  6. package/dist/index.cjs.js +8 -5
  7. package/dist/index.cjs12.js +41 -346
  8. package/dist/index.cjs13.js +308 -359
  9. package/dist/index.cjs14.js +387 -177
  10. package/dist/index.cjs15.js +182 -364
  11. package/dist/index.cjs16.js +287 -478
  12. package/dist/index.cjs17.js +563 -136
  13. package/dist/index.cjs18.js +154 -23
  14. package/dist/index.cjs19.js +23 -39
  15. package/dist/index.cjs2.js +1 -1
  16. package/dist/index.cjs20.js +39 -13
  17. package/dist/index.cjs21.js +14 -102
  18. package/dist/index.cjs22.js +93 -118
  19. package/dist/index.cjs23.js +127 -5
  20. package/dist/index.cjs24.js +5 -25
  21. package/dist/index.cjs25.js +24 -7
  22. package/dist/index.cjs26.js +8 -27
  23. package/dist/index.cjs27.js +25 -42
  24. package/dist/index.cjs28.js +44 -6
  25. package/dist/index.cjs29.js +6 -7
  26. package/dist/index.cjs3.js +9 -26
  27. package/dist/index.cjs30.js +5 -3
  28. package/dist/index.cjs31.js +40 -5
  29. package/dist/index.cjs33.js +3 -40
  30. package/dist/index.cjs34.js +9 -0
  31. package/dist/index.cjs7.js +1 -1
  32. package/dist/index.d.ts +2 -1
  33. package/dist/index.mjs +10 -7
  34. package/dist/index12.mjs +40 -346
  35. package/dist/index13.mjs +308 -359
  36. package/dist/index14.mjs +387 -177
  37. package/dist/index15.mjs +182 -364
  38. package/dist/index16.mjs +287 -478
  39. package/dist/index17.mjs +563 -136
  40. package/dist/index18.mjs +154 -23
  41. package/dist/index19.mjs +23 -38
  42. package/dist/index2.mjs +1 -1
  43. package/dist/index20.mjs +38 -13
  44. package/dist/index21.mjs +14 -102
  45. package/dist/index22.mjs +93 -117
  46. package/dist/index23.mjs +126 -5
  47. package/dist/index24.mjs +5 -25
  48. package/dist/index25.mjs +24 -7
  49. package/dist/index26.mjs +8 -27
  50. package/dist/index27.mjs +25 -42
  51. package/dist/index28.mjs +44 -6
  52. package/dist/index29.mjs +6 -7
  53. package/dist/index3.mjs +9 -26
  54. package/dist/index30.mjs +5 -3
  55. package/dist/index31.mjs +40 -5
  56. package/dist/index33.mjs +3 -40
  57. package/dist/index34.mjs +10 -0
  58. package/dist/index7.mjs +1 -1
  59. package/dist/utils/reactiveOrAsync.d.ts +59 -0
  60. package/package.json +1 -1
@@ -1,386 +1,124 @@
1
1
  "use strict";
2
- const deepClone = require("./index.cjs19.js");
3
- const match = require("./index.cjs23.js");
2
+ const deepClone = require("./index.cjs20.js");
3
+ const match = require("./index.cjs24.js");
4
4
  const modify = require("./index.cjs7.js");
5
- const queryId = require("./index.cjs20.js");
5
+ const queryId = require("./index.cjs21.js");
6
6
  const isEqual = require("./index.cjs6.js");
7
- const getIndexInfo = require("./index.cjs22.js");
8
- const getMatchingKeys = require("./index.cjs26.js");
9
- const sortItems = require("./index.cjs25.js");
10
- const project = require("./index.cjs24.js");
11
- function defaultMergeItems(a, b) {
12
- return { ...a, ...b };
13
- }
14
- function selectorId(selector) {
15
- return JSON.stringify(selector ?? {});
16
- }
17
- class AutoFetchDataAdapter {
7
+ const getIndexInfo = require("./index.cjs23.js");
8
+ const getMatchingKeys = require("./index.cjs27.js");
9
+ const sortItems = require("./index.cjs26.js");
10
+ const project = require("./index.cjs25.js");
11
+ const compact = require("./index.cjs29.js");
12
+ class WorkerDataAdapterHost {
13
+ workerContext;
18
14
  options;
19
15
  id;
20
- onError;
21
- fetchQueryItems;
22
- mergeItems;
23
- purgeDelay;
24
- // Per-collection resources
16
+ log = () => {
17
+ };
25
18
  storageAdapters = /* @__PURE__ */ new Map();
26
19
  storageAdapterReady = /* @__PURE__ */ new Map();
27
20
  collectionIndices = /* @__PURE__ */ new Map();
28
- // Per-collection query registries and auto-fetch bookkeeping
29
21
  queries = /* @__PURE__ */ new Map();
30
- activeObservers = /* @__PURE__ */ new Map();
31
- // per-collection: key->record
32
- observerTimeouts = /* @__PURE__ */ new Map();
33
- // per-collection
34
- selectorIds = /* @__PURE__ */ new Map();
35
- // per-collection: selectorKey -> Set<id>
36
- idRefCounts = /* @__PURE__ */ new Map();
37
- // per-collection: id -> refcount (for auto-fetched items)
38
- autoloadIds = /* @__PURE__ */ new Map();
39
- // per-collection: ids that were introduced via auto-fetch
40
- constructor(options) {
22
+ onError = (error) => {
23
+ console.error(error);
24
+ };
25
+ constructor(workerContext, options) {
26
+ this.workerContext = workerContext;
41
27
  this.options = options;
42
- this.id = options.id || "autofetch-data-adapter";
43
- this.onError = options.onError ?? ((error) => {
44
- console.error(error);
45
- });
46
- this.fetchQueryItems = options.fetchQueryItems;
47
- this.mergeItems = options.mergeItems ?? defaultMergeItems;
48
- this.purgeDelay = options.purgeDelay ?? 1e4;
49
- if (options.registerRemoteChange) {
50
- void options.registerRemoteChange(async () => {
51
- await this.forceRefetchAll();
52
- });
53
- }
54
- }
55
- createCollectionBackend(collection, indices) {
56
- this.collectionIndices.set(collection.name, indices);
57
- this.queries.set(collection.name, /* @__PURE__ */ new Map());
58
- this.ensureStorageAdapter(collection.name);
59
- this.activeObservers.set(collection.name, /* @__PURE__ */ new Map());
60
- this.observerTimeouts.set(collection.name, /* @__PURE__ */ new Map());
61
- this.selectorIds.set(collection.name, /* @__PURE__ */ new Map());
62
- this.idRefCounts.set(collection.name, /* @__PURE__ */ new Map());
63
- this.autoloadIds.set(collection.name, /* @__PURE__ */ new Set());
64
- const ready = this.setupStorage(collection.name, indices);
65
- this.storageAdapterReady.set(collection.name, ready);
66
- const registerQuery = (selector, options) => {
67
- const qid = queryId(selector, options);
68
- const registry = this.queries.get(collection.name);
69
- if (!registry)
70
- throw new Error(`Collection ${collection.name} not initialized!`);
71
- registry.set(qid, {
72
- selector,
73
- options,
74
- items: [],
75
- listeners: /* @__PURE__ */ new Set(),
76
- ...registry.get(qid),
77
- state: "active",
78
- error: null
79
- });
80
- const key = selectorId(selector);
81
- const perColObservers = this.activeObservers.get(collection.name);
82
- const current = perColObservers?.get(key)?.count ?? 0;
83
- perColObservers?.set(key, { selector, count: current + 1 });
84
- const timeouts = this.observerTimeouts.get(collection.name);
85
- const t = timeouts?.get(key);
86
- if (t)
87
- clearTimeout(t);
88
- if (current === 0) {
89
- void this.fetchAndIngest(collection.name, selector).catch(this.onError);
90
- }
91
- void this.fulfillQuery(collection.name, selector, options).catch(this.onError);
92
- };
93
- const unregisterQuery = (selector, options) => {
94
- const qid = queryId(selector, options);
95
- const registry = this.queries.get(collection.name);
96
- registry?.delete(qid);
97
- const key = selectorId(selector);
98
- const perColObservers = this.activeObservers.get(collection.name);
99
- const current = perColObservers?.get(key)?.count ?? 0;
100
- const remaining = Math.max(0, current - 1);
101
- if (remaining > 0) {
102
- perColObservers?.set(key, { selector, count: remaining });
103
- return;
104
- }
105
- const doPurge = () => {
106
- perColObservers?.delete(key);
107
- void this.purgeSelector(collection.name, selector).catch(this.onError);
108
- };
109
- if (this.purgeDelay === 0) {
110
- doPurge();
111
- } else {
112
- const timeouts = this.observerTimeouts.get(collection.name);
113
- const t = timeouts?.get(key);
114
- if (t)
115
- clearTimeout(t);
116
- timeouts?.set(key, setTimeout(doPurge, this.purgeDelay));
117
- }
118
- };
119
- const getQueryState = (selector, options) => {
120
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
121
- return q?.state ?? "active";
122
- };
123
- const getQueryError = (selector, options) => {
124
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
125
- return q?.error ?? null;
126
- };
127
- const getQueryResult = (selector, options) => {
128
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
129
- return q?.items ?? [];
130
- };
131
- const onQueryStateChange = (selector, options, callback) => {
132
- const qid = queryId(selector, options);
133
- const registry = this.queries.get(collection.name);
134
- if (!registry)
135
- throw new Error(`Collection ${collection.name} not initialized!`);
136
- if (!registry.has(qid)) {
137
- registry.set(qid, {
138
- selector,
139
- options,
140
- state: "active",
141
- error: null,
142
- items: [],
143
- listeners: /* @__PURE__ */ new Set()
144
- });
145
- }
146
- registry.get(qid)?.listeners.add(callback);
147
- return () => {
148
- registry.get(qid)?.listeners.delete(callback);
149
- };
150
- };
151
- return {
152
- insert: async (item) => {
153
- await ready;
154
- const inserted = await this.insert(collection.name, item);
155
- return inserted;
156
- },
157
- updateOne: async (selector, modifier) => {
158
- await ready;
159
- return this.updateOne(collection.name, selector, modifier);
160
- },
161
- updateMany: async (selector, modifier) => {
162
- await ready;
163
- return this.updateMany(collection.name, selector, modifier);
164
- },
165
- replaceOne: async (selector, replacement) => {
166
- await ready;
167
- return this.replaceOne(collection.name, selector, replacement);
168
- },
169
- removeOne: async (selector) => {
170
- await ready;
171
- return this.removeOne(collection.name, selector);
172
- },
173
- removeMany: async (selector) => {
174
- await ready;
175
- return this.removeMany(collection.name, selector);
176
- },
177
- registerQuery,
178
- unregisterQuery,
179
- getQueryState,
180
- getQueryError,
181
- getQueryResult,
182
- onQueryStateChange,
183
- executeQuery: async (selector, options) => {
184
- await ready;
185
- registerQuery(selector, options);
186
- await new Promise((resolve) => {
187
- let stop = () => {
188
- };
189
- stop = onQueryStateChange(selector, options, (state) => {
190
- if (state === "active")
191
- return;
192
- resolve();
193
- stop();
194
- });
195
- });
196
- const result = getQueryResult(selector, options);
197
- unregisterQuery(selector, options);
198
- return result;
199
- },
200
- dispose: async () => {
201
- this.storageAdapters.delete(collection.name);
202
- this.queries.delete(collection.name);
203
- this.collectionIndices.delete(collection.name);
204
- this.storageAdapterReady.delete(collection.name);
205
- this.activeObservers.delete(collection.name);
206
- this.observerTimeouts.delete(collection.name);
207
- this.selectorIds.delete(collection.name);
208
- this.idRefCounts.delete(collection.name);
209
- this.autoloadIds.delete(collection.name);
210
- },
211
- isReady: async () => {
212
- await ready;
213
- }
214
- };
215
- }
216
- // ===== Auto-fetch mechanics =====
217
- async forceRefetchAll() {
218
- const tasks = [];
219
- for (const [collectionName, observers] of this.activeObservers.entries()) {
220
- for (const { selector, count } of observers.values()) {
221
- if (count > 0)
222
- tasks.push(this.fetchAndIngest(collectionName, selector));
223
- }
28
+ this.id = this.options.id || "default-worker-data-adapter";
29
+ if (this.options.onError) {
30
+ this.onError = this.options.onError;
224
31
  }
225
- await Promise.all(tasks);
226
- }
227
- async fetchAndIngest(collectionName, selector) {
228
- this.publishForSelector(collectionName, selector, "active", null);
229
- try {
230
- const items = await this.fetchQueryItems(collectionName, selector);
231
- if (!items || !Array.isArray(items)) {
232
- throw new Error("AutoFetchDataAdapter: fetchQueryItems must resolve to { items: T[] }");
233
- }
234
- const ids = items.map((i) => i.id);
235
- const selectorKey = selectorId(selector);
236
- const selMap = this.selectorIds.get(collectionName);
237
- const previous = selMap?.get(selectorKey) ?? /* @__PURE__ */ new Set();
238
- ids.forEach((id) => previous.add(id));
239
- selMap?.set(selectorKey, previous);
240
- await this.upsertMerged(collectionName, items);
241
- await this.checkQueryUpdates(collectionName, items);
242
- } catch (error) {
243
- this.publishForSelector(collectionName, selector, "error", error);
244
- this.onError(error);
32
+ if (this.options.log)
33
+ this.log = this.options.log;
34
+ if (typeof addEventListener === "undefined" || typeof postMessage === "undefined") {
35
+ throw new TypeError("WorkerDataAdapterHost can only be used in a Web Worker context");
245
36
  }
246
- }
247
- async purgeSelector(collectionName, selector) {
248
- const selectorKey = selectorId(selector);
249
- const selMap = this.selectorIds.get(collectionName);
250
- const ids = selMap?.get(selectorKey);
251
- selMap?.delete(selectorKey);
252
- if (!ids || ids.size === 0)
253
- return;
254
- const referenceMap = this.idRefCounts.get(collectionName);
255
- const autoload = this.autoloadIds.get(collectionName);
256
- const toRemove = [];
257
- for (const id of ids) {
258
- const current = referenceMap?.get(id) ?? 0;
259
- const next = Math.max(0, current - 1);
260
- if (next === 0) {
261
- referenceMap?.delete(id);
262
- if (autoload?.has(id))
263
- toRemove.push(id);
264
- } else {
265
- referenceMap?.set(id, next);
37
+ this.workerContext.addEventListener("message", async (event) => {
38
+ try {
39
+ const { workerId, id, method, args } = event.data;
40
+ await this.handleMessage(workerId, id, method, args);
41
+ } catch (error) {
42
+ this.onError(error);
266
43
  }
267
- }
268
- if (toRemove.length === 0)
269
- return;
270
- const storage = this.storageAdapters.get(collectionName);
271
- if (!storage)
272
- throw new Error(`No persistence adapter for collection ${collectionName}`);
273
- const items = await storage.readIds(toRemove);
274
- if (items.length > 0) {
275
- await storage.remove(items);
276
- await this.checkQueryUpdates(collectionName, items);
277
- }
278
- toRemove.forEach((id) => autoload?.delete(id));
279
- }
280
- // ===== Core query + CRUD plumbing (index-aware, same semantics as AsyncDataAdapter) =====
281
- async setupStorage(collectionName, indices) {
282
- const storage = this.storageAdapters.get(collectionName);
283
- if (!storage)
284
- throw new Error(`No persistence adapter for collection ${collectionName}`);
285
- await Promise.all(indices.map((field) => storage.createIndex(field)));
286
- await storage.setup();
44
+ });
45
+ this.respond("ready", null, null, "ready");
287
46
  }
288
- ensureStorageAdapter(name) {
289
- if (this.storageAdapters.has(name))
290
- return;
291
- const adapter = this.options.storage && this.options.storage(name);
292
- if (!adapter)
293
- return;
294
- this.storageAdapters.set(name, adapter);
47
+ respond(id, data, error = null, type = "response") {
48
+ this.workerContext.postMessage({ id, workerId: this.id, type, data, error });
295
49
  }
296
- publishForSelector(collectionName, selector, state, error) {
297
- const registry = this.queries.get(collectionName);
298
- if (!registry)
50
+ async handleMessage(workerId, id, method, args) {
51
+ if (workerId !== this.id)
299
52
  return;
300
- for (const query of registry.values()) {
301
- if (!isEqual(query.selector, selector))
302
- continue;
303
- this.publishState(collectionName, queryId(query.selector, query.options), state, error);
304
- }
305
- }
306
- publishState(collectionName, qid, state, error) {
307
- const query = this.queries.get(collectionName)?.get(qid);
308
- if (!query)
53
+ const fn = this[method];
54
+ if (typeof fn !== "function") {
55
+ this.respond(id, null, new Error(`Method ${method} not found`));
309
56
  return;
310
- query.state = state;
311
- query.error = error;
312
- for (const callback of query.listeners) {
313
- try {
314
- callback(state);
315
- } catch (error_) {
316
- this.onError(error_);
317
- }
318
57
  }
319
- }
320
- publishResult(collectionName, qid, items) {
321
- const query = this.queries.get(collectionName)?.get(qid);
322
- if (!query)
323
- return;
324
- query.items = items;
325
- this.queries.get(collectionName)?.set(qid, query);
326
- }
327
- async fulfillQuery(collectionName, selector, options) {
328
- const qid = queryId(selector, options);
329
- const registry = this.queries.get(collectionName);
330
- if (!registry)
331
- throw new Error(`Collection ${collectionName} not initialized!`);
332
- const rec = registry.get(qid);
333
- if (!rec)
334
- return;
335
- this.publishState(collectionName, qid, "active", null);
58
+ this.log(method, ...args);
59
+ await this.isReady(args[0]);
336
60
  try {
337
- const items = await this.executeQuery(collectionName, selector, options);
338
- this.publishResult(collectionName, qid, items);
339
- this.publishState(collectionName, qid, "complete", null);
61
+ const result = await fn.apply(this, args);
62
+ this.respond(id, result);
340
63
  } catch (error) {
341
- this.publishState(collectionName, qid, "error", error);
64
+ this.respond(id, null, error);
342
65
  }
343
66
  }
344
67
  async getIndexInfo(collectionName, selector) {
345
- const storage = this.storageAdapters.get(collectionName);
346
- if (!storage)
68
+ const storageAdapter = this.storageAdapters.get(collectionName);
69
+ if (!storageAdapter)
347
70
  throw new Error(`No persistence adapter for collection ${collectionName}`);
348
71
  if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
349
- return { matched: true, ids: [selector.id], optimizedSelector: {} };
72
+ return {
73
+ matched: true,
74
+ ids: compact([selector.id]),
75
+ optimizedSelector: {}
76
+ };
350
77
  }
351
78
  if (selector == null) {
352
- return { matched: false, ids: [], optimizedSelector: {} };
79
+ return {
80
+ matched: false,
81
+ ids: [],
82
+ optimizedSelector: {}
83
+ };
353
84
  }
354
85
  const indices = this.collectionIndices.get(collectionName) ?? [];
355
86
  return getIndexInfo.default(indices.map((field) => async (flatSelector) => {
356
- if (!Object.hasOwnProperty.call(flatSelector, field))
87
+ if (!Object.hasOwnProperty.call(flatSelector, field)) {
357
88
  return { matched: false };
358
- const index = await storage.readIndex(field);
89
+ }
90
+ const index = await storageAdapter.readIndex(field);
359
91
  const fieldSelector = flatSelector[field];
360
- const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
361
- const keys = filtersForNull ? { include: null, exclude: [...index.keys()].filter((key) => key != null) } : getMatchingKeys(field, flatSelector);
92
+ const filteresForNull = fieldSelector == null || fieldSelector.$exists === false;
93
+ const keys = filteresForNull ? { include: null, exclude: [...index.keys()].filter((key) => key != null) } : getMatchingKeys(field, flatSelector);
362
94
  if (keys.include == null && keys.exclude == null)
363
95
  return { matched: false };
364
96
  let includedIds = [];
365
97
  if (keys.include == null) {
366
- for (const set of index.values())
367
- for (const pos of set)
98
+ for (const set of index.values()) {
99
+ for (const pos of set) {
368
100
  includedIds.push(pos);
101
+ }
102
+ }
369
103
  } else {
370
104
  for (const key of keys.include) {
371
105
  const idSet = index.get(key);
372
- if (idSet)
373
- for (const id of idSet)
106
+ if (idSet) {
107
+ for (const id of idSet) {
374
108
  includedIds.push(id);
109
+ }
110
+ }
375
111
  }
376
112
  }
377
113
  if (keys.exclude != null) {
378
114
  const excludeIds = /* @__PURE__ */ new Set();
379
115
  for (const key of keys.exclude) {
380
116
  const idSet = index.get(key);
381
- if (idSet)
382
- for (const id of idSet)
117
+ if (idSet) {
118
+ for (const id of idSet) {
383
119
  excludeIds.add(id);
120
+ }
121
+ }
384
122
  }
385
123
  includedIds = includedIds.filter((pos) => !excludeIds.has(pos));
386
124
  }
@@ -388,35 +126,38 @@ class AutoFetchDataAdapter {
388
126
  matched: true,
389
127
  ids: includedIds,
390
128
  fields: [field],
391
- keepSelector: filtersForNull
129
+ keepSelector: filteresForNull
392
130
  };
393
131
  }), selector);
394
132
  }
395
133
  async queryItems(collectionName, selector) {
396
- const storage = this.storageAdapters.get(collectionName);
397
- if (!storage)
134
+ const storageAdapter = this.storageAdapters.get(collectionName);
135
+ if (!storageAdapter)
398
136
  throw new Error(`No persistence adapter for collection ${collectionName}`);
399
- const index = await this.getIndexInfo(collectionName, selector);
137
+ const indexInfo = await this.getIndexInfo(collectionName, selector);
400
138
  const matchItems = (item) => {
401
- if (index.optimizedSelector == null)
139
+ if (indexInfo.optimizedSelector == null)
402
140
  return true;
403
- if (Object.keys(index.optimizedSelector).length <= 0)
141
+ if (Object.keys(indexInfo.optimizedSelector).length <= 0)
404
142
  return true;
405
- return match(item, index.optimizedSelector);
143
+ const matches = match(item, indexInfo.optimizedSelector);
144
+ return matches;
406
145
  };
407
- if (index.matched) {
408
- const items = await storage.readIds(index.ids);
409
- if (isEqual(index.optimizedSelector, {}))
146
+ if (indexInfo.matched) {
147
+ const items = await storageAdapter.readIds(indexInfo.ids);
148
+ if (isEqual(indexInfo.optimizedSelector, {}))
410
149
  return items;
411
150
  return items.filter(matchItems);
412
151
  } else {
413
- const allItems = await storage.readAll();
152
+ const allItems = await storageAdapter.readAll();
414
153
  if (isEqual(selector, {}))
415
154
  return allItems;
416
155
  return allItems.filter(matchItems);
417
156
  }
418
157
  }
419
158
  async executeQuery(collectionName, selector, options) {
159
+ if (selector === null)
160
+ return [];
420
161
  const items = await this.queryItems(collectionName, selector || {});
421
162
  const { sort, skip, limit, fields } = options || {};
422
163
  const sorted = sort ? sortItems(items, sort) : items;
@@ -426,154 +167,222 @@ class AutoFetchDataAdapter {
426
167
  return limited.map((item) => {
427
168
  if (!fields)
428
169
  return item;
429
- return { ...idExcluded ? {} : { id: item.id }, ...project(item, fields) };
170
+ return {
171
+ ...idExcluded ? {} : { id: item.id },
172
+ ...project(item, fields)
173
+ };
430
174
  });
431
175
  }
432
- async checkQueryUpdates(collectionName, changedItems) {
433
- const registry = this.queries.get(collectionName);
434
- if (!registry)
176
+ ensureQuery(collectionName, selector, options) {
177
+ const id = queryId(selector, options);
178
+ if (!this.queries.get(collectionName)) {
435
179
  throw new Error(`Collection ${collectionName} not initialized!`);
436
- if (registry.size === 0)
180
+ }
181
+ let query = this.queries.get(collectionName)?.get(id);
182
+ if (!query) {
183
+ query = { selector, options };
184
+ this.queries.get(collectionName)?.set(id, query);
185
+ }
186
+ return query;
187
+ }
188
+ emitQueryUpdate(collectionName, selector, options, state, error, items) {
189
+ const id = queryId(selector, options);
190
+ const collectionQueries = this.queries.get(collectionName);
191
+ if (!collectionQueries)
192
+ throw new Error(`Collection ${collectionName} not initialized!`);
193
+ this.respond(id, { collectionName, selector, options, state, error, items }, null, "queryUpdate");
194
+ }
195
+ ensureStorageAdapter(name) {
196
+ if (this.storageAdapters.has(name))
437
197
  return;
438
- const affected = [...registry.values()].filter(({ selector }) => changedItems.some((item) => match(item, selector)));
439
- if (affected.length === 0)
198
+ const adapter = this.options.storage(name);
199
+ if (!adapter)
440
200
  return;
441
- for (const { selector, options } of affected) {
442
- const qid = queryId(selector, options);
443
- this.publishState(collectionName, qid, "active", null);
444
- }
445
- await Promise.all(affected.map(async ({ selector, options }) => {
446
- const qid = queryId(selector, options);
447
- try {
448
- const items = await this.executeQuery(collectionName, selector, options);
449
- this.publishResult(collectionName, qid, items);
450
- this.publishState(collectionName, qid, "complete", null);
451
- } catch (error) {
452
- this.publishState(collectionName, qid, "error", error);
453
- }
201
+ this.storageAdapters.set(name, adapter);
202
+ }
203
+ async checkQueryUpdates(collectionName, items) {
204
+ const queries = this.queries.get(collectionName);
205
+ if (!queries)
206
+ throw new Error(`Collection ${collectionName} not initialized!`);
207
+ const affectedQueries = [...queries.values()].filter(({ selector }) => items.some((item) => match(item, selector))) ?? [];
208
+ if (affectedQueries.length === 0)
209
+ return;
210
+ affectedQueries.forEach(({ selector, options }) => {
211
+ this.emitQueryUpdate(collectionName, selector, options, "active", null);
212
+ });
213
+ await Promise.all(affectedQueries.map(async ({ selector, options }) => {
214
+ const queryItems = await this.executeQuery(collectionName, selector, options);
215
+ this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
454
216
  }));
455
217
  }
456
- // ===== CRUD with push-style query updates =====
457
- async insert(collectionName, newItem) {
458
- const storage = this.storageAdapters.get(collectionName);
459
- if (!storage)
218
+ registerCollection = async (collectionName, indices) => {
219
+ this.collectionIndices.set(collectionName, indices);
220
+ this.queries.set(collectionName, /* @__PURE__ */ new Map());
221
+ this.ensureStorageAdapter(collectionName);
222
+ const storageAdapter = this.storageAdapters.get(collectionName);
223
+ if (!storageAdapter)
460
224
  throw new Error(`No persistence adapter for collection ${collectionName}`);
461
- const existing = await this.executeQuery(collectionName, { id: newItem.id }, { limit: 1 });
462
- if (existing.length > 0)
463
- throw new Error(`Item with id ${String(newItem.id)} already exists`);
464
- await storage.insert([newItem]);
465
- await this.checkQueryUpdates(collectionName, [newItem]);
466
- return newItem;
467
- }
468
- async updateOne(collectionName, selector, modifier) {
469
- const storage = this.storageAdapters.get(collectionName);
470
- if (!storage)
225
+ const setupPromise = (async () => {
226
+ await Promise.all(indices.map((index) => storageAdapter.createIndex(index)));
227
+ await storageAdapter.setup();
228
+ })();
229
+ this.storageAdapterReady.set(collectionName, setupPromise);
230
+ await setupPromise;
231
+ };
232
+ unregisterCollection = async (collectionName) => {
233
+ this.storageAdapters.delete(collectionName);
234
+ this.queries.delete(collectionName);
235
+ };
236
+ registerQuery = async (collectionName, selector, options) => {
237
+ this.ensureQuery(collectionName, selector, options);
238
+ const queryItems = await this.executeQuery(collectionName, selector, options);
239
+ this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
240
+ };
241
+ unregisterQuery = async (collectionName, selector, options) => {
242
+ const id = queryId(selector, options);
243
+ if (!this.queries.get(collectionName))
244
+ throw new Error(`Collection ${collectionName} not initialized!`);
245
+ this.queries.get(collectionName)?.delete(id);
246
+ };
247
+ insert = async (collectionName, input) => {
248
+ const storageAdapter = this.storageAdapters.get(collectionName);
249
+ if (!storageAdapter)
471
250
  throw new Error(`No persistence adapter for collection ${collectionName}`);
472
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
473
- const [item] = items;
474
- const { $setOnInsert, ...rest } = modifier;
475
- if (item == null)
476
- return [];
477
- const modified = modify(deepClone.default(item), rest);
478
- if (item.id !== modified.id) {
479
- const existing = await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 });
480
- if (existing.length > 0)
481
- throw new Error(`Item with id ${String(modified.id)} already exists`);
482
- }
483
- await storage.replace([modified]);
484
- await this.checkQueryUpdates(collectionName, [item, modified]);
485
- return [modified];
486
- }
487
- async updateMany(collectionName, selector, modifier) {
488
- const storage = this.storageAdapters.get(collectionName);
489
- if (!storage)
251
+ const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map((i) => i[0].id) } });
252
+ const result = input.map(([item]) => {
253
+ if (item.id == null)
254
+ return new Error("Item must have an id");
255
+ if (existingItems.some((existing) => existing.id === item.id)) {
256
+ return new Error(`Item with id ${item.id} already exists`);
257
+ }
258
+ return item;
259
+ });
260
+ const newItems = result.filter((item) => !(item instanceof Error));
261
+ await storageAdapter.insert(newItems);
262
+ await this.checkQueryUpdates(collectionName, newItems);
263
+ return result;
264
+ };
265
+ updateOne = async (collectionName, parameters) => {
266
+ const storageAdapter = this.storageAdapters.get(collectionName);
267
+ if (!storageAdapter)
490
268
  throw new Error(`No persistence adapter for collection ${collectionName}`);
491
- const items = await this.executeQuery(collectionName, selector);
492
- if (items.length === 0)
493
- return [];
494
- const { $setOnInsert, ...rest } = modifier;
495
- const changed = await Promise.all(items.map(async (item) => {
496
- const modified = modify(deepClone.default(item), rest);
497
- if (item.id !== modified.id) {
498
- const existing = await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 });
499
- if (existing.length > 0)
500
- throw new Error(`Item with id ${String(modified.id)} already exists`);
269
+ const previousItems = [];
270
+ const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
271
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
272
+ const { $setOnInsert, ...restModifier } = modifier;
273
+ if (item == null)
274
+ return [];
275
+ previousItems.push(item);
276
+ const modifiedItem = modify(deepClone.default(item), restModifier);
277
+ if (item.id !== modifiedItem.id) {
278
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
279
+ if (existingItems.length > 0) {
280
+ return new Error(`Item with id ${modifiedItem.id} already exists`);
281
+ }
501
282
  }
502
- return modified;
283
+ return [modifiedItem];
503
284
  }));
504
- await storage.replace(changed);
505
- await this.checkQueryUpdates(collectionName, [...items, ...changed]);
506
- return changed;
507
- }
508
- async replaceOne(collectionName, selector, replacement) {
509
- const storage = this.storageAdapters.get(collectionName);
510
- if (!storage)
285
+ const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
286
+ if (modifiedItems.length > 0) {
287
+ await storageAdapter.replace(modifiedItems);
288
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
289
+ }
290
+ return result;
291
+ };
292
+ updateMany = async (collectionName, parameters) => {
293
+ const storageAdapter = this.storageAdapters.get(collectionName);
294
+ if (!storageAdapter)
511
295
  throw new Error(`No persistence adapter for collection ${collectionName}`);
512
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
513
- const [item] = items;
514
- if (item == null)
515
- return [];
516
- const modified = { ...replacement, id: replacement.id ?? item.id };
517
- if (item.id !== modified.id) {
518
- const existing = await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 });
519
- if (existing.length > 0)
520
- throw new Error(`Item with id ${String(modified.id)} already exists`);
296
+ const previousItems = [];
297
+ const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
298
+ const items = await this.executeQuery(collectionName, selector);
299
+ if (items.length === 0)
300
+ return [];
301
+ const { $setOnInsert, ...restModifier } = modifier;
302
+ try {
303
+ const changedItems = await Promise.all(items.map(async (item) => {
304
+ const modifiedItem = modify(deepClone.default(item), restModifier);
305
+ if (item.id !== modifiedItem.id) {
306
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
307
+ if (existingItems.length > 0) {
308
+ throw new Error(`Item with id ${modifiedItem.id} already exists`);
309
+ }
310
+ }
311
+ previousItems.push(item);
312
+ return modifiedItem;
313
+ }));
314
+ return changedItems;
315
+ } catch (error) {
316
+ return error;
317
+ }
318
+ }));
319
+ const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
320
+ if (modifiedItems.length > 0) {
321
+ await storageAdapter.replace(modifiedItems);
322
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
521
323
  }
522
- await storage.replace([modified]);
523
- await this.checkQueryUpdates(collectionName, [item, modified]);
524
- return [modified];
525
- }
526
- async removeOne(collectionName, selector) {
527
- const storage = this.storageAdapters.get(collectionName);
528
- if (!storage)
324
+ return result;
325
+ };
326
+ replaceOne = async (collectionName, parameters) => {
327
+ const storageAdapter = this.storageAdapters.get(collectionName);
328
+ if (!storageAdapter)
529
329
  throw new Error(`No persistence adapter for collection ${collectionName}`);
530
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
531
- const [item] = items;
532
- if (item == null)
533
- return [];
534
- await storage.remove([item]);
535
- await this.checkQueryUpdates(collectionName, [item]);
536
- return [item];
537
- }
538
- async removeMany(collectionName, selector) {
539
- const storage = this.storageAdapters.get(collectionName);
540
- if (!storage)
330
+ const previousItems = [];
331
+ const result = await Promise.all(parameters.map(async ([selector, replacement]) => {
332
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
333
+ if (item == null)
334
+ return [];
335
+ previousItems.push(item);
336
+ const modifiedItem = {
337
+ ...replacement,
338
+ id: replacement.id ?? item.id
339
+ };
340
+ if (item.id !== modifiedItem.id) {
341
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
342
+ if (existingItems.length > 0) {
343
+ return new Error(`Item with id ${modifiedItem.id} already exists`);
344
+ }
345
+ }
346
+ return [modifiedItem];
347
+ }));
348
+ const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
349
+ if (modifiedItems.length > 0) {
350
+ await storageAdapter.replace(modifiedItems);
351
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
352
+ }
353
+ return result;
354
+ };
355
+ removeOne = async (collectionName, selectors) => {
356
+ const storageAdapter = this.storageAdapters.get(collectionName);
357
+ if (!storageAdapter)
541
358
  throw new Error(`No persistence adapter for collection ${collectionName}`);
542
- const items = await this.executeQuery(collectionName, selector);
543
- if (items.length === 0)
544
- return [];
545
- await storage.remove(items);
546
- await this.checkQueryUpdates(collectionName, items);
547
- return items;
548
- }
549
- // ===== Upsert/merge helper for ingesting fetched items =====
550
- async upsertMerged(collectionName, incoming) {
551
- if (incoming.length === 0)
552
- return;
553
- const storage = this.storageAdapters.get(collectionName);
554
- if (!storage)
359
+ const result = await Promise.all(selectors.map(async ([selector]) => {
360
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items2) => items2[0] ?? null);
361
+ if (item == null)
362
+ return [];
363
+ return [item];
364
+ }));
365
+ const items = result.flat();
366
+ if (items.length > 0) {
367
+ await storageAdapter.remove(items);
368
+ await this.checkQueryUpdates(collectionName, items);
369
+ }
370
+ return result;
371
+ };
372
+ removeMany = async (collectionName, selectors) => {
373
+ const storageAdapter = this.storageAdapters.get(collectionName);
374
+ if (!storageAdapter)
555
375
  throw new Error(`No persistence adapter for collection ${collectionName}`);
556
- const ids = incoming.map((i) => i.id);
557
- const existing = await storage.readIds(ids);
558
- const existingById = new Map(existing.map((it) => [it.id, it]));
559
- const toInsert = [];
560
- const toReplace = [];
561
- const referenceMap = this.idRefCounts.get(collectionName);
562
- const autoload = this.autoloadIds.get(collectionName);
563
- for (const item of incoming) {
564
- const previous = existingById.get(item.id);
565
- if (previous) {
566
- toReplace.push(this.mergeItems(previous, item));
567
- } else {
568
- toInsert.push(item);
569
- }
570
- autoload?.add(item.id);
571
- referenceMap?.set(item.id, (referenceMap?.get(item.id) ?? 0) + 1);
376
+ const result = await Promise.all(selectors.map(async ([selector]) => this.executeQuery(collectionName, selector)));
377
+ const items = result.flat();
378
+ if (items.length > 0) {
379
+ await storageAdapter.remove(items);
380
+ await this.checkQueryUpdates(collectionName, items);
572
381
  }
573
- if (toInsert.length > 0)
574
- await storage.insert(toInsert);
575
- if (toReplace.length > 0)
576
- await storage.replace(toReplace);
577
- }
382
+ return result;
383
+ };
384
+ isReady = async (collectionName) => {
385
+ return this.storageAdapterReady.get(collectionName);
386
+ };
578
387
  }
579
- module.exports = AutoFetchDataAdapter;
388
+ module.exports = WorkerDataAdapterHost;