@signaldb/core 1.7.2 → 1.8.1

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