@signaldb/core 1.7.0 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/.vite/manifest.json +125 -82
  2. package/dist/AsyncDataAdapter.d.ts +64 -0
  3. package/dist/AutoFetchDataAdapter.d.ts +113 -0
  4. package/dist/Collection/Cursor.d.ts +9 -9
  5. package/dist/Collection/Observer.d.ts +3 -2
  6. package/dist/Collection/index.d.ts +52 -46
  7. package/dist/Collection/types.d.ts +4 -9
  8. package/dist/DataAdapter.d.ts +33 -0
  9. package/dist/DefaultDataAdapter.d.ts +32 -0
  10. package/dist/WorkerDataAdapter.d.ts +22 -0
  11. package/dist/WorkerDataAdapterHost.d.ts +59 -0
  12. package/dist/createIndex.d.ts +7 -0
  13. package/dist/createStorageAdapter.d.ts +9 -0
  14. package/dist/{Collection/getIndexInfo.d.ts → getIndexInfo.d.ts} +17 -19
  15. package/dist/index.cjs.js +21 -17
  16. package/dist/index.cjs10.js +15 -26
  17. package/dist/index.cjs11.js +13 -10
  18. package/dist/index.cjs12.js +337 -3
  19. package/dist/index.cjs13.js +384 -115
  20. package/dist/index.cjs14.js +183 -67
  21. package/dist/index.cjs15.js +339 -8
  22. package/dist/index.cjs16.js +563 -20
  23. package/dist/index.cjs17.js +16 -12
  24. package/dist/index.cjs18.js +24 -5
  25. package/dist/index.cjs2.js +27 -36
  26. package/dist/index.cjs20.js +102 -13
  27. package/dist/index.cjs21.js +124 -21
  28. package/dist/index.cjs22.js +5 -68
  29. package/dist/index.cjs23.js +22 -82
  30. package/dist/index.cjs24.js +5 -16
  31. package/dist/index.cjs25.js +8 -27
  32. package/dist/index.cjs26.js +27 -7
  33. package/dist/index.cjs27.js +3 -5
  34. package/dist/index.cjs28.js +5 -27
  35. package/dist/index.cjs29.js +27 -40
  36. package/dist/index.cjs3.js +242 -497
  37. package/dist/index.cjs30.js +42 -0
  38. package/dist/index.cjs31.js +9 -0
  39. package/dist/index.cjs4.js +3 -170
  40. package/dist/index.cjs5.js +3 -67
  41. package/dist/index.cjs6.js +27 -3
  42. package/dist/index.cjs7.js +10 -3
  43. package/dist/index.cjs8.js +3 -3
  44. package/dist/index.cjs9.js +131 -3
  45. package/dist/index.d.ts +10 -8
  46. package/dist/index.mjs +15 -11
  47. package/dist/index10.mjs +15 -26
  48. package/dist/index11.mjs +13 -10
  49. package/dist/index12.mjs +337 -3
  50. package/dist/index13.mjs +384 -115
  51. package/dist/index14.mjs +183 -66
  52. package/dist/index15.mjs +339 -8
  53. package/dist/index16.mjs +563 -20
  54. package/dist/index17.mjs +16 -12
  55. package/dist/index18.mjs +24 -5
  56. package/dist/index2.mjs +27 -36
  57. package/dist/index20.mjs +102 -13
  58. package/dist/index21.mjs +123 -21
  59. package/dist/index22.mjs +5 -67
  60. package/dist/index23.mjs +22 -81
  61. package/dist/index24.mjs +5 -16
  62. package/dist/index25.mjs +8 -27
  63. package/dist/index26.mjs +27 -7
  64. package/dist/index27.mjs +3 -5
  65. package/dist/index28.mjs +5 -27
  66. package/dist/index29.mjs +27 -40
  67. package/dist/index3.mjs +241 -497
  68. package/dist/index30.mjs +43 -0
  69. package/dist/index31.mjs +10 -0
  70. package/dist/index4.mjs +3 -170
  71. package/dist/index5.mjs +3 -66
  72. package/dist/index6.mjs +27 -3
  73. package/dist/index7.mjs +10 -3
  74. package/dist/index8.mjs +3 -3
  75. package/dist/index9.mjs +131 -3
  76. package/dist/types/IndexProvider.d.ts +12 -7
  77. package/dist/types/StorageAdapter.d.ts +20 -0
  78. package/dist/utils/objectId.d.ts +8 -0
  79. package/dist/utils/queryId.d.ts +9 -0
  80. package/package.json +2 -2
  81. package/dist/AutoFetchCollection.d.ts +0 -60
  82. package/dist/Collection/createIndex.d.ts +0 -15
  83. package/dist/ReplicatedCollection.d.ts +0 -60
  84. package/dist/createMemoryAdapter.d.ts +0 -7
  85. package/dist/persistence/combinePersistenceAdapters.d.ts +0 -32
  86. package/dist/persistence/createPersistenceAdapter.d.ts +0 -9
  87. package/dist/types/MemoryAdapter.d.ts +0 -15
  88. package/dist/types/PersistenceAdapter.d.ts +0 -20
package/dist/index3.mjs CHANGED
@@ -1,253 +1,96 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import EventEmitter from "./index13.mjs";
5
- import match from "./index18.mjs";
6
- import modify from "./index11.mjs";
7
- import isEqual from "./index10.mjs";
8
- import randomId from "./index12.mjs";
1
+ import EventEmitter from "./index9.mjs";
2
+ import createSignal from "./index18.mjs";
3
+ import randomId from "./index8.mjs";
4
+ import DefaultDataAdapter from "./index12.mjs";
5
+ import modify from "./index7.mjs";
9
6
  import deepClone from "./index19.mjs";
