@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/.vite/manifest.json +4 -3
- package/dist/AutoFetchCollection.d.ts +3 -3
- package/dist/Collection/Cursor.d.ts +10 -7
- package/dist/Collection/index.d.ts +27 -11
- package/dist/Collection/types.d.ts +1 -0
- package/dist/ReplicatedCollection.d.ts +3 -3
- package/dist/index.cjs11.js +1 -1
- package/dist/index.cjs17.js +36 -136
- package/dist/index.cjs18.js +140 -5
- package/dist/index.cjs19.js +5 -40
- package/dist/index.cjs2.js +9 -3
- package/dist/index.cjs3.js +80 -54
- package/dist/index.d.ts +1 -1
- package/dist/index11.mjs +1 -1
- package/dist/index17.mjs +35 -136
- package/dist/index18.mjs +140 -5
- package/dist/index19.mjs +5 -39
- package/dist/index2.mjs +9 -3
- package/dist/index3.mjs +80 -54
- package/dist/utils/deepClone.d.ts +1 -1
- package/package.json +3 -3
package/dist/index3.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import EventEmitter from "./index13.mjs";
|
|
2
|
-
import match from "./
|
|
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 "./
|
|
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.
|
|
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.
|
|
58
|
+
"mingo": "^7.1.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"zod": "^4.
|
|
61
|
+
"zod": "^4.3.5"
|
|
62
62
|
}
|
|
63
63
|
}
|