@signaldb/core 1.7.2 → 1.8.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.
@@ -2,7 +2,8 @@
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
3
  const sortItems = require("./index.cjs15.js");
4
4
  const project = require("./index.cjs16.js");
5
- const Observer = require("./index.cjs17.js");
5
+ const deepClone = require("./index.cjs17.js");
6
+ const Observer = require("./index.cjs18.js");
6
7
  function isInReactiveScope(reactivity) {
7
8
  if (!reactivity)
8
9
  return false;
@@ -30,6 +31,7 @@ class Cursor {
30
31
  * @param options.limit - The maximum number of items to return in the result set.
31
32
  * @param options.reactive - A reactivity adapter to enable observing changes in the cursor's result set.
32
33
  * @param options.fieldTracking - A boolean to enable fine-grained field tracking for reactivity.
34
+ * @param options.transformAll - A function that will be able to solve the n+1 problem
33
35
  */
34
36
  constructor(getItems, options) {
35
37
  this.getFilteredItems = getItems;
@@ -65,12 +67,16 @@ class Cursor {
65
67
  }
66
68
  getItems() {
67
69
  const items = this.getFilteredItems();
68
- const { sort, skip, limit } = this.options;
70
+ const { sort, skip, limit, transformAll, fields } = this.options;
69
71
  const sorted = sort ? sortItems(items, sort) : items;
70
72
  const skipped = skip ? sorted.slice(skip) : sorted;
71
73
  const limited = limit ? skipped.slice(0, limit) : skipped;
72
74
  const idExcluded = this.options.fields && this.options.fields.id === 0;
73
- return limited.map((item) => {
75
+ let entries = limited;
76
+ if (transformAll) {
77
+ entries = transformAll(deepClone.default(limited), fields);
78
+ }
79
+ return entries.map((item) => {
74
80
  if (!this.options.fields)
75
81
  return item;
76
82
  return {
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
3
  const EventEmitter = require("./index.cjs13.js");
4
- const match = require("./index.cjs18.js");
4
+ const match = require("./index.cjs19.js");
5
5
  const modify = require("./index.cjs11.js");
6
6
  const isEqual = require("./index.cjs10.js");
7
7
  const randomId = require("./index.cjs12.js");
8
- const deepClone = require("./index.cjs19.js");
8
+ const deepClone = require("./index.cjs17.js");
9
9
  const serializeValue = require("./index.cjs20.js");
10
10
  const createSignal = require("./index.cjs21.js");
11
11
  const Cursor = require("./index.cjs2.js");
@@ -94,6 +94,7 @@ class Collection extends EventEmitter {
94
94
  postBatchCallbacks = /* @__PURE__ */ new Set();
95
95
  fieldTracking = false;
96
96
  persistenceReadyPromise;
97
+ pendingUpdates = { added: [], modified: [], removed: [] };
97
98
  /**
98
99
  * Initializes a new instance of the `Collection` class with optional configuration.
99
100
  * Sets up memory, persistence, reactivity, and indices as specified in the options.
@@ -109,6 +110,7 @@ class Collection extends EventEmitter {
109
110
  * @param options.indices - An array of index providers for optimized querying.
110
111
  * @param options.enableDebugMode - A boolean to enable or disable debug mode.
111
112
  * @param options.fieldTracking - A boolean to enable or disable field tracking by default.
113
+ * @param options.transformAll - A function that will be able to solve the n+1 problem
112
114
  */
113
115
  constructor(options) {
114
116
  super();
@@ -143,48 +145,6 @@ class Collection extends EventEmitter {
143
145
  if (this.persistenceAdapter) {
144
146
  let ongoingSaves = 0;
145
147
  let isInitialized = false;
146
- const pendingUpdates = { added: [], modified: [], removed: [] };
147
- const loadPersistentData = async (data) => {
148
- if (!this.persistenceAdapter)
149
- throw new Error("Persistence adapter not found");
150
- this.emit("persistence.pullStarted");
151
- const { items, changes } = data ?? await this.persistenceAdapter.load();
152
- if (items) {
153
- if (ongoingSaves > 0)
154
- return;
155
- this.memory().splice(0, this.memoryArray().length, ...items);
156
- this.idIndex.clear();
157
- this.memory().map((item, index) => {
158
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
159
- });
160
- } else if (changes) {
161
- changes.added.forEach((item) => {
162
- const index = this.memory().findIndex((document) => document.id === item.id);
163
- if (index !== -1) {
164
- this.memory().splice(index, 1, item);
165
- return;
166
- }
167
- this.memory().push(item);
168
- const itemIndex = this.memory().findIndex((document) => document === item);
169
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
170
- });
171
- changes.modified.forEach((item) => {
172
- const index = this.memory().findIndex((document) => document.id === item.id);
173
- if (index === -1)
174
- throw new Error("Cannot resolve index for item");
175
- this.memory().splice(index, 1, item);
176
- });
177
- changes.removed.forEach((item) => {
178
- const index = this.memory().findIndex((document) => document.id === item.id);
179
- if (index === -1)
180
- throw new Error("Cannot resolve index for item");
181
- this.memory().splice(index, 1);
182
- });
183
- }
184
- this.rebuildIndices();
185
- this.emit("persistence.received");
186
- setTimeout(() => this.emit("persistence.pullCompleted"), 0);
187
- };
188
148
  const saveQueue = {
189
149
  added: [],
190
150
  modified: [],
@@ -221,7 +181,7 @@ class Collection extends EventEmitter {
221
181
  };
222
182
  this.on("added", (item) => {
223
183
  if (!isInitialized) {
224
- pendingUpdates.added.push(item);
184
+ this.pendingUpdates.added.push(item);
225
185
  return;
226
186
  }
227
187
  saveQueue.added.push(item);
@@ -229,7 +189,7 @@ class Collection extends EventEmitter {
229
189
  });
230
190
  this.on("changed", (item) => {
231
191
  if (!isInitialized) {
232
- pendingUpdates.modified.push(item);
192
+ this.pendingUpdates.modified.push(item);
233
193
  return;
234
194
  }
235
195
  saveQueue.modified.push(item);
@@ -237,27 +197,27 @@ class Collection extends EventEmitter {
237
197
  });
238
198
  this.on("removed", (item) => {
239
199
  if (!isInitialized) {
240
- pendingUpdates.removed.push(item);
200
+ this.pendingUpdates.removed.push(item);
241
201
  return;
242
202
  }
243
203
  saveQueue.removed.push(item);
244
204
  flushQueue();
245
205
  });
246
- this.persistenceAdapter.register((data) => loadPersistentData(data)).then(async () => {
206
+ this.persistenceAdapter.register((data) => this.loadPersistentData(data, ongoingSaves > 0)).then(async () => {
247
207
  if (!this.persistenceAdapter)
248
208
  throw new Error("Persistence adapter not found");
249
209
  let currentItems = this.memoryArray();
250
- await loadPersistentData();
251
- while (hasPendingUpdates(pendingUpdates)) {
252
- const added = pendingUpdates.added.splice(0);
253
- const modified = pendingUpdates.modified.splice(0);
254
- const removed = pendingUpdates.removed.splice(0);
210
+ await this.loadPersistentData();
211
+ while (hasPendingUpdates(this.pendingUpdates)) {
212
+ const added = this.pendingUpdates.added.splice(0);
213
+ const modified = this.pendingUpdates.modified.splice(0);
214
+ const removed = this.pendingUpdates.removed.splice(0);
255
215
  currentItems = applyUpdates(this.memoryArray(), { added, modified, removed });
256
216
  await this.persistenceAdapter.save(currentItems, { added, modified, removed }).then(() => {
257
217
  this.emit("persistence.transmitted");
258
218
  });
259
219
  }
260
- await loadPersistentData();
220
+ await this.loadPersistentData();
261
221
  isInitialized = true;
262
222
  setTimeout(() => this.emit("persistence.init"), 0);
263
223
  }).catch((error) => {
@@ -272,6 +232,66 @@ class Collection extends EventEmitter {
272
232
  });
273
233
  Collection.onCreationCallbacks.forEach((callback) => callback(this));
274
234
  }
235
+ /**
236
+ * Resets the collection's data by clearing the in-memory items and reloading from the persistence adapter.
237
+ * @returns A promise that resolves when the data has been reset and reloaded.
238
+ */
239
+ async resetData() {
240
+ if (hasPendingUpdates(this.pendingUpdates)) {
241
+ await new Promise((resolve) => {
242
+ this.on("persistence.transmitted", resolve);
243
+ });
244
+ }
245
+ this.options.memory = [];
246
+ await this.loadPersistentData();
247
+ }
248
+ /**
249
+ * Loads data from the persistence adapter and updates the in-memory collection accordingly.
250
+ * @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.
251
+ * @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.
252
+ * @returns A promise that resolves when the data has been loaded and the in-memory collection has been updated.
253
+ */
254
+ async loadPersistentData(data, hasOngoingSaves = false) {
255
+ if (!this.persistenceAdapter)
256
+ throw new Error("Persistence adapter not found");
257
+ this.emit("persistence.pullStarted");
258
+ const { items, changes } = data ?? await this.persistenceAdapter.load();
259
+ if (items) {
260
+ if (hasOngoingSaves)
261
+ return;
262
+ this.memory().splice(0, this.memoryArray().length, ...items);
263
+ this.idIndex.clear();
264
+ this.memory().map((item, index) => {
265
+ this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
266
+ });
267
+ } else if (changes) {
268
+ changes.added.forEach((item) => {
269
+ const index = this.memory().findIndex((document) => document.id === item.id);
270
+ if (index !== -1) {
271
+ this.memory().splice(index, 1, item);
272
+ return;
273
+ }
274
+ this.memory().push(item);
275
+ const itemIndex = this.memory().findIndex((document) => document === item);
276
+ this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
277
+ });
278
+ changes.modified.forEach((item) => {
279
+ const index = this.memory().findIndex((document) => document.id === item.id);
280
+ if (index === -1)
281
+ throw new Error("Cannot resolve index for item");
282
+ this.memory().splice(index, 1, item);
283
+ });
284
+ changes.removed.forEach((item) => {
285
+ const index = this.memory().findIndex((document) => document.id === item.id);
286
+ if (index === -1)
287
+ throw new Error("Cannot resolve index for item");
288
+ this.memory().splice(index, 1);
289
+ });
290
+ }
291
+ this.rebuildIndices();
292
+ this.emit("persistence.received");
293
+ setTimeout(() => this.emit("persistence.pullCompleted"), 0);
294
+ }
275
295
  /**
276
296
  * Checks whether the collection is currently performing a pull operation
277
297
  * ⚡️ this function is reactive!
@@ -426,6 +446,11 @@ class Collection extends EventEmitter {
426
446
  return item;
427
447
  return this.options.transform(item);
428
448
  }
449
+ transformAll(items, fields) {
450
+ if (!this.options.transformAll)
451
+ return items;
452
+ return this.options.transformAll(items, fields);
453
+ }
429
454
  getItems(selector) {
430
455
  return this.profile(() => {
431
456
  const indexInfo = this.getIndexInfo(selector);
@@ -485,6 +510,7 @@ class Collection extends EventEmitter {
485
510
  fieldTracking: this.fieldTracking,
486
511
  ...options,
487
512
  transform: this.transform.bind(this),
513
+ transformAll: this.transformAll.bind(this),
488
514
  bindEvents: (requery) => {
489
515
  const handleRequery = () => {
490
516
  if (this.batchOperationInProgress) {
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { default as MemoryAdapter } from './types/MemoryAdapter';
3
3
  export type { default as PersistenceAdapter, Changeset, LoadResponse, } from './types/PersistenceAdapter';
4
4
  export type { default as Selector } from './types/Selector';
5
5
  export type { default as Modifier } from './types/Modifier';
6
- export type { BaseItem, ObserveCallbacks, CursorOptions, Transform, SortSpecifier, FieldSpecifier, FindOptions, CollectionOptions, } from './Collection';
6
+ export type { BaseItem, ObserveCallbacks, CursorOptions, Transform, TransformAll, SortSpecifier, FieldSpecifier, FindOptions, CollectionOptions, } from './Collection';
7
7
  export { default as Cursor } from './Collection/Cursor';
8
8
  export { default as Collection, createIndex } from './Collection';
9
9
  export { default as AutoFetchCollection } from './AutoFetchCollection';
package/dist/index11.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { update } from "mingo";
2
- import deepClone from "./index19.mjs";
2
+ import deepClone from "./index17.mjs";
3
3
  function modify(item, modifier) {
4
4
  const hasOperators = Object.keys(modifier).some((key) => key.startsWith("$"));
5
5
  if (!hasOperators)
package/dist/index17.mjs CHANGED
@@ -1,143 +1,42 @@
1
- import isEqual from "./index10.mjs";
2
- import uniqueBy from "./index26.mjs";
3
- class Observer {
4
- previousItems = [];
5
- callbacks;
6
- unbindEvents;
7
- /**
8
- * Creates a new instance of the `Observer` class.
9
- * Sets up event bindings and initializes the callbacks for tracking changes in a collection.
10
- * @param bindEvents - A function to bind external events to the observer. Must return a cleanup function to unbind those events.
11
- */
12
- constructor(bindEvents) {
13
- this.callbacks = {
14
- added: [],
15
- addedBefore: [],
16
- changed: [],
17
- changedField: [],
18
- movedBefore: [],
19
- removed: []
20
- };
21
- this.unbindEvents = bindEvents();
22
- }
23
- call(event, ...args) {
24
- this.callbacks[event].forEach(({ callback, options }) => {
25
- if (!options.skipInitial || !options.isInitial) {
26
- callback(...args);
27
- }
28
- });
29
- }
30
- hasCallbacks(events) {
31
- return events.some((event) => this.callbacks[event].length > 0);
32
- }
33
- /**
34
- * Determines if the observer has no active callbacks registered for any events.
35
- * @returns A boolean indicating whether the observer is empty (i.e., no callbacks are registered).
36
- */
37
- isEmpty() {
38
- return !this.hasCallbacks([
39
- "added",
40
- "addedBefore",
41
- "changed",
42
- "changedField",
43
- "movedBefore",
44
- "removed"
45
- ]);
46
- }
47
- /**
48
- * Compares the previous state of items with the new state and triggers the appropriate callbacks
49
- * for events such as added, removed, changed, or moved items.
50
- * @param newItems - The new list of items to compare against the previous state.
51
- */
52
- runChecks(newItems) {
53
- const oldItemsMap = new Map(this.previousItems.map((item, index) => [
54
- item.id,
55
- { item, index, beforeItem: this.previousItems[index + 1] || null }
56
- ]));
57
- const newItemsMap = new Map(newItems.map((item, index) => [
58
- item.id,
59
- { item, index, beforeItem: newItems[index + 1] || null }
60
- ]));
61
- if (this.hasCallbacks(["changed", "changedField", "movedBefore", "removed"])) {
62
- oldItemsMap.forEach(({ item: oldItem, index, beforeItem: oldBeforeItem }) => {
63
- const newItem = newItemsMap.get(oldItem.id);
64
- if (newItem) {
65
- if (this.hasCallbacks(["changed", "changedField"]) && !isEqual(newItem.item, oldItem)) {
66
- this.call("changed", newItem.item);
67
- if (this.hasCallbacks(["changedField"])) {
68
- const keys = uniqueBy([
69
- ...Object.keys(newItem.item),
70
- ...Object.keys(oldItem)
71
- ], (value) => value);
72
- keys.forEach((key) => {
73
- if (isEqual(newItem.item[key], oldItem[key]))
74
- return;
75
- this.call("changedField", newItem.item, key, oldItem[key], newItem.item[key]);
76
- });
77
- }
78
- }
79
- if (newItem.index !== index && newItem.beforeItem?.id !== oldBeforeItem?.id) {
80
- this.call("movedBefore", newItem.item, newItem.beforeItem);
81
- }
82
- } else {
83
- this.call("removed", oldItem);
84
- }
85
- });
86
- }
87
- if (this.hasCallbacks(["added", "addedBefore"])) {
88
- newItems.forEach((newItem, index) => {
89
- const oldItem = oldItemsMap.get(newItem.id);
90
- if (oldItem)
91
- return;
92
- this.call("added", newItem);
93
- this.call("addedBefore", newItem, newItems[index + 1] || null);
94
- });
95
- }
96
- this.previousItems = newItems;
97
- Object.keys(this.callbacks).forEach((key) => {
98
- const event = key;
99
- const callbacks = this.callbacks[event];
100
- this.callbacks[event] = callbacks.map((callback) => ({
101
- ...callback,
102
- options: {
103
- ...callback.options,
104
- isInitial: false
105
- }
106
- }));
107
- });
108
- }
109
- /**
110
- * Stops the observer by unbinding all events and cleaning up resources.
111
- */
112
- stop() {
113
- this.unbindEvents();
114
- }
115
- /**
116
- * Registers callbacks for specific events to observe changes in the collection.
117
- * @param callbacks - An object containing the callbacks for various events (e.g., 'added', 'removed').
118
- * @param skipInitial - A boolean indicating whether to skip invoking the callbacks for the initial state of the collection.
119
- */
120
- addCallbacks(callbacks, skipInitial = false) {
121
- Object.keys(callbacks).forEach((key) => {
122
- const typedKey = key;
123
- this.callbacks[typedKey].push({
124
- callback: callbacks[typedKey],
125
- options: { skipInitial, isInitial: true }
126
- });
1
+ function clone(value) {
2
+ if (typeof value === "function")
3
+ throw new Error("Cloning functions is not supported");
4
+ if (value === null || typeof value !== "object")
5
+ return value;
6
+ if (value instanceof Date)
7
+ return new Date(value);
8
+ if (Array.isArray(value))
9
+ return value.map((item) => clone(item));
10
+ if (value instanceof Map) {
11
+ const result2 = /* @__PURE__ */ new Map();
12
+ value.forEach((currentValue, key) => {
13
+ result2.set(key, clone(currentValue));
127
14
  });
15
+ return result2;
128
16
  }
129
- /**
130
- * Removes the specified callbacks for specific events, unregistering them from the observer.
131
- * @param callbacks - An object containing the callbacks to be removed for various events.
132
- */
133
- removeCallbacks(callbacks) {
134
- Object.keys(callbacks).forEach((key) => {
135
- const typedKey = key;
136
- const index = this.callbacks[typedKey].findIndex(({ callback }) => callback === callbacks[typedKey]);
137
- this.callbacks[typedKey].splice(index, 1);
17
+ if (value instanceof Set) {
18
+ const result2 = /* @__PURE__ */ new Set();
19
+ value.forEach((currentValue) => {
20
+ result2.add(clone(currentValue));
138
21
  });
22
+ return result2;
23
+ }
24
+ if (value instanceof RegExp)
25
+ return new RegExp(value);
26
+ const result = {};
27
+ for (const key in value) {
28
+ if (Object.hasOwnProperty.call(value, key)) {
29
+ result[key] = clone(value[key]);
30
+ }
139
31
  }
32
+ return result;
33
+ }
34
+ function deepClone(object) {
35
+ if (typeof structuredClone === "function")
36
+ return structuredClone(object);
37
+ return clone(object);
140
38
  }
141
39
  export {
142
- Observer as default
40
+ clone,
41
+ deepClone as default
143
42
  };
package/dist/index18.mjs CHANGED
@@ -1,8 +1,143 @@
1
- import { Query } from "mingo";
2
- function match(item, selector) {
3
- const query = new Query(selector);
4
- return query.test(item);
1
+ import isEqual from "./index10.mjs";
2
+ import uniqueBy from "./index26.mjs";
3
+ class Observer {
4
+ previousItems = [];
5
+ callbacks;
6
+ unbindEvents;
7
+ /**
8
+ * Creates a new instance of the `Observer` class.
9
+ * Sets up event bindings and initializes the callbacks for tracking changes in a collection.
10
+ * @param bindEvents - A function to bind external events to the observer. Must return a cleanup function to unbind those events.
11
+ */
12
+ constructor(bindEvents) {
13
+ this.callbacks = {
14
+ added: [],
15
+ addedBefore: [],
16
+ changed: [],
17
+ changedField: [],
18
+ movedBefore: [],
19
+ removed: []
20
+ };
21
+ this.unbindEvents = bindEvents();
22
+ }
23
+ call(event, ...args) {
24
+ this.callbacks[event].forEach(({ callback, options }) => {
25
+ if (!options.skipInitial || !options.isInitial) {
26
+ callback(...args);
27
+ }
28
+ });
29
+ }
30
+ hasCallbacks(events) {
31
+ return events.some((event) => this.callbacks[event].length > 0);
32
+ }
33
+ /**
34
+ * Determines if the observer has no active callbacks registered for any events.
35
+ * @returns A boolean indicating whether the observer is empty (i.e., no callbacks are registered).
36
+ */
37
+ isEmpty() {
38
+ return !this.hasCallbacks([
39
+ "added",
40
+ "addedBefore",
41
+ "changed",
42
+ "changedField",
43
+ "movedBefore",
44
+ "removed"
45
+ ]);
46
+ }
47
+ /**
48
+ * Compares the previous state of items with the new state and triggers the appropriate callbacks
49
+ * for events such as added, removed, changed, or moved items.
50
+ * @param newItems - The new list of items to compare against the previous state.
51
+ */
52
+ runChecks(newItems) {
53
+ const oldItemsMap = new Map(this.previousItems.map((item, index) => [
54
+ item.id,
55
+ { item, index, beforeItem: this.previousItems[index + 1] || null }
56
+ ]));
57
+ const newItemsMap = new Map(newItems.map((item, index) => [
58
+ item.id,
59
+ { item, index, beforeItem: newItems[index + 1] || null }
60
+ ]));
61
+ if (this.hasCallbacks(["changed", "changedField", "movedBefore", "removed"])) {
62
+ oldItemsMap.forEach(({ item: oldItem, index, beforeItem: oldBeforeItem }) => {
63
+ const newItem = newItemsMap.get(oldItem.id);
64
+ if (newItem) {
65
+ if (this.hasCallbacks(["changed", "changedField"]) && !isEqual(newItem.item, oldItem)) {
66
+ this.call("changed", newItem.item);
67
+ if (this.hasCallbacks(["changedField"])) {
68
+ const keys = uniqueBy([
69
+ ...Object.keys(newItem.item),
70
+ ...Object.keys(oldItem)
71
+ ], (value) => value);
72
+ keys.forEach((key) => {
73
+ if (isEqual(newItem.item[key], oldItem[key]))
74
+ return;
75
+ this.call("changedField", newItem.item, key, oldItem[key], newItem.item[key]);
76
+ });
77
+ }
78
+ }
79
+ if (newItem.index !== index && newItem.beforeItem?.id !== oldBeforeItem?.id) {
80
+ this.call("movedBefore", newItem.item, newItem.beforeItem);
81
+ }
82
+ } else {
83
+ this.call("removed", oldItem);
84
+ }
85
+ });
86
+ }
87
+ if (this.hasCallbacks(["added", "addedBefore"])) {
88
+ newItems.forEach((newItem, index) => {
89
+ const oldItem = oldItemsMap.get(newItem.id);
90
+ if (oldItem)
91
+ return;
92
+ this.call("added", newItem);
93
+ this.call("addedBefore", newItem, newItems[index + 1] || null);
94
+ });
95
+ }
96
+ this.previousItems = newItems;
97
+ Object.keys(this.callbacks).forEach((key) => {
98
+ const event = key;
99
+ const callbacks = this.callbacks[event];
100
+ this.callbacks[event] = callbacks.map((callback) => ({
101
+ ...callback,
102
+ options: {
103
+ ...callback.options,
104
+ isInitial: false
105
+ }
106
+ }));
107
+ });
108
+ }
109
+ /**
110
+ * Stops the observer by unbinding all events and cleaning up resources.
111
+ */
112
+ stop() {
113
+ this.unbindEvents();
114
+ }
115
+ /**
116
+ * Registers callbacks for specific events to observe changes in the collection.
117
+ * @param callbacks - An object containing the callbacks for various events (e.g., 'added', 'removed').
118
+ * @param skipInitial - A boolean indicating whether to skip invoking the callbacks for the initial state of the collection.
119
+ */
120
+ addCallbacks(callbacks, skipInitial = false) {
121
+ Object.keys(callbacks).forEach((key) => {
122
+ const typedKey = key;
123
+ this.callbacks[typedKey].push({
124
+ callback: callbacks[typedKey],
125
+ options: { skipInitial, isInitial: true }
126
+ });
127
+ });
128
+ }
129
+ /**
130
+ * Removes the specified callbacks for specific events, unregistering them from the observer.
131
+ * @param callbacks - An object containing the callbacks to be removed for various events.
132
+ */
133
+ removeCallbacks(callbacks) {
134
+ Object.keys(callbacks).forEach((key) => {
135
+ const typedKey = key;
136
+ const index = this.callbacks[typedKey].findIndex(({ callback }) => callback === callbacks[typedKey]);
137
+ this.callbacks[typedKey].splice(index, 1);
138
+ });
139
+ }
5
140
  }
6
141
  export {
7
- match as default
142
+ Observer as default
8
143
  };
package/dist/index19.mjs CHANGED
@@ -1,42 +1,8 @@
1
- function clone(value) {
2
- if (typeof value === "function")
3
- throw new Error("Cloning functions is not supported");
4
- if (value === null || typeof value !== "object")
5
- return value;
6
- if (value instanceof Date)
7
- return new Date(value);
8
- if (Array.isArray(value))
9
- return value.map((item) => clone(item));
10
- if (value instanceof Map) {
11
- const result2 = /* @__PURE__ */ new Map();
12
- value.forEach((currentValue, key) => {
13
- result2.set(key, clone(currentValue));
14
- });
15
- return result2;
16
- }
17
- if (value instanceof Set) {
18
- const result2 = /* @__PURE__ */ new Set();
19
- value.forEach((currentValue) => {
20
- result2.add(clone(currentValue));
21
- });
22
- return result2;
23
- }
24
- if (value instanceof RegExp)
25
- return new RegExp(value);
26
- const result = {};
27
- for (const key in value) {
28
- if (Object.hasOwnProperty.call(value, key)) {
29
- result[key] = clone(value[key]);
30
- }
31
- }
32
- return result;
33
- }
34
- function deepClone(object) {
35
- if (typeof structuredClone === "function")
36
- return structuredClone(object);
37
- return clone(object);
1
+ import { Query } from "mingo";
2
+ function match(item, selector) {
3
+ const query = new Query(selector);
4
+ return query.test(item);
38
5
  }
39
6
  export {
40
- clone,
41
- deepClone as default
7
+ match as default
42
8
  };
package/dist/index2.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import sortItems from "./index15.mjs";
2
2
  import project from "./index16.mjs";
3
- import Observer from "./index17.mjs";
3
+ import deepClone from "./index17.mjs";
4
+ import Observer from "./index18.mjs";
4
5
  function isInReactiveScope(reactivity) {
5
6
  if (!reactivity)
6
7
  return false;
@@ -28,6 +29,7 @@ class Cursor {
28
29
  * @param options.limit - The maximum number of items to return in the result set.
29
30
  * @param options.reactive - A reactivity adapter to enable observing changes in the cursor's result set.
30
31
  * @param options.fieldTracking - A boolean to enable fine-grained field tracking for reactivity.
32
+ * @param options.transformAll - A function that will be able to solve the n+1 problem
31
33
  */
32
34
  constructor(getItems, options) {
33
35
  this.getFilteredItems = getItems;
@@ -63,12 +65,16 @@ class Cursor {
63
65
  }
64
66
  getItems() {
65
67
  const items = this.getFilteredItems();
66
- const { sort, skip, limit } = this.options;
68
+ const { sort, skip, limit, transformAll, fields } = this.options;
67
69
  const sorted = sort ? sortItems(items, sort) : items;
68
70
  const skipped = skip ? sorted.slice(skip) : sorted;
69
71
  const limited = limit ? skipped.slice(0, limit) : skipped;
70
72
  const idExcluded = this.options.fields && this.options.fields.id === 0;
71
- return limited.map((item) => {
73
+ let entries = limited;
74
+ if (transformAll) {
75
+ entries = transformAll(deepClone(limited), fields);
76
+ }
77
+ return entries.map((item) => {
72
78
  if (!this.options.fields)
73
79
  return item;
74
80
  return {