10
- import serializeValue from "./index20.mjs";
11
- import createSignal from "./index21.mjs";
12
7
  import Cursor from "./index2.mjs";
13
- import getIndexInfo from "./index22.mjs";
14
- import { createExternalIndex } from "./index14.mjs";
15
- import { default as default2 } from "./index14.mjs";
16
- function hasPendingUpdates(pendingUpdates) {
17
- return pendingUpdates.added.length > 0 || pendingUpdates.modified.length > 0 || pendingUpdates.removed.length > 0;
18
- }
19
- function applyUpdates(currentItems, { added, modified, removed }) {
20
- const items = [...currentItems];
21
- added.forEach((item) => {
22
- items.push(item);
23
- });
24
- modified.forEach((item) => {
25
- const index = items.findIndex(({ id }) => id === item.id);
26
- if (index === -1)
27
- return;
28
- items[index] = item;
29
- });
30
- removed.forEach((item) => {
31
- const index = items.findIndex(({ id }) => id === item.id);
32
- if (index === -1)
33
- return;
34
- items.splice(index, 1);
35
- });
36
- return items;
37
- }
38
- const _Collection = class _Collection extends EventEmitter {
39
- /**
40
- * Initializes a new instance of the `Collection` class with optional configuration.
41
- * Sets up memory, persistence, reactivity, and indices as specified in the options.
42
- * @template T - The type of the items stored in the collection.
43
- * @template I - The type of the unique identifier for the items.
44
- * @template U - The transformed item type after applying transformations (default is T).
45
- * @param options - Optional configuration for the collection.
46
- * @param options.name - An optional name for the collection.
47
- * @param options.memory - The in-memory adapter for storing items.
48
- * @param options.reactivity - The reactivity adapter for observing changes in the collection.
49
- * @param options.transform - A transformation function to apply to items when retrieving them.
50
- * @param options.persistence - The persistence adapter for saving and loading items.
51
- * @param options.indices - An array of index providers for optimized querying.
52
- * @param options.enableDebugMode - A boolean to enable or disable debug mode.
53
- * @param options.fieldTracking - A boolean to enable or disable field tracking by default.
54
- */
55
- constructor(options) {
56
- super();
57
- __publicField(this, "name");
58
- __publicField(this, "options");
59
- __publicField(this, "persistenceAdapter", null);
60
- __publicField(this, "isPullingSignal");
61
- __publicField(this, "isPushingSignal");
62
- __publicField(this, "indexProviders", []);
63
- __publicField(this, "indicesOutdated", false);
64
- __publicField(this, "idIndex", /* @__PURE__ */ new Map());
65
- __publicField(this, "debugMode");
66
- __publicField(this, "batchOperationInProgress", false);
67
- __publicField(this, "isDisposed", false);
68
- __publicField(this, "postBatchCallbacks", /* @__PURE__ */ new Set());
69
- __publicField(this, "fieldTracking", false);
70
- __publicField(this, "persistenceReadyPromise");
71
- _Collection.collections.push(this);
72
- this.name = (options == null ? void 0 : options.name) ?? `${this.constructor.name}-${randomId()}`;
73
- this.options = {
74
- memory: [],
75
- ...options
76
- };
77
- this.fieldTracking = this.options.fieldTracking ?? _Collection.fieldTracking;
78
- this.debugMode = this.options.enableDebugMode ?? _Collection.debugMode;
79
- this.indexProviders = [
80
- createExternalIndex("id", this.idIndex),
81
- ...this.options.indices || []
82
- ];
83
- this.rebuildIndices();
84
- this.isPullingSignal = createSignal(this.options.reactivity, !!(options == null ? void 0 : options.persistence));
85
- this.isPushingSignal = createSignal(this.options.reactivity, false);
86
- this.on("persistence.pullStarted", () => {
87
- this.isPullingSignal.set(true);
88
- });
89
- this.on("persistence.pullCompleted", () => {
90
- this.isPullingSignal.set(false);
91
- });
92
- this.on("persistence.pushStarted", () => {
93
- this.isPushingSignal.set(true);
94
- });
95
- this.on("persistence.pushCompleted", () => {
96
- this.isPushingSignal.set(false);
97
- });
98
- this.persistenceAdapter = this.options.persistence ?? null;
99
- if (this.persistenceAdapter) {
100
- let ongoingSaves = 0;
101
- let isInitialized = false;
102
- const pendingUpdates = { added: [], modified: [], removed: [] };
103
- const loadPersistentData = async (data) => {
104
- if (!this.persistenceAdapter)
105
- throw new Error("Persistence adapter not found");
106
- this.emit("persistence.pullStarted");
107
- const { items, changes } = data ?? await this.persistenceAdapter.load();
108
- if (items) {
109
- if (ongoingSaves > 0)
110
- return;
111
- this.memory().splice(0, this.memoryArray().length, ...items);
112
- this.idIndex.clear();
113
- this.memory().map((item, index) => {
114
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
115
- });
116
- } else if (changes) {
117
- changes.added.forEach((item) => {
118
- const index = this.memory().findIndex((document) => document.id === item.id);
119
- if (index !== -1) {
120
- this.memory().splice(index, 1, item);
121
- return;
122
- }
123
- this.memory().push(item);
124
- const itemIndex = this.memory().findIndex((document) => document === item);
125
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
126
- });
127
- changes.modified.forEach((item) => {
128
- const index = this.memory().findIndex((document) => document.id === item.id);
129
- if (index === -1)
130
- throw new Error("Cannot resolve index for item");
131
- this.memory().splice(index, 1, item);
132
- });
133
- changes.removed.forEach((item) => {
134
- const index = this.memory().findIndex((document) => document.id === item.id);
135
- if (index === -1)
136
- throw new Error("Cannot resolve index for item");
137
- this.memory().splice(index, 1);
138
- });
139
- }
140
- this.rebuildIndices();
141
- this.emit("persistence.received");
142
- setTimeout(() => this.emit("persistence.pullCompleted"), 0);
143
- };
144
- const saveQueue = {
145
- added: [],
146
- modified: [],
147
- removed: []
148
- };
149
- let isFlushing = false;
150
- const flushQueue = () => {
151
- if (!this.persistenceAdapter)
152
- throw new Error("Persistence adapter not found");
153
- if (ongoingSaves <= 0)
154
- this.emit("persistence.pushStarted");
155
- if (isFlushing)
156
- return;
157
- if (!hasPendingUpdates(saveQueue))
158
- return;
159
- isFlushing = true;
160
- ongoingSaves += 1;
161
- const currentItems = this.memoryArray();
162
- const changes = { ...saveQueue };
163
- saveQueue.added = [];
164
- saveQueue.modified = [];
165
- saveQueue.removed = [];
166
- this.persistenceAdapter.save(currentItems, changes).then(() => {
167
- this.emit("persistence.transmitted");
168
- }).catch((error) => {
169
- this.emit("persistence.error", error instanceof Error ? error : new Error(error));
170
- }).finally(() => {
171
- ongoingSaves -= 1;
172
- isFlushing = false;
173
- flushQueue();
174
- if (ongoingSaves <= 0)
175
- this.emit("persistence.pushCompleted");
176
- });
177
- };
178
- this.on("added", (item) => {
179
- if (!isInitialized) {
180
- pendingUpdates.added.push(item);
181
- return;
182
- }
183
- saveQueue.added.push(item);
184
- flushQueue();
185
- });
186
- this.on("changed", (item) => {
187
- if (!isInitialized) {
188
- pendingUpdates.modified.push(item);
189
- return;
190
- }
191
- saveQueue.modified.push(item);
192
- flushQueue();
193
- });
194
- this.on("removed", (item) => {
195
- if (!isInitialized) {
196
- pendingUpdates.removed.push(item);
197
- return;
198
- }
199
- saveQueue.removed.push(item);
200
- flushQueue();
201
- });
202
- this.persistenceAdapter.register((data) => loadPersistentData(data)).then(async () => {
203
- if (!this.persistenceAdapter)
204
- throw new Error("Persistence adapter not found");
205
- let currentItems = this.memoryArray();
206
- await loadPersistentData();
207
- while (hasPendingUpdates(pendingUpdates)) {
208
- const added = pendingUpdates.added.splice(0);
209
- const modified = pendingUpdates.modified.splice(0);
210
- const removed = pendingUpdates.removed.splice(0);
211
- currentItems = applyUpdates(this.memoryArray(), { added, modified, removed });
212
- await this.persistenceAdapter.save(currentItems, { added, modified, removed }).then(() => {
213
- this.emit("persistence.transmitted");
214
- });
215
- }
216
- await loadPersistentData();
217
- isInitialized = true;
218
- setTimeout(() => this.emit("persistence.init"), 0);
219
- }).catch((error) => {
220
- this.emit("persistence.error", error instanceof Error ? error : new Error(error));
221
- });
222
- }
223
- this.persistenceReadyPromise = new Promise((resolve, reject) => {
224
- if (!this.persistenceAdapter)
225
- return resolve();
226
- this.once("persistence.init", resolve);
227
- this.once("persistence.error", reject);
228
- });
229
- _Collection.onCreationCallbacks.forEach((callback) => callback(this));
230
- }
8
+ class Collection extends EventEmitter {
9
+ static collections = [];
10
+ static debugMode = false;
11
+ static batchOperationInProgress = false;
12
+ static fieldTracking = false;
13
+ static onCreationCallbacks = [];
14
+ static onDisposeCallbacks = [];
231
15
  static getCollections() {
232
- return _Collection.collections;
16
+ return Collection.collections;
233
17
  }
234
18
  static onCreation(callback) {
235
- _Collection.onCreationCallbacks.push(callback);
19
+ Collection.onCreationCallbacks.push(callback);
236
20
  }
237
21
  static onDispose(callback) {
238
- _Collection.onDisposeCallbacks.push(callback);
22
+ Collection.onDisposeCallbacks.push(callback);
239
23
  }
240
24
  /**
241
- * Executes a batch operation, allowing multiple modifications to the collection
242
- * while deferring index rebuilding until all operations in the batch are completed.
243
- * This improves performance by avoiding repetitive index recalculations and
244
- * provides atomicity for the batch of operations.
245
- * @param callback - The batch operation to execute.
25
+ * Enables debug mode for all collections.
246
26
  */
27
+ static enableDebugMode = () => {
28
+ Collection.debugMode = true;
29
+ Collection.collections.forEach((collection) => {
30
+ collection.setDebugMode(true);
31
+ });
32
+ };
33
+ /**
34
+ * Enables field tracking for all collections.
35
+ * @param enable - A boolean indicating whether to enable field tracking.
36
+ */
37
+ static setFieldTracking = (enable) => {
38
+ Collection.fieldTracking = enable;
39
+ Collection.collections.forEach((collection) => {
40
+ collection.setFieldTracking(enable);
41
+ });
42
+ };
247
43
  static batch(callback) {
248
- _Collection.batchOperationInProgress = true;
249
- _Collection.collections.reduce((memo, collection) => () => collection.batch(() => memo()), callback)();
250
- _Collection.batchOperationInProgress = false;
44
+ Collection.batchOperationInProgress = true;
45
+ const execute = () => Collection.collections.reduce((memo, collection) => () => {
46
+ return collection.batch(memo);
47
+ }, callback)();
48
+ const maybePromise = execute();
49
+ const afterBatch = () => {
50
+ Collection.batchOperationInProgress = false;
51
+ };
52
+ if (maybePromise && typeof maybePromise.then === "function") {
53
+ return maybePromise.then(() => afterBatch());
54
+ } else {
55
+ afterBatch();
56
+ }
57
+ }
58
+ name;
59
+ backend;
60
+ options;
61
+ isPullingSignal;
62
+ isPushingSignal;
63
+ readySignal;
64
+ debugMode;
65
+ batchOperationInProgress = false;
66
+ isDisposed = false;
67
+ postBatchCallbacks = /* @__PURE__ */ new Set();
68
+ fieldTracking = false;
69
+ constructor(nameOrOptions, maybeDataAdapter, maybeOptions) {
70
+ super();
71
+ const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions?.name || `${this.constructor.name}-${randomId()}`;
72
+ const options = typeof nameOrOptions === "string" ? maybeOptions || {} : nameOrOptions || {};
73
+ const persistence = options.persistence;
74
+ const dataAdapter = maybeDataAdapter || new DefaultDataAdapter({
75
+ ...persistence ? { storage: () => persistence } : {}
76
+ });
77
+ Collection.collections.push(this);
78
+ this.name = name;
79
+ this.options = { ...options };
80
+ this.fieldTracking = this.options.fieldTracking ?? Collection.fieldTracking;
81
+ this.debugMode = this.options.enableDebugMode ?? Collection.debugMode;
82
+ this.isPullingSignal = createSignal(this.options.reactivity, false);
83
+ this.isPushingSignal = createSignal(this.options.reactivity, false);
84
+ this.readySignal = createSignal(this.options.reactivity, false);
85
+ this.backend = dataAdapter.createCollectionBackend(this, this.options.indices ?? []);
86
+ void this.backend.isReady().then(() => {
87
+ this.readySignal.set(true);
88
+ }).catch(() => {
89
+ });
90
+ Collection.onCreationCallbacks.forEach((callback) => callback(this));
91
+ }
92
+ isBatchOperationInProgress() {
93
+ return Collection.batchOperationInProgress || this.batchOperationInProgress;
251
94
  }
252
95
  /**
253
96
  * Checks whether the collection is currently performing a pull operation
@@ -313,17 +156,28 @@ const _Collection = class _Collection extends EventEmitter {
313
156
  *
314
157
  * collection.insert({ name: 'Item 1' })
315
158
  */
316
- async isReady() {
317
- return this.persistenceReadyPromise;
159
+ async ready() {
160
+ return this.backend.isReady();
161
+ }
162
+ /**
163
+ * Checks if the collection is ready.
164
+ * ⚡️ this function is reactive!
165
+ * @returns A boolean indicating whether the collection is ready.
166
+ */
167
+ isReady() {
168
+ return this.readySignal.get() ?? false;
318
169
  }
319
170
  profile(fn, measureFunction) {
320
171
  if (!this.debugMode)
321
172
  return fn();
322
173
  const startTime = performance.now();
323
- const result = fn();
324
- const endTime = performance.now();
325
- measureFunction(endTime - startTime);
326
- return result;
174
+ const handleProfileEnd = (result) => {
175
+ const endTime = performance.now();
176
+ measureFunction(endTime - startTime);
177
+ return result;
178
+ };
179
+ const maybePromise = fn();
180
+ return maybePromise instanceof Promise ? maybePromise.then(handleProfileEnd) : handleProfileEnd(maybePromise);
327
181
  }
328
182
  executeInDebugMode(fn) {
329
183
  if (!this.debugMode)
@@ -331,119 +185,62 @@ const _Collection = class _Collection extends EventEmitter {
331
185
  const callstack = new Error().stack || "";
332
186
  fn(callstack);
333
187
  }
334
- rebuildIndices() {
335
- this.indicesOutdated = true;
336
- if (this.batchOperationInProgress)
337
- return;
338
- this.rebuildAllIndices();
339
- }
340
- rebuildAllIndices() {
341
- this.idIndex.clear();
342
- this.memory().map((item, index) => {
343
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
344
- });
345
- this.indexProviders.forEach((index) => index.rebuild(this.memoryArray()));
346
- this.indicesOutdated = false;
347
- }
348
- getIndexInfo(selector) {
349
- if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
350
- return {
351
- matched: true,
352
- positions: [...this.idIndex.get(serializeValue(selector.id)) || []],
353
- optimizedSelector: {}
354
- };
355
- }
356
- if (selector == null) {
357
- return {
358
- matched: false,
359
- positions: [],
360
- optimizedSelector: {}
361
- };
362
- }
363
- if (this.indicesOutdated) {
364
- return {
365
- matched: false,
366
- positions: [],
367
- optimizedSelector: selector
368
- };
369
- }
370
- return getIndexInfo(this.indexProviders, selector);
371
- }
372
- getItemAndIndex(selector) {
373
- const memory = this.memoryArray();
374
- const indexInfo = this.getIndexInfo(selector);
375
- const items = indexInfo.matched ? indexInfo.positions.map((index2) => memory[index2]) : memory;
376
- const item = items.find((document) => match(document, selector));
377
- const foundInIndex = indexInfo.matched && indexInfo.positions.find((itemIndex) => memory[itemIndex] === item);
378
- const index = foundInIndex || memory.findIndex((document) => document === item);
379
- if (item == null)
380
- return { item: null, index: -1 };
381
- if (index === -1)
382
- throw new Error("Cannot resolve index for item");
383
- return { item, index };
384
- }
385
- deleteFromIdIndex(id, index) {
386
- this.idIndex.delete(serializeValue(id));
387
- if (!this.batchOperationInProgress)
388
- return;
389
- this.idIndex.forEach(([currenIndex], key) => {
390
- if (currenIndex > index) {
391
- this.idIndex.set(key, /* @__PURE__ */ new Set([currenIndex - 1]));
392
- }
393
- });
394
- }
395
- memory() {
396
- return this.options.memory;
397
- }
398
- memoryArray() {
399
- return this.memory().map((item) => item);
400
- }
401
188
  transform(item) {
402
189
  if (!this.options.transform)
403
190
  return item;
404
191
  return this.options.transform(item);
405
192
  }
406
- getItems(selector) {
193
+ getItem(selector, options) {
194
+ const itemsOrPromise = this.getItems(selector, { ...options, limit: 1 });
195
+ if (itemsOrPromise instanceof Promise) {
196
+ return itemsOrPromise.then((items) => {
197
+ return items[0] || void 0;
198
+ });
199
+ }
200
+ return itemsOrPromise[0];
201
+ }
202
+ getItems(selector, options) {
203
+ this.emit("getItems", selector);
407
204
  return this.profile(() => {
408
- const indexInfo = this.getIndexInfo(selector);
409
- const matchItems = (item) => {
410
- if (indexInfo.optimizedSelector == null)
411
- return true;
412
- if (Object.keys(indexInfo.optimizedSelector).length <= 0)
413
- return true;
414
- const matches = match(item, indexInfo.optimizedSelector);
415
- return matches;
416
- };
417
- this.emit("getItems", selector);
418
- const memory = this.memoryArray();
419
- if (!indexInfo.matched) {
420
- if (isEqual(selector, {}))
421
- return memory;
422
- return memory.filter(matchItems);
423
- }
424
- const items = indexInfo.positions.map((index) => memory[index]);
425
- if (isEqual(indexInfo.optimizedSelector, {}))
426
- return items;
427
- return items.filter(matchItems);
205
+ if (!options?.async)
206
+ return this.backend.getQueryResult(selector, options);
207
+ return new Promise((resolve, reject) => {
208
+ this.isPullingSignal.set(true);
209
+ const cleanup = this.backend.onQueryStateChange(selector, options, (state) => {
210
+ if (state === "error") {
211
+ cleanup();
212
+ reject(this.backend.getQueryError(selector, options) || new Error("Unknown error"));
213
+ } else if (state === "complete") {
214
+ cleanup();
215
+ resolve(this.backend.getQueryResult(selector, options) || []);
216
+ }
217
+ });
218
+ this.backend.registerQuery(selector, options);
219
+ }).finally(() => {
220
+ this.isPullingSignal.set(false);
221
+ this.backend.unregisterQuery(selector, options);
222
+ });
428
223
  }, (measuredTime) => this.executeInDebugMode((callstack) => this.emit("_debug.getItems", callstack, selector, measuredTime)));
429
224
  }
225
+ async withPushState(asyncFunction) {
226
+ this.isPushingSignal.set(true);
227
+ try {
228
+ return await asyncFunction();
229
+ } finally {
230
+ this.isPushingSignal.set(false);
231
+ }
232
+ }
430
233
  /**
431
234
  * Disposes the collection, unregisters persistence adapters, clears memory, and
432
235
  * cleans up all resources used by the collection.
433
236
  * @returns A promise that resolves when the collection is disposed.
434
237
  */
435
238
  async dispose() {
436
- var _a;
437
- if ((_a = this.persistenceAdapter) == null ? void 0 : _a.unregister)
438
- await this.persistenceAdapter.unregister();
439
- this.persistenceAdapter = null;
440
- this.memory().map(() => this.memory().pop());
441
- this.idIndex.clear();
442
- this.indexProviders = [];
239
+ await this.backend.dispose();
443
240
  this.isDisposed = true;
444
241
  this.removeAllListeners();
445
- _Collection.collections = _Collection.collections.filter((collection) => collection !== this);
446
- _Collection.onDisposeCallbacks.forEach((callback) => callback(this));
242
+ Collection.collections = Collection.collections.filter((collection) => collection !== this);
243
+ Collection.onDisposeCallbacks.forEach((callback) => callback(this));
447
244
  }
448
245
  /**
449
246
  * Finds multiple items in the collection based on a selector and optional options.
@@ -453,12 +250,12 @@ const _Collection = class _Collection extends EventEmitter {
453
250
  * @param [options] - Options for the find operation, such as limit and sort.
454
251
  * @returns A cursor to fetch and observe the matching items.
455
252
  */
456
- find(selector, options) {
253
+ find(selector = {}, options) {
457
254
  if (this.isDisposed)
458
255
  throw new Error("Collection is disposed");
459
256
  if (selector !== void 0 && (!selector || typeof selector !== "object"))
460
257
  throw new Error("Invalid selector");
461
- const cursor = new Cursor(() => this.getItems(selector), {
258
+ const cursor = new Cursor((() => this.getItems(selector, options || {})), {
462
259
  reactive: this.options.reactivity,
463
260
  fieldTracking: this.fieldTracking,
464
261
  ...options,
@@ -471,16 +268,16 @@ const _Collection = class _Collection extends EventEmitter {
471
268
  }
472
269
  requery();
473
270
  };
474
- this.addListener("persistence.received", handleRequery);
475
- this.addListener("added", handleRequery);
476
- this.addListener("changed", handleRequery);
477
- this.addListener("removed", handleRequery);
271
+ this.backend.registerQuery(selector, options);
272
+ const queryStateChangeCleanup = this.backend.onQueryStateChange(selector, options, (state) => {
273
+ if (state !== "complete")
274
+ return;
275
+ handleRequery();
276
+ });
478
277
  this.emit("observer.created", selector, options);
479
278
  return () => {
480
- this.removeListener("persistence.received", handleRequery);
481
- this.removeListener("added", handleRequery);
482
- this.removeListener("changed", handleRequery);
483
- this.removeListener("removed", handleRequery);
279
+ this.backend.unregisterQuery(selector, options);
280
+ queryStateChangeCleanup();
484
281
  this.emit("observer.disposed", selector, options);
485
282
  };
486
283
  }
@@ -493,6 +290,7 @@ const _Collection = class _Collection extends EventEmitter {
493
290
  * Finds a single item in the collection based on a selector and optional options.
494
291
  * ⚡️ this function is reactive!
495
292
  * Returns the found item or undefined if no item matches.
293
+ * @template Async - Whether to perform the operation asynchronously.
496
294
  * @template O - The options type for the find operation.
497
295
  * @param selector - The criteria to select the item.
498
296
  * @param [options] - Options for the find operation, such as projection.
@@ -505,23 +303,39 @@ const _Collection = class _Collection extends EventEmitter {
505
303
  limit: 1,
506
304
  ...options
507
305
  });
508
- const returnValue = cursor.fetch()[0] || void 0;
509
- this.emit("findOne", selector, options, returnValue);
510
- this.executeInDebugMode((callstack) => this.emit("_debug.findOne", callstack, selector, options, returnValue));
511
- return returnValue;
306
+ const handleItems = (items) => {
307
+ const returnValue = items[0] || void 0;
308
+ this.emit("findOne", selector, options, returnValue);
309
+ this.executeInDebugMode((callstack) => this.emit("_debug.findOne", callstack, selector, options, returnValue));
310
+ return returnValue;
311
+ };
312
+ const maybePromise = cursor.fetch();
313
+ return maybePromise instanceof Promise ? maybePromise.then(handleItems) : handleItems(maybePromise);
512
314
  }
513
- /**
514
- * Performs a batch operation, deferring index rebuilds and allowing multiple
515
- * modifications to be made atomically. Executes any post-batch callbacks afterwards.
516
- * @param callback - The batch operation to execute.
517
- */
518
315
  batch(callback) {
316
+ if (this.batchOperationInProgress)
317
+ return callback();
519
318
  this.batchOperationInProgress = true;
520
- callback();
521
- this.batchOperationInProgress = false;
522
- this.rebuildAllIndices();
523
- this.postBatchCallbacks.forEach((callback_) => callback_());
524
- this.postBatchCallbacks.clear();
319
+ const maybePromise = callback();
320
+ const afterBatch = () => {
321
+ this.batchOperationInProgress = false;
322
+ this.postBatchCallbacks.forEach((callback_) => callback_());
323
+ this.postBatchCallbacks.clear();
324
+ };
325
+ if (maybePromise && typeof maybePromise.then === "function") {
326
+ return maybePromise.then(() => afterBatch());
327
+ } else {
328
+ afterBatch();
329
+ }
330
+ }
331
+ onPostBatch(callback) {
332
+ if (this.isDisposed)
333
+ throw new Error("Collection is disposed");
334
+ if (this.batchOperationInProgress) {
335
+ this.postBatchCallbacks.add(callback);
336
+ return;
337
+ }
338
+ return callback();
525
339
  }
526
340
  /**
527
341
  * Inserts a single item into the collection. Generates a unique ID if not provided.
@@ -529,20 +343,18 @@ const _Collection = class _Collection extends EventEmitter {
529
343
  * @returns The ID of the inserted item.
530
344
  * @throws {Error} If the collection is disposed or the item has an invalid ID.
531
345
  */
532
- insert(item) {
346
+ async insert(item) {
533
347
  if (this.isDisposed)
534
348
  throw new Error("Collection is disposed");
535
349
  if (!item)
536
350
  throw new Error("Invalid item");
537
351
  const primaryKeyGenerator = this.options.primaryKeyGenerator ?? randomId;
538
- const newItem = { id: primaryKeyGenerator(item), ...item };
539
- this.emit("validate", newItem);
540
- if (this.idIndex.has(serializeValue(newItem.id)))
541
- throw new Error("Item with same id already exists");
542
- this.memory().push(newItem);
543
- const itemIndex = this.memory().findIndex((document) => document === newItem);
544
- this.idIndex.set(serializeValue(newItem.id), /* @__PURE__ */ new Set([itemIndex]));
545
- this.rebuildIndices();
352
+ const itemWithId = {
353
+ id: primaryKeyGenerator(item),
354
+ ...item
355
+ };
356
+ this.emit("validate", itemWithId);
357
+ const newItem = await this.withPushState(() => this.backend.insert(itemWithId));
546
358
  this.emit("added", newItem);
547
359
  this.emit("insert", newItem);
548
360
  this.executeInDebugMode((callstack) => this.emit("_debug.insert", callstack, newItem));
@@ -554,7 +366,7 @@ const _Collection = class _Collection extends EventEmitter {
554
366
  * @returns An array of IDs of the inserted items.
555
367
  * @throws {Error} If the collection is disposed or the items are invalid.
556
368
  */
557
- insertMany(items) {
369
+ async insertMany(items) {
558
370
  if (this.isDisposed)
559
371
  throw new Error("Collection is disposed");
560
372
  if (!items)
@@ -563,10 +375,10 @@ const _Collection = class _Collection extends EventEmitter {
563
375
  return [];
564
376
  }
565
377
  const ids = [];
566
- this.batch(() => {
567
- items.forEach((item) => {
568
- ids.push(this.insert(item));
569
- });
378
+ await this.batch(async () => {
379
+ await Promise.all(items.map(async (item) => {
380
+ ids.push(await this.insert(item));
381
+ }));
570
382
  });
571
383
  return ids;
572
384
  }
@@ -579,7 +391,7 @@ const _Collection = class _Collection extends EventEmitter {
579
391
  * @returns The number of items updated (0 or 1).
580
392
  * @throws {Error} If the collection is disposed or invalid arguments are provided.
581
393
  */
582
- updateOne(selector, modifier, options) {
394
+ async updateOne(selector, modifier, options) {
583
395
  if (this.isDisposed)
584
396
  throw new Error("Collection is disposed");
585
397
  if (!selector)
@@ -587,9 +399,9 @@ const _Collection = class _Collection extends EventEmitter {
587
399
  if (!modifier)
588
400
  throw new Error("Invalid modifier");
589
401
  const { $setOnInsert, ...restModifier } = modifier;
590
- const { item, index } = this.getItemAndIndex(selector);
402
+ const item = await this.getItem(selector, { async: true });
591
403
  if (item == null) {
592
- if (options == null ? void 0 : options.upsert) {
404
+ if (options?.upsert) {
593
405
  const newItem = modify({}, {
594
406
  ...restModifier,
595
407
  $set: {
@@ -597,26 +409,18 @@ const _Collection = class _Collection extends EventEmitter {
597
409
  ...restModifier.$set
598
410
  }
599
411
  });
600
- if (newItem.id != null && this.getItemAndIndex({ id: newItem.id }).item != null) {
601
- throw new Error("Item with same id already exists");
602
- }
603
- this.insert(newItem);
604
- }
605
- } else {
606
- const modifiedItem = modify(deepClone(item), restModifier);
607
- if (item.id !== modifiedItem.id && this.getItemAndIndex({ id: modifiedItem.id }).item != null) {
608
- throw new Error("Item with same id already exists");
412
+ await this.insert(newItem);
413
+ return 1;
609
414
  }
610
- this.emit("validate", modifiedItem);
611
- this.memory().splice(index, 1, modifiedItem);
612
- this.rebuildIndices();
613
- this.emit("changed", modifiedItem, restModifier);
415
+ return 0;
614
416
  }
417
+ const modifiedItem = modify(deepClone(item), restModifier);
418
+ this.emit("validate", modifiedItem);
419
+ const changes = await this.withPushState(() => this.backend.updateOne(selector, modifier));
420
+ this.emit("changed", modifiedItem, restModifier);
615
421
  this.emit("updateOne", selector, modifier);
616
422
  this.executeInDebugMode((callstack) => this.emit("_debug.updateOne", callstack, selector, modifier));
617
- if (item == null && !(options == null ? void 0 : options.upsert))
618
- return 0;
619
- return 1;
423
+ return changes.length;
620
424
  }
621
425
  /**
622
426
  * Updates multiple items in the collection that match the given selector.
@@ -627,7 +431,7 @@ const _Collection = class _Collection extends EventEmitter {
627
431
  * @returns The number of items updated.
628
432
  * @throws {Error} If the collection is disposed or invalid arguments are provided.
629
433
  */
630
- updateMany(selector, modifier, options) {
434
+ async updateMany(selector, modifier, options) {
631
435
  if (this.isDisposed)
632
436
  throw new Error("Collection is disposed");
633
437
  if (!selector)
@@ -635,44 +439,32 @@ const _Collection = class _Collection extends EventEmitter {
635
439
  if (!modifier)
636
440
  throw new Error("Invalid modifier");
637
441
  const { $setOnInsert, ...restModifier } = modifier;
638
- const items = this.getItems(selector);
639
- if (items.length === 0 && (options == null ? void 0 : options.upsert)) {
640
- const newItem = modify({}, {
641
- ...restModifier,
642
- $set: {
643
- ...$setOnInsert,
644
- ...restModifier.$set
645
- }
646
- });
647
- if (newItem.id != null && this.getItemAndIndex({ id: newItem.id }).item != null) {
648
- throw new Error("Item with same id already exists");
442
+ const items = await this.getItems(selector, { async: true });
443
+ if (items.length === 0) {
444
+ if (options?.upsert) {
445
+ const newItem = modify({}, {
446
+ ...restModifier,
447
+ $set: {
448
+ ...$setOnInsert,
449
+ ...restModifier.$set
450
+ }
451
+ });
452
+ await this.insert(newItem);
453
+ return 1;
649
454
  }
650
- this.insert(newItem);
455
+ return 0;
651
456
  }
652
- const changes = items.map((item) => {
653
- const { index } = this.getItemAndIndex({ id: item.id });
654
- if (index === -1)
655
- throw new Error(`Cannot resolve index for item with id '${item.id}'`);
457
+ items.forEach((item) => {
656
458
  const modifiedItem = modify(deepClone(item), restModifier);
657
- if (item.id !== modifiedItem.id && this.getItemAndIndex({ id: modifiedItem.id }).item != null) {
658
- throw new Error(`Item with same id '${modifiedItem.id}' already exists`);
659
- }
660
459
  this.emit("validate", modifiedItem);
661
- return {
662
- item: modifiedItem,
663
- index
664
- };
665
- });
666
- changes.forEach(({ item, index }) => {
667
- this.memory().splice(index, 1, item);
668
460
  });
669
- this.rebuildIndices();
670
- changes.forEach(({ item }) => {
461
+ const changes = await this.withPushState(() => this.backend.updateMany(selector, modifier));
462
+ changes.forEach((item) => {
671
463
  this.emit("changed", item, restModifier);
672
464
  });
673
465
  this.emit("updateMany", selector, modifier);
674
466
  this.executeInDebugMode((callstack) => this.emit("_debug.updateMany", callstack, selector, modifier));
675
- return changes.length === 0 && (options == null ? void 0 : options.upsert) ? 1 : changes.length;
467
+ return changes.length;
676
468
  }
677
469
  /**
678
470
  * Replaces a single item in the collection that matches the given selector.
@@ -683,34 +475,26 @@ const _Collection = class _Collection extends EventEmitter {
683
475
  * @returns The number of items replaced (0 or 1).
684
476
  * @throws {Error} If the collection is disposed or invalid arguments are provided.
685
477
  */
686
- replaceOne(selector, replacement, options) {
478
+ async replaceOne(selector, replacement, options) {
687
479
  if (this.isDisposed)
688
480
  throw new Error("Collection is disposed");
689
481
  if (!selector)
690
482
  throw new Error("Invalid selector");
691
- const { item, index } = this.getItemAndIndex(selector);
483
+ const item = await this.getItem(selector, { async: true });
692
484
  if (item == null) {
693
- if (options == null ? void 0 : options.upsert) {
694
- if (replacement.id != null && this.getItemAndIndex({ id: replacement.id }).item != null) {
695
- throw new Error("Item with same id already exists");
696
- }
697
- this.insert(replacement);
698
- }
699
- } else {
700
- if (item.id !== replacement.id && this.getItemAndIndex({ id: replacement.id }).item != null) {
701
- throw new Error("Item with same id already exists");
485
+ if (options?.upsert) {
486
+ await this.insert(replacement);
487
+ return 1;
702
488
  }
703
- const modifiedItem = { id: item.id, ...replacement };
704
- this.emit("validate", modifiedItem);
705
- this.memory().splice(index, 1, modifiedItem);
706
- this.rebuildIndices();
707
- this.emit("changed", modifiedItem, replacement);
489
+ return 0;
708
490
  }
491
+ const modifiedItem = { id: item.id, ...replacement };
492
+ this.emit("validate", modifiedItem);
493
+ const changes = await this.withPushState(() => this.backend.replaceOne(selector, replacement));
494
+ this.emit("changed", modifiedItem, replacement);
709
495
  this.emit("replaceOne", selector, replacement);
710
496
  this.executeInDebugMode((callstack) => this.emit("_debug.replaceOne", callstack, selector, replacement));
711
- if (item == null && !(options == null ? void 0 : options.upsert))
712
- return 0;
713
- return 1;
497
+ return changes.length;
714
498
  }
715
499
  /**
716
500
  * Removes a single item from the collection that matches the given selector.
@@ -718,21 +502,16 @@ const _Collection = class _Collection extends EventEmitter {
718
502
  * @returns The number of items removed (0 or 1).
719
503
  * @throws {Error} If the collection is disposed or invalid arguments are provided.
720
504
  */
721
- removeOne(selector) {
505
+ async removeOne(selector) {
722
506
  if (this.isDisposed)
723
507
  throw new Error("Collection is disposed");
724
508
  if (!selector)
725
509
  throw new Error("Invalid selector");
726
- const { item, index } = this.getItemAndIndex(selector);
727
- if (item != null) {
728
- this.memory().splice(index, 1);
729
- this.deleteFromIdIndex(item.id, index);
730
- this.rebuildIndices();
731
- this.emit("removed", item);
732
- }
510
+ const removedItems = await this.withPushState(() => this.backend.removeOne(selector));
511
+ this.emit("removed", removedItems[0]);
733
512
  this.emit("removeOne", selector);
734
513
  this.executeInDebugMode((callstack) => this.emit("_debug.removeOne", callstack, selector));
735
- return item == null ? 0 : 1;
514
+ return removedItems.length;
736
515
  }
737
516
  /**
738
517
  * Removes multiple items from the collection that match the given selector.
@@ -740,55 +519,20 @@ const _Collection = class _Collection extends EventEmitter {
740
519
  * @returns The number of items removed.
741
520
  * @throws {Error} If the collection is disposed or invalid arguments are provided.
742
521
  */
743
- removeMany(selector) {
522
+ async removeMany(selector) {
744
523
  if (this.isDisposed)
745
524
  throw new Error("Collection is disposed");
746
525
  if (!selector)
747
526
  throw new Error("Invalid selector");
748
- const items = this.getItems(selector);
749
- items.forEach((item) => {
750
- const index = this.memory().findIndex((document) => document === item);
751
- if (index === -1)
752
- throw new Error("Cannot resolve index for item");
753
- this.memory().splice(index, 1);
754
- this.deleteFromIdIndex(item.id, index);
755
- this.rebuildIndices();
756
- });
757
- items.forEach((item) => {
527
+ const removedItems = await this.withPushState(() => this.backend.removeMany(selector));
528
+ removedItems.forEach((item) => {
758
529
  this.emit("removed", item);
759
530
  });
760
531
  this.emit("removeMany", selector);
761
532
  this.executeInDebugMode((callstack) => this.emit("_debug.removeMany", callstack, selector));
762
- return items.length;
533
+ return removedItems.length;
763
534
  }
764
- };
765
- __publicField(_Collection, "collections", []);
766
- __publicField(_Collection, "debugMode", false);
767
- __publicField(_Collection, "batchOperationInProgress", false);
768
- __publicField(_Collection, "fieldTracking", false);
769
- __publicField(_Collection, "onCreationCallbacks", []);
770
- __publicField(_Collection, "onDisposeCallbacks", []);
771
- /**
772
- * Enables debug mode for all collections.
773
- */
774
- __publicField(_Collection, "enableDebugMode", () => {
775
- _Collection.debugMode = true;
776
- _Collection.collections.forEach((collection) => {
777
- collection.setDebugMode(true);
778
- });
779
- });
780
- /**
781
- * Enables field tracking for all collections.
782
- * @param enable - A boolean indicating whether to enable field tracking.
783
- */
784
- __publicField(_Collection, "setFieldTracking", (enable) => {
785
- _Collection.fieldTracking = enable;
786
- _Collection.collections.forEach((collection) => {
787
- collection.setFieldTracking(enable);
788
- });
789
- });
790
- let Collection = _Collection;
535
+ }
791
536
  export {
792
- default2 as createIndex,
793
537
  Collection as default
794
538
  };