@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/index.cjs2.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 {
|
package/dist/index.cjs3.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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
package/dist/index17.mjs
CHANGED
|
@@ -1,143 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
40
|
+
clone,
|
|
41
|
+
deepClone as default
|
|
143
42
|
};
|
package/dist/index18.mjs
CHANGED
|
@@ -1,8 +1,143 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
142
|
+
Observer as default
|
|
8
143
|
};
|
package/dist/index19.mjs
CHANGED
|
@@ -1,42 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|