@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.
package/dist/index3.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import EventEmitter from "./index13.mjs";
2
- import match from "./index18.mjs";
2
+ import match from "./index19.mjs";
3
3
  import modify from "./index11.mjs";
4
4
  import isEqual from "./index10.mjs";
5
5
  import randomId from "./index12.mjs";
6
- import deepClone from "./index19.mjs";
6
+ import deepClone from "./index17.mjs";
7
7
  import serializeValue from "./index20.mjs";
8
8
  import createSignal from "./index21.mjs";
9
9
  import Cursor from "./index2.mjs";
@@ -93,6 +93,7 @@ class Collection extends EventEmitter {
93
93
  postBatchCallbacks = /* @__PURE__ */ new Set();
94
94
  fieldTracking = false;
95
95
  persistenceReadyPromise;
96
+ pendingUpdates = { added: [], modified: [], removed: [] };
96
97
  /**
97
98
  * Initializes a new instance of the `Collection` class with optional configuration.
98
99
  * Sets up memory, persistence, reactivity, and indices as specified in the options.
@@ -108,6 +109,7 @@ class Collection extends EventEmitter {
108
109
  * @param options.indices - An array of index providers for optimized querying.
109
110
  * @param options.enableDebugMode - A boolean to enable or disable debug mode.
110
111
  * @param options.fieldTracking - A boolean to enable or disable field tracking by default.
112
+ * @param options.transformAll - A function that will be able to solve the n+1 problem
111
113
  */
112
114
  constructor(options) {
113
115
  super();
@@ -142,48 +144,6 @@ class Collection extends EventEmitter {
142
144
  if (this.persistenceAdapter) {
143
145
  let ongoingSaves = 0;
144
146
  let isInitialized = false;
145
- const pendingUpdates = { added: [], modified: [], removed: [] };
146
- const loadPersistentData = async (data) => {
147
- if (!this.persistenceAdapter)
148
- throw new Error("Persistence adapter not found");
149
- this.emit("persistence.pullStarted");
150
- const { items, changes } = data ?? await this.persistenceAdapter.load();
151
- if (items) {
152
- if (ongoingSaves > 0)
153
- return;
154
- this.memory().splice(0, this.memoryArray().length, ...items);
155
- this.idIndex.clear();
156
- this.memory().map((item, index) => {
157
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
158
- });
159
- } else if (changes) {
160
- changes.added.forEach((item) => {
161
- const index = this.memory().findIndex((document) => document.id === item.id);
162
- if (index !== -1) {
163
- this.memory().splice(index, 1, item);
164
- return;
165
- }
166
- this.memory().push(item);
167
- const itemIndex = this.memory().findIndex((document) => document === item);
168
- this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
169
- });
170
- changes.modified.forEach((item) => {
171
- const index = this.memory().findIndex((document) => document.id === item.id);
172
- if (index === -1)
173
- throw new Error("Cannot resolve index for item");
174
- this.memory().splice(index, 1, item);
175
- });
176
- changes.removed.forEach((item) => {
177
- const index = this.memory().findIndex((document) => document.id === item.id);
178
- if (index === -1)
179
- throw new Error("Cannot resolve index for item");
180
- this.memory().splice(index, 1);
181
- });
182
- }
183
- this.rebuildIndices();
184
- this.emit("persistence.received");
185
- setTimeout(() => this.emit("persistence.pullCompleted"), 0);
186
- };
187
147
  const saveQueue = {
188
148
  added: [],
189
149
  modified: [],
@@ -220,7 +180,7 @@ class Collection extends EventEmitter {
220
180
  };
221
181
  this.on("added", (item) => {
222
182
  if (!isInitialized) {
223
- pendingUpdates.added.push(item);
183
+ this.pendingUpdates.added.push(item);
224
184
  return;
225
185
  }
226
186
  saveQueue.added.push(item);
@@ -228,7 +188,7 @@ class Collection extends EventEmitter {
228
188
  });
229
189
  this.on("changed", (item) => {
230
190
  if (!isInitialized) {
231
- pendingUpdates.modified.push(item);
191
+ this.pendingUpdates.modified.push(item);
232
192
  return;
233
193
  }
234
194
  saveQueue.modified.push(item);
@@ -236,27 +196,27 @@ class Collection extends EventEmitter {
236
196
  });
237
197
  this.on("removed", (item) => {
238
198
  if (!isInitialized) {
239
- pendingUpdates.removed.push(item);
199
+ this.pendingUpdates.removed.push(item);
240
200
  return;
241
201
  }
242
202
  saveQueue.removed.push(item);
243
203
  flushQueue();
244
204
  });
245
- this.persistenceAdapter.register((data) => loadPersistentData(data)).then(async () => {
205
+ this.persistenceAdapter.register((data) => this.loadPersistentData(data, ongoingSaves > 0)).then(async () => {
246
206
  if (!this.persistenceAdapter)
247
207
  throw new Error("Persistence adapter not found");
248
208
  let currentItems = this.memoryArray();
249
- await loadPersistentData();
250
- while (hasPendingUpdates(pendingUpdates)) {
251
- const added = pendingUpdates.added.splice(0);
252
- const modified = pendingUpdates.modified.splice(0);
253
- const removed = pendingUpdates.removed.splice(0);
209
+ await this.loadPersistentData();
210
+ while (hasPendingUpdates(this.pendingUpdates)) {
211
+ const added = this.pendingUpdates.added.splice(0);
212
+ const modified = this.pendingUpdates.modified.splice(0);
213
+ const removed = this.pendingUpdates.removed.splice(0);
254
214
  currentItems = applyUpdates(this.memoryArray(), { added, modified, removed });
255
215
  await this.persistenceAdapter.save(currentItems, { added, modified, removed }).then(() => {
256
216
  this.emit("persistence.transmitted");
257
217
  });
258
218
  }
259
- await loadPersistentData();
219
+ await this.loadPersistentData();
260
220
  isInitialized = true;
261
221
  setTimeout(() => this.emit("persistence.init"), 0);
262
222
  }).catch((error) => {
@@ -271,6 +231,66 @@ class Collection extends EventEmitter {
271
231
  });
272
232
  Collection.onCreationCallbacks.forEach((callback) => callback(this));
273
233
  }
234
+ /**
235
+ * Resets the collection's data by clearing the in-memory items and reloading from the persistence adapter.
236
+ * @returns A promise that resolves when the data has been reset and reloaded.
237
+ */
238
+ async resetData() {
239
+ if (hasPendingUpdates(this.pendingUpdates)) {
240
+ await new Promise((resolve) => {
241
+ this.on("persistence.transmitted", resolve);
242
+ });
243
+ }
244
+ this.options.memory = [];
245
+ await this.loadPersistentData();
246
+ }
247
+ /**
248
+ * Loads data from the persistence adapter and updates the in-memory collection accordingly.
249
+ * @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.
250
+ * @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.
251
+ * @returns A promise that resolves when the data has been loaded and the in-memory collection has been updated.
252
+ */
253
+ async loadPersistentData(data, hasOngoingSaves = false) {
254
+ if (!this.persistenceAdapter)
255
+ throw new Error("Persistence adapter not found");
256
+ this.emit("persistence.pullStarted");
257
+ const { items, changes } = data ?? await this.persistenceAdapter.load();
258
+ if (items) {
259
+ if (hasOngoingSaves)
260
+ return;
261
+ this.memory().splice(0, this.memoryArray().length, ...items);
262
+ this.idIndex.clear();
263
+ this.memory().map((item, index) => {
264
+ this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
265
+ });
266
+ } else if (changes) {
267
+ changes.added.forEach((item) => {
268
+ const index = this.memory().findIndex((document) => document.id === item.id);
269
+ if (index !== -1) {
270
+ this.memory().splice(index, 1, item);
271
+ return;
272
+ }
273
+ this.memory().push(item);
274
+ const itemIndex = this.memory().findIndex((document) => document === item);
275
+ this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
276
+ });
277
+ changes.modified.forEach((item) => {
278
+ const index = this.memory().findIndex((document) => document.id === item.id);
279
+ if (index === -1)
280
+ throw new Error("Cannot resolve index for item");
281
+ this.memory().splice(index, 1, item);
282
+ });
283
+ changes.removed.forEach((item) => {
284
+ const index = this.memory().findIndex((document) => document.id === item.id);
285
+ if (index === -1)
286
+ throw new Error("Cannot resolve index for item");
287
+ this.memory().splice(index, 1);
288
+ });
289
+ }
290
+ this.rebuildIndices();
291
+ this.emit("persistence.received");
292
+ setTimeout(() => this.emit("persistence.pullCompleted"), 0);
293
+ }
274
294
  /**
275
295
  * Checks whether the collection is currently performing a pull operation
276
296
  * ⚡️ this function is reactive!
@@ -425,6 +445,11 @@ class Collection extends EventEmitter {
425
445
  return item;
426
446
  return this.options.transform(item);
427
447
  }
448
+ transformAll(items, fields) {
449
+ if (!this.options.transformAll)
450
+ return items;
451
+ return this.options.transformAll(items, fields);
452
+ }
428
453
  getItems(selector) {
429
454
  return this.profile(() => {
430
455
  const indexInfo = this.getIndexInfo(selector);
@@ -484,6 +509,7 @@ class Collection extends EventEmitter {
484
509
  fieldTracking: this.fieldTracking,
485
510
  ...options,
486
511
  transform: this.transform.bind(this),
512
+ transformAll: this.transformAll.bind(this),
487
513
  bindEvents: (requery) => {
488
514
  const handleRequery = () => {
489
515
  if (this.batchOperationInProgress) {
@@ -4,7 +4,7 @@
4
4
  * @template T - The type of the value to clone.
5
5
  * @param value - The value to deep clone.
6
6
  * @returns A deep copy of the provided value.
7
- * @throws An error if the value is a function, as cloning functions is not supported.
7
+ * @throws {Error} An error if the value is a function, as cloning functions is not supported.
8
8
  */
9
9
  export declare function clone<T>(value: T): T;
10
10
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaldb/core",
3
- "version": "1.7.2",
3
+ "version": "1.8.0",
4
4
  "description": "SignalDB is a client-side database that provides a simple MongoDB-like interface to the data with first-class typescript support to achieve an optimistic UI. Data persistence can be achieved by using storage providers that store the data through a JSON interface to places such as localStorage.",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && vite build",
@@ -55,9 +55,9 @@
55
55
  ],
56
56
  "dependencies": {
57
57
  "fast-sort": "^3.4.1",
58
- "mingo": "^7.0.0"
58
+ "mingo": "^7.1.1"
59
59
  },
60
60
  "devDependencies": {
61
- "zod": "^4.0.14"
61
+ "zod": "^4.3.5"
62
62
  }
63
63
  }