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