@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
package/dist/index13.mjs CHANGED
@@ -1,212 +1,68 @@
1
- import deepClone from "./index19.mjs";
2
- import match from "./index23.mjs";
3
- import modify from "./index7.mjs";
4
- import queryId from "./index20.mjs";
1
+ import createIndex from "./index22.mjs";
2
+ import getIndexInfo from "./index23.mjs";
3
+ import deepClone from "./index20.mjs";
4
+ import EventEmitter from "./index9.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
- class AsyncDataAdapter {
6
+ import match from "./index24.mjs";
7
+ import modify from "./index7.mjs";
8
+ import project from "./index25.mjs";
9
+ import queryId from "./index21.mjs";
10
+ import serializeValue from "./index11.mjs";
11
+ import sortItems from "./index26.mjs";
12
+ function hasPendingUpdates(pendingUpdates) {
13
+ return pendingUpdates.added.length > 0 || pendingUpdates.modified.length > 0 || pendingUpdates.removed.length > 0;
14
+ }
15
+ class DefaultDataAdapter {
16
+ items = /* @__PURE__ */ new Map();
11
17
  options;
12
- id;
13
- onError;
14
- // Per-collection resources
15
18
  storageAdapters = /* @__PURE__ */ new Map();
16
- storageAdapterReady = /* @__PURE__ */ new Map();
17
- collectionIndices = /* @__PURE__ */ new Map();
18
- // Per-collection query registries
19
- queries = /* @__PURE__ */ new Map();
19
+ collections = /* @__PURE__ */ new Set();
20
+ indices = /* @__PURE__ */ new Map();
21
+ activeQueries = /* @__PURE__ */ new Map();
22
+ queryEmitters = /* @__PURE__ */ new Map();
23
+ queuedQueryUpdates = /* @__PURE__ */ new Map();
24
+ cachedQueryResults = /* @__PURE__ */ new Map();
20
25
  constructor(options) {
21
- this.options = options;
22
- this.id = options.id || "async-data-adapter";
23
- this.onError = options.onError ?? ((error) => {
24
- console.error(error);
25
- });
26
- }
27
- createCollectionBackend(collection, indices) {
28
- this.collectionIndices.set(collection.name, indices);
29
- this.queries.set(collection.name, /* @__PURE__ */ new Map());
30
- this.ensureStorageAdapter(collection.name);
31
- const ready = (async () => {
32
- try {
33
- await this.setupStorage(collection.name, indices);
34
- } catch (error) {
35
- this.onError(error);
36
- throw error;
37
- }
38
- })();
39
- this.storageAdapterReady.set(collection.name, ready);
40
- const registerQuery = (selector, options) => {
41
- const qid = queryId(selector, options);
42
- const registry = this.queries.get(collection.name);
43
- if (!registry)
44
- throw new Error(`Collection ${collection.name} not initialized!`);
45
- registry.set(qid, {
46
- selector,
47
- options,
48
- items: [],
49
- listeners: /* @__PURE__ */ new Set(),
50
- ...registry.get(qid),
51
- state: "active",
52
- error: null
53
- });
54
- void this.fulfillQuery(collection.name, selector, options).catch(this.onError);
55
- };
56
- const unregisterQuery = (selector, options) => {
57
- const qid = queryId(selector, options);
58
- this.queries.get(collection.name)?.delete(qid);
59
- };
60
- const getQueryState = (selector, options) => {
61
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
62
- return q?.state ?? "active";
63
- };
64
- const getQueryError = (selector, options) => {
65
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
66
- return q?.error ?? null;
67
- };
68
- const getQueryResult = (selector, options) => {
69
- const q = this.queries.get(collection.name)?.get(queryId(selector, options));
70
- return q?.items ?? [];
71
- };
72
- const onQueryStateChange = (selector, options, callback) => {
73
- const qid = queryId(selector, options);
74
- const registry = this.queries.get(collection.name);
75
- if (!registry)
76
- throw new Error(`Collection ${collection.name} not initialized!`);
77
- if (!registry.has(qid)) {
78
- registry.set(qid, {
79
- selector,
80
- options,
81
- state: "active",
82
- error: null,
83
- items: [],
84
- listeners: /* @__PURE__ */ new Set()
85
- });
86
- }
87
- registry.get(qid)?.listeners.add(callback);
88
- return () => registry.get(qid)?.listeners.delete(callback);
89
- };
90
- return {
91
- insert: async (item) => {
92
- await ready;
93
- const inserted = await this.insert(collection.name, item);
94
- return inserted;
95
- },
96
- updateOne: async (selector, modifier) => {
97
- await ready;
98
- return this.updateOne(collection.name, selector, modifier);
99
- },
100
- updateMany: async (selector, modifier) => {
101
- await ready;
102
- return this.updateMany(collection.name, selector, modifier);
103
- },
104
- replaceOne: async (selector, replacement) => {
105
- await ready;
106
- return this.replaceOne(collection.name, selector, replacement);
107
- },
108
- removeOne: async (selector) => {
109
- await ready;
110
- return this.removeOne(collection.name, selector);
111
- },
112
- removeMany: async (selector) => {
113
- await ready;
114
- return this.removeMany(collection.name, selector);
115
- },
116
- registerQuery,
117
- unregisterQuery,
118
- getQueryState,
119
- getQueryError,
120
- getQueryResult,
121
- onQueryStateChange,
122
- executeQuery: async (selector, options) => {
123
- await ready;
124
- return this.executeQuery(collection.name, selector, options);
125
- },
126
- dispose: async () => {
127
- this.storageAdapters.delete(collection.name);
128
- this.queries.delete(collection.name);
129
- this.collectionIndices.delete(collection.name);
130
- this.storageAdapterReady.delete(collection.name);
131
- },
132
- isReady: async () => {
133
- await ready;
134
- }
135
- };
136
- }
137
- async setupStorage(collectionName, indices) {
138
- const storage = this.storageAdapters.get(collectionName);
139
- if (!storage)
140
- throw new Error(`No persistence adapter for collection ${collectionName}`);
141
- await storage.setup();
142
- await Promise.all(indices.map((index) => storage.createIndex(index)));
26
+ this.options = options || {};
143
27
  }
144
28
  ensureStorageAdapter(name) {
145
- if (this.storageAdapters.has(name))
29
+ if (this.storageAdapters.get(name))
30
+ return;
31
+ if (!this.options.storage)
146
32
  return;
147
33
  const adapter = this.options.storage(name);
148
34
  if (!adapter)
149
35
  return;
150
36
  this.storageAdapters.set(name, adapter);
151
37
  }
152
- /**
153
- * Compute and publish the result for a specific query
154
- * @param collectionName - name of the collection
155
- * @param selector - query selector
156
- * @param options - query options
157
- */
158
- async fulfillQuery(collectionName, selector, options) {
159
- const qid = queryId(selector, options);
160
- const registry = this.queries.get(collectionName);
161
- if (!registry)
162
- throw new Error(`Collection ${collectionName} not initialized!`);
163
- const rec = registry.get(qid);
164
- if (!rec)
165
- return;
166
- this.publishState(collectionName, qid, "active", null);
167
- try {
168
- const items = await this.executeQuery(collectionName, selector, options);
169
- this.publishResult(collectionName, qid, items);
170
- this.publishState(collectionName, qid, "complete", null);
171
- } catch (error) {
172
- this.publishState(collectionName, qid, "error", error);
173
- }
38
+ rebuildIndices(collection) {
39
+ const items = this.items.get(collection.name);
40
+ if (!items)
41
+ throw new Error(`Items not found for collection ${collection.name}`);
42
+ const indices = this.indices.get(collection.name);
43
+ if (!indices)
44
+ throw new Error(`Indices not found for collection ${collection.name}`);
45
+ indices.forEach((index) => index.rebuild([...items.values()]));
174
46
  }
175
- /**
176
- * Notify listeners about state changes and keep the cache updated
177
- * @param collectionName - name of the collection
178
- * @param qid - query id
179
- * @param state - new state
180
- * @param error - error if state is 'error', null otherwise
181
- */
182
- publishState(collectionName, qid, state, error) {
183
- const rec = this.queries.get(collectionName)?.get(qid);
184
- if (!rec)
47
+ async setupStorageAdapter(collection) {
48
+ const storageAdapter = this.storageAdapters.get(collection.name);
49
+ if (!storageAdapter)
185
50
  return;
186
- rec.state = state;
187
- rec.error = error;
188
- for (const callback of rec.listeners) {
189
- try {
190
- callback(state);
191
- } catch (error_) {
192
- this.onError(error_);
51
+ return storageAdapter.setup().then(async () => {
52
+ await this.fetchItemsFromStorage(collection);
53
+ }).catch((error) => {
54
+ if (!this.options.onError) {
55
+ console.error(`Error during data persistence operation in collection ${collection.name}`, error);
56
+ return;
193
57
  }
194
- }
195
- }
196
- publishResult(collectionName, qid, items) {
197
- const rec = this.queries.get(collectionName)?.get(qid);
198
- if (!rec)
199
- return;
200
- rec.items = items;
58
+ this.options.onError(collection.name, error instanceof Error ? error : new Error(error));
59
+ });
201
60
  }
202
- async getIndexInfo(collectionName, selector) {
203
- const storageAdapter = this.storageAdapters.get(collectionName);
204
- if (!storageAdapter)
205
- throw new Error(`No persistence adapter for collection ${collectionName}`);
61
+ getIndexInfo(collection, selector) {
206
62
  if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
207
63
  return {
208
64
  matched: true,
209
- ids: [selector.id].filter(Boolean),
65
+ ids: [serializeValue(selector.id)],
210
66
  optimizedSelector: {}
211
67
  };
212
68
  }
@@ -217,68 +73,49 @@ class AsyncDataAdapter {
217
73
  optimizedSelector: {}
218
74
  };
219
75
  }
220
- const indices = this.collectionIndices.get(collectionName) ?? [];
221
- return getIndexInfo(indices.map((field) => async (flatSelector) => {
222
- if (!Object.hasOwnProperty.call(flatSelector, field))
223
- return { matched: false };
224
- const index = await storageAdapter.readIndex(field);
225
- const fieldSelector = flatSelector[field];
226
- const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
227
- const keys = filtersForNull ? { include: null, exclude: [...index.keys()].filter((key) => key != null) } : getMatchingKeys(field, flatSelector);
228
- if (keys.include == null && keys.exclude == null)
229
- return { matched: false };
230
- let includedIds = [];
231
- if (keys.include == null) {
232
- for (const set of index.values())
233
- for (const pos of set)
234
- includedIds.push(pos);
235
- } else {
236
- for (const key of keys.include) {
237
- const idSet = index.get(key);
238
- if (idSet)
239
- for (const id of idSet)
240
- includedIds.push(id);
241
- }
242
- }
243
- if (keys.exclude != null) {
244
- const excludeIds = /* @__PURE__ */ new Set();
245
- for (const key of keys.exclude) {
246
- const idSet = index.get(key);
247
- if (idSet)
248
- for (const id of idSet)
249
- excludeIds.add(id);
250
- }
251
- includedIds = includedIds.filter((pos) => !excludeIds.has(pos));
252
- }
253
- return { matched: true, ids: includedIds, fields: [field], keepSelector: filtersForNull };
254
- }), selector);
76
+ return getIndexInfo((this.indices.get(collection.name) ?? []).map((i) => i.query.bind(i)), selector);
77
+ }
78
+ applyIndexDeltas(collection, changes) {
79
+ const indices = this.indices.get(collection.name) ?? [];
80
+ for (const index of indices) {
81
+ if (changes.modified)
82
+ index.update(changes.modified);
83
+ if (changes.added && changes.added.length > 0)
84
+ index.insert(changes.added);
85
+ if (changes.removed && changes.removed.length > 0)
86
+ index.remove(changes.removed);
87
+ }
88
+ }
89
+ getItem(collection, selector) {
90
+ const memory = this.items.get(collection.name) ?? /* @__PURE__ */ new Map();
91
+ const indexInfo = this.getIndexInfo(collection, selector);
92
+ const items = indexInfo.matched ? indexInfo.ids.map((id) => memory.get(serializeValue(id))).filter((i) => i != null) : [...memory.values()];
93
+ const item = items.find((document) => match(document, selector));
94
+ return item;
255
95
  }
256
- async queryItems(collectionName, selector) {
257
- const storage = this.storageAdapters.get(collectionName);
258
- if (!storage)
259
- throw new Error(`No persistence adapter for collection ${collectionName}`);
260
- const index = await this.getIndexInfo(collectionName, selector);
96
+ queryItems(collection, selector) {
97
+ const indexInfo = this.getIndexInfo(collection, selector);
261
98
  const matchItems = (item) => {
262
- if (index.optimizedSelector == null)
99
+ if (indexInfo.optimizedSelector == null)
263
100
  return true;
264
- if (Object.keys(index.optimizedSelector).length <= 0)
101
+ if (Object.keys(indexInfo.optimizedSelector).length <= 0)
265
102
  return true;
266
- return match(item, index.optimizedSelector);
103
+ const matches = match(item, indexInfo.optimizedSelector);
104
+ return matches;
267
105
  };
268
- if (index.matched) {
269
- const items = await storage.readIds(index.ids);
270
- if (isEqual(index.optimizedSelector, {}))
271
- return items;
272
- return items.filter(matchItems);
273
- } else {
274
- const allItems = await storage.readAll();
106
+ const items = this.items.get(collection.name);
107
+ if (!indexInfo.matched) {
275
108
  if (isEqual(selector, {}))
276
- return allItems;
277
- return allItems.filter(matchItems);
109
+ return [...items.values()];
110
+ return [...items.values()].filter(matchItems);
278
111
  }
112
+ const foundItems = indexInfo.ids.map((ids) => items.get(serializeValue(ids))).filter((i) => i != null);
113
+ if (isEqual(indexInfo.optimizedSelector, {}))
114
+ return foundItems;
115
+ return foundItems.filter(matchItems);
279
116
  }
280
- async executeQuery(collectionName, selector, options) {
281
- const items = await this.queryItems(collectionName, selector || {});
117
+ executeQuery(collection, selector, options) {
118
+ const items = this.queryItems(collection, selector || {});
282
119
  const { sort, skip, limit, fields } = options || {};
283
120
  const sorted = sort ? sortItems(items, sort) : items;
284
121
  const skipped = skip ? sorted.slice(skip) : sorted;
@@ -287,131 +124,243 @@ class AsyncDataAdapter {
287
124
  return limited.map((item) => {
288
125
  if (!fields)
289
126
  return item;
290
- return { ...idExcluded ? {} : { id: item.id }, ...project(item, fields) };
127
+ return {
128
+ ...idExcluded ? {} : { id: item.id },
129
+ ...project(item, fields)
130
+ };
291
131
  });
292
132
  }
293
- /**
294
- * After mutations, recompute and push updates for affected active queries
295
- * @param collectionName - name of the collection
296
- * @param changedItems - items that were inserted/updated/replaced/removed
297
- */
298
- async checkQueryUpdates(collectionName, changedItems) {
299
- const registry = this.queries.get(collectionName);
300
- if (!registry)
301
- throw new Error(`Collection ${collectionName} not initialized!`);
302
- if (registry.size === 0)
133
+ flushQueuedQueryUpdates(collection) {
134
+ if (!this.queuedQueryUpdates.get(collection.name))
303
135
  return;
304
- const affected = [...registry.values()].filter(({ selector }) => changedItems.some((item) => match(item, selector)));
305
- if (affected.length === 0)
136
+ const changes = this.queuedQueryUpdates.get(collection.name);
137
+ if (!changes || !hasPendingUpdates(changes))
306
138
  return;
307
- for (const { selector, options } of affected) {
308
- const qid = queryId(selector, options);
309
- this.publishState(collectionName, qid, "active", null);
310
- }
311
- await Promise.all(affected.map(async ({ selector, options }) => {
312
- const qid = queryId(selector, options);
313
- try {
314
- const items = await this.executeQuery(collectionName, selector, options);
315
- this.publishResult(collectionName, qid, items);
316
- this.publishState(collectionName, qid, "complete", null);
317
- } catch (error) {
318
- this.publishState(collectionName, qid, "error", error);
319
- }
320
- }));
139
+ this.queuedQueryUpdates.set(collection.name, { added: [], modified: [], removed: [] });
140
+ const flatItems = [...changes.added, ...changes.modified, ...changes.removed];
141
+ const itemIds = new Set(flatItems.map((i) => i.id));
142
+ const queries = [
143
+ ...this.activeQueries.get(collection.name)?.values() ?? []
144
+ ].filter(({ selector, options }) => {
145
+ const idsInQuery = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options))?.map((i) => i.id) ?? [];
146
+ if (idsInQuery.some((id) => itemIds.has(id)))
147
+ return true;
148
+ return flatItems.some((item) => match(item, selector));
149
+ });
150
+ queries.forEach(({ selector, options }) => {
151
+ this.executeAndCacheQuery(collection, selector, options);
152
+ });
321
153
  }
322
- async insert(collectionName, newItem) {
323
- const storage = this.storageAdapters.get(collectionName);
324
- if (!storage)
325
- throw new Error(`No persistence adapter for collection ${collectionName}`);
326
- const existingItems = await storage.readIds([newItem.id]);
327
- if (existingItems.length > 0)
328
- throw new Error(`Item with id ${String(newItem.id)} already exists`);
329
- await storage.insert([newItem]);
330
- await this.checkQueryUpdates(collectionName, [newItem]);
331
- return newItem;
154
+ executeAndCacheQuery(collection, selector, options) {
155
+ const result = this.executeQuery(collection, selector, options);
156
+ this.cachedQueryResults.set(collection.name, this.cachedQueryResults.get(collection.name) || /* @__PURE__ */ new Map());
157
+ this.cachedQueryResults.get(collection.name)?.set(queryId(selector, options), result);
158
+ const emitter = this.queryEmitters.get(collection.name);
159
+ if (!emitter)
160
+ return;
161
+ emitter.emit("change", selector, options, "complete");
332
162
  }
333
- async updateOne(collectionName, selector, modifier) {
334
- const storage = this.storageAdapters.get(collectionName);
335
- if (!storage)
336
- throw new Error(`No persistence adapter for collection ${collectionName}`);
337
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
338
- const [item] = items;
339
- const { $setOnInsert, ...rest } = modifier;
340
- if (item == null)
341
- return [];
342
- const modified = modify(deepClone(item), rest);
343
- if (item.id !== modified.id) {
344
- const existing = await storage.readIds([modified.id]);
345
- if (existing.length > 0)
346
- throw new Error(`Item with id ${String(modified.id)} already exists`);
347
- }
348
- await storage.replace([modified]);
349
- await this.checkQueryUpdates(collectionName, [modified]);
350
- return [modified];
163
+ updateQueries(collection, changes) {
164
+ this.queuedQueryUpdates.set(collection.name, this.queuedQueryUpdates.get(collection.name) || { added: [], modified: [], removed: [] });
165
+ this.queuedQueryUpdates.get(collection.name)?.added.push(...changes.added);
166
+ this.queuedQueryUpdates.get(collection.name)?.modified.push(...changes.modified);
167
+ this.queuedQueryUpdates.get(collection.name)?.removed.push(...changes.removed);
168
+ this.flushQueuedQueryUpdates(collection);
351
169
  }
352
- async updateMany(collectionName, selector, modifier) {
353
- const storage = this.storageAdapters.get(collectionName);
354
- if (!storage)
355
- throw new Error(`No persistence adapter for collection ${collectionName}`);
356
- const items = await this.executeQuery(collectionName, selector);
357
- if (items.length === 0)
358
- return [];
359
- const { $setOnInsert, ...rest } = modifier;
360
- const changed = await Promise.all(items.map(async (item) => {
361
- const modified = modify(deepClone(item), rest);
362
- if (item.id !== modified.id) {
363
- const existing = await storage.readIds([modified.id]);
364
- if (existing.length > 0)
365
- throw new Error(`Item with id ${String(modified.id)} already exists`);
366
- }
367
- return modified;
368
- }));
369
- await storage.replace(changed);
370
- await this.checkQueryUpdates(collectionName, changed);
371
- return changed;
170
+ createCollectionBackend(collection, indices) {
171
+ this.collections.add(collection);
172
+ this.ensureStorageAdapter(collection.name);
173
+ this.items.set(collection.name, this.items.get(collection.name) ?? /* @__PURE__ */ new Map());
174
+ this.queryEmitters.set(collection.name, this.queryEmitters.get(collection.name) ?? new EventEmitter());
175
+ this.queryEmitters.get(collection.name)?.setMaxListeners(Infinity);
176
+ this.activeQueries.set(collection.name, this.activeQueries.get(collection.name) || /* @__PURE__ */ new Map());
177
+ this.indices.set(collection.name, indices.map((field) => createIndex(field)));
178
+ this.rebuildIndices(collection);
179
+ const persistenceReadyPromise = this.setupStorageAdapter(collection);
180
+ const backend = {
181
+ // CRUD operations
182
+ insert: async (newItem) => {
183
+ const items = this.items.get(collection.name);
184
+ if (!items)
185
+ throw new Error(`Items not found for collection ${collection.name}`);
186
+ if (items.has(serializeValue(newItem.id))) {
187
+ throw new Error(`Item with id '${newItem.id}' already exists`);
188
+ }
189
+ this.items.get(collection.name)?.set(serializeValue(newItem.id), newItem);
190
+ await this.storageAdapters.get(collection.name)?.insert([newItem]);
191
+ this.applyIndexDeltas(collection, { added: [newItem] });
192
+ this.updateQueries(collection, {
193
+ added: [],
194
+ modified: [newItem],
195
+ removed: []
196
+ });
197
+ return newItem;
198
+ },
199
+ updateOne: async (selector, modifier) => {
200
+ const { $setOnInsert, ...restModifier } = modifier;
201
+ const item = this.getItem(collection, selector);
202
+ if (item == null)
203
+ return [];
204
+ const modifiedItem = modify(deepClone(item), restModifier);
205
+ const hasItemWithSameId = item.id !== modifiedItem.id && this.getItem(collection, { id: modifiedItem.id }) != null;
206
+ if (hasItemWithSameId) {
207
+ throw new Error(`Item with id '${modifiedItem.id}' already exists`);
208
+ }
209
+ this.items.get(collection.name)?.set(serializeValue(modifiedItem.id), modifiedItem);
210
+ await this.storageAdapters.get(collection.name)?.replace([modifiedItem]);
211
+ this.applyIndexDeltas(collection, { modified: [{ oldItem: item, newItem: modifiedItem }] });
212
+ this.updateQueries(collection, {
213
+ added: [],
214
+ modified: [modifiedItem],
215
+ removed: []
216
+ });
217
+ return [modifiedItem];
218
+ },
219
+ updateMany: async (selector, modifier) => {
220
+ const { $setOnInsert, ...restModifier } = modifier;
221
+ const items = this.executeQuery(collection, selector, {});
222
+ const changedItems = items.map((item) => {
223
+ const modifiedItem = modify(deepClone(item), restModifier);
224
+ const hasItemWithSameId = item.id !== modifiedItem.id && this.getItem(collection, { id: modifiedItem.id }) != null;
225
+ if (hasItemWithSameId) {
226
+ throw new Error(`Item with id '${modifiedItem.id}' already exists`);
227
+ }
228
+ return modifiedItem;
229
+ });
230
+ changedItems.forEach((item) => {
231
+ this.items.get(collection.name)?.set(serializeValue(item.id), item);
232
+ });
233
+ await this.storageAdapters.get(collection.name)?.replace(changedItems);
234
+ const pairs = items.map((oldItem, index) => ({ oldItem, newItem: changedItems[index] }));
235
+ this.applyIndexDeltas(collection, { modified: pairs });
236
+ this.updateQueries(collection, {
237
+ added: [],
238
+ modified: changedItems,
239
+ removed: []
240
+ });
241
+ return changedItems;
242
+ },
243
+ replaceOne: async (selector, replacement) => {
244
+ const item = this.getItem(collection, selector);
245
+ if (item == null)
246
+ return [];
247
+ const hasItemWithSameId = item.id !== replacement.id && replacement.id != null && this.getItem(collection, { id: replacement.id }) != null;
248
+ if (hasItemWithSameId) {
249
+ throw new Error(`Item with id '${replacement.id}' already exists`);
250
+ }
251
+ const modifiedItem = { id: item.id, ...replacement };
252
+ this.items.get(collection.name)?.set(serializeValue(modifiedItem.id), modifiedItem);
253
+ await this.storageAdapters.get(collection.name)?.replace([modifiedItem]);
254
+ this.applyIndexDeltas(collection, { modified: [{ oldItem: item, newItem: modifiedItem }] });
255
+ this.updateQueries(collection, {
256
+ added: [],
257
+ modified: [modifiedItem],
258
+ removed: []
259
+ });
260
+ return [modifiedItem];
261
+ },
262
+ removeOne: async (selector) => {
263
+ const item = this.getItem(collection, selector);
264
+ if (item == null)
265
+ return [];
266
+ this.items.get(collection.name)?.delete(serializeValue(item.id));
267
+ await this.storageAdapters.get(collection.name)?.remove([item]);
268
+ this.applyIndexDeltas(collection, { removed: [item] });
269
+ this.updateQueries(collection, {
270
+ added: [],
271
+ modified: [],
272
+ removed: [item]
273
+ });
274
+ return [item];
275
+ },
276
+ removeMany: async (selector) => {
277
+ const items = backend.getQueryResult(selector, {});
278
+ items.forEach((item) => {
279
+ this.items.get(collection.name)?.delete(serializeValue(item.id));
280
+ });
281
+ await this.storageAdapters.get(collection.name)?.remove(items);
282
+ this.applyIndexDeltas(collection, { removed: items });
283
+ this.updateQueries(collection, {
284
+ added: [],
285
+ modified: [],
286
+ removed: items
287
+ });
288
+ return items;
289
+ },
290
+ // methods for registering and unregistering queries that will be called from the collection during find/findOne
291
+ registerQuery: (selector, options) => {
292
+ this.activeQueries.set(collection.name, this.activeQueries.get(collection.name) || /* @__PURE__ */ new Map());
293
+ this.activeQueries.get(collection.name)?.set(queryId(selector, options), {
294
+ selector,
295
+ options
296
+ });
297
+ this.executeAndCacheQuery(collection, selector, options);
298
+ },
299
+ unregisterQuery: (selector, options) => {
300
+ if (!this.activeQueries.get(collection.name))
301
+ return;
302
+ this.activeQueries.get(collection.name)?.delete(queryId(selector, options));
303
+ },
304
+ getQueryState: () => "complete",
305
+ onQueryStateChange: (selector, options, callback) => {
306
+ const emitter = this.queryEmitters.get(collection.name);
307
+ if (!emitter)
308
+ throw new Error(`Query emitter not found for collection ${collection.name}`);
309
+ const handler = (querySelector, queryOptions, state) => {
310
+ if (queryId(querySelector, queryOptions) !== queryId(selector, options))
311
+ return;
312
+ callback(state);
313
+ };
314
+ emitter.on("change", handler);
315
+ return () => {
316
+ emitter.off("change", handler);
317
+ };
318
+ },
319
+ getQueryError: () => null,
320
+ getQueryResult: (selector, options) => {
321
+ const isQueryActive = this.activeQueries.get(collection.name)?.has(queryId(selector, options));
322
+ if (isQueryActive) {
323
+ const results = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options));
324
+ if (!results)
325
+ throw new Error("Cached query results are not defined!");
326
+ return results;
327
+ }
328
+ return this.executeQuery(collection, selector, options);
329
+ },
330
+ executeQuery: (selector, options) => Promise.resolve(this.executeQuery(collection, selector, options)),
331
+ // lifecycle methods
332
+ dispose: async () => {
333
+ const adapter = this.storageAdapters.get(collection.name);
334
+ if (adapter?.teardown)
335
+ await adapter.teardown();
336
+ this.storageAdapters.delete(collection.name);
337
+ this.items.delete(collection.name);
338
+ this.indices.delete(collection.name);
339
+ this.activeQueries.delete(collection.name);
340
+ this.queryEmitters.delete(collection.name);
341
+ this.queuedQueryUpdates.delete(collection.name);
342
+ this.cachedQueryResults.delete(collection.name);
343
+ },
344
+ isReady: () => persistenceReadyPromise
345
+ };
346
+ return backend;
372
347
  }
373
- async replaceOne(collectionName, selector, replacement) {
374
- const storage = this.storageAdapters.get(collectionName);
375
- if (!storage)
376
- throw new Error(`No persistence adapter for collection ${collectionName}`);
377
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
378
- const [item] = items;
379
- if (item == null)
380
- return [];
381
- const modified = { ...replacement, id: replacement.id ?? item.id };
382
- if (item.id !== modified.id) {
383
- const existing = await storage.readIds([modified.id]);
384
- if (existing.length > 0)
385
- throw new Error(`Item with id ${String(modified.id)} already exists`);
348
+ async fetchItemsFromStorage(collection) {
349
+ if (!collection) {
350
+ await Promise.all([...this.collections].map((currentCollection) => this.fetchItemsFromStorage(currentCollection)));
351
+ return;
386
352
  }
387
- await storage.replace([modified]);
388
- await this.checkQueryUpdates(collectionName, [modified]);
389
- return [modified];
390
- }
391
- async removeOne(collectionName, selector) {
392
- const storage = this.storageAdapters.get(collectionName);
393
- if (!storage)
394
- throw new Error(`No persistence adapter for collection ${collectionName}`);
395
- const items = await this.executeQuery(collectionName, selector, { limit: 1 });
396
- const [item] = items;
397
- if (item == null)
398
- return [];
399
- await storage.remove([item]);
400
- await this.checkQueryUpdates(collectionName, [item]);
401
- return [item];
402
- }
403
- async removeMany(collectionName, selector) {
404
- const storage = this.storageAdapters.get(collectionName);
405
- if (!storage)
406
- throw new Error(`No persistence adapter for collection ${collectionName}`);
407
- const items = await this.executeQuery(collectionName, selector);
408
- if (items.length === 0)
409
- return [];
410
- await storage.remove(items);
411
- await this.checkQueryUpdates(collectionName, items);
412
- return items;
353
+ const storageAdapter = this.storageAdapters.get(collection.name);
354
+ if (!storageAdapter)
355
+ return;
356
+ const items = await storageAdapter.readAll();
357
+ this.items.set(collection.name, items.reduce((map, item) => {
358
+ map.set(serializeValue(item.id), item);
359
+ return map;
360
+ }, /* @__PURE__ */ new Map()));
361
+ this.rebuildIndices(collection);
413
362
  }
414
363
  }
415
364
  export {
416
- AsyncDataAdapter as default
365
+ DefaultDataAdapter as default
417
366
  };