@signaldb/core 1.2.3 → 1.3.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 +28 -47
- package/dist/index.cjs.js +12 -18
- package/dist/index.cjs10.js +6 -27
- package/dist/index.cjs11.js +3 -6
- package/dist/index.cjs12.js +136 -3
- package/dist/index.cjs13.js +40 -134
- package/dist/index.cjs14.js +5 -38
- package/dist/index.cjs15.js +42 -2
- package/dist/index.cjs16.js +11 -5
- package/dist/index.cjs17.js +17 -40
- package/dist/index.cjs18.js +262 -11
- package/dist/index.cjs19.js +66 -16
- package/dist/index.cjs2.js +688 -96
- package/dist/index.cjs20.js +71 -247
- package/dist/index.cjs21.js +8 -68
- package/dist/index.cjs22.js +22 -83
- package/dist/index.cjs23.js +144 -8
- package/dist/index.cjs24.js +5 -25
- package/dist/index.cjs25.js +16 -144
- package/dist/index.cjs26.js +27 -5
- package/dist/index.cjs27.js +39 -15
- package/dist/index.cjs28.js +25 -24
- package/dist/index.cjs29.js +7 -40
- package/dist/index.cjs3.js +139 -664
- package/dist/index.cjs4.js +62 -166
- package/dist/index.cjs5.js +3 -67
- package/dist/index.cjs6.js +2 -2
- package/dist/index.cjs7.js +2 -2
- package/dist/index.cjs8.js +2 -2
- package/dist/index.cjs9.js +27 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.mjs +12 -18
- package/dist/index10.mjs +6 -27
- package/dist/index11.mjs +3 -6
- package/dist/index12.mjs +136 -3
- package/dist/index13.mjs +39 -134
- package/dist/index14.mjs +5 -37
- package/dist/index15.mjs +41 -2
- package/dist/index16.mjs +11 -5
- package/dist/index17.mjs +17 -39
- package/dist/index18.mjs +261 -11
- package/dist/index19.mjs +65 -16
- package/dist/index2.mjs +688 -94
- package/dist/index20.mjs +71 -247
- package/dist/index21.mjs +8 -67
- package/dist/index22.mjs +22 -82
- package/dist/index23.mjs +144 -8
- package/dist/index24.mjs +5 -25
- package/dist/index25.mjs +16 -144
- package/dist/index26.mjs +27 -5
- package/dist/index27.mjs +39 -15
- package/dist/index28.mjs +25 -24
- package/dist/index29.mjs +7 -40
- package/dist/index3.mjs +139 -664
- package/dist/index4.mjs +61 -166
- package/dist/index5.mjs +3 -66
- package/dist/index6.mjs +2 -2
- package/dist/index7.mjs +2 -2
- package/dist/index8.mjs +2 -2
- package/dist/index9.mjs +27 -3
- package/dist/types/Selector.d.ts +33 -35
- package/dist/utils/getMatchingKeys.d.ts +11 -6
- package/package.json +1 -1
- package/dist/devtools.d.ts +0 -4
- package/dist/index.cjs30.js +0 -29
- package/dist/index.cjs31.js +0 -9
- package/dist/index30.mjs +0 -30
- package/dist/index31.mjs +0 -10
package/dist/index3.mjs
CHANGED
|
@@ -1,699 +1,174 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import isEqual from "./index10.mjs";
|
|
8
|
-
import randomId from "./index12.mjs";
|
|
9
|
-
import deepClone from "./index17.mjs";
|
|
10
|
-
import serializeValue from "./index18.mjs";
|
|
11
|
-
import createSignal from "./index19.mjs";
|
|
12
|
-
import Cursor from "./index20.mjs";
|
|
13
|
-
import getIndexInfo from "./index21.mjs";
|
|
14
|
-
import { createExternalIndex } from "./index14.mjs";
|
|
15
|
-
import { default as default2 } from "./index14.mjs";
|
|
16
|
-
function hasPendingUpdates(pendingUpdates) {
|
|
17
|
-
return pendingUpdates.added.length > 0 || pendingUpdates.modified.length > 0 || pendingUpdates.removed.length > 0;
|
|
18
|
-
}
|
|
19
|
-
function applyUpdates(currentItems, { added, modified, removed }) {
|
|
20
|
-
const items = [...currentItems];
|
|
21
|
-
added.forEach((item) => {
|
|
22
|
-
items.push(item);
|
|
23
|
-
});
|
|
24
|
-
modified.forEach((item) => {
|
|
25
|
-
const index = items.findIndex(({ id }) => id === item.id);
|
|
26
|
-
if (index === -1)
|
|
27
|
-
return;
|
|
28
|
-
items[index] = item;
|
|
29
|
-
});
|
|
30
|
-
removed.forEach((item) => {
|
|
31
|
-
const index = items.findIndex(({ id }) => id === item.id);
|
|
32
|
-
if (index === -1)
|
|
33
|
-
return;
|
|
34
|
-
items.splice(index, 1);
|
|
35
|
-
});
|
|
36
|
-
return items;
|
|
37
|
-
}
|
|
38
|
-
const _Collection = class _Collection extends EventEmitter {
|
|
4
|
+
import ReplicatedCollection from "./index20.mjs";
|
|
5
|
+
import createSignal from "./index17.mjs";
|
|
6
|
+
class AutoFetchCollection extends ReplicatedCollection {
|
|
39
7
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* @
|
|
43
|
-
* @template I - The type of the unique identifier for the items.
|
|
44
|
-
* @template U - The transformed item type after applying transformations (default is T).
|
|
45
|
-
* @param options - Optional configuration for the collection.
|
|
46
|
-
* @param options.name - An optional name for the collection.
|
|
47
|
-
* @param options.memory - The in-memory adapter for storing items.
|
|
48
|
-
* @param options.reactivity - The reactivity adapter for observing changes in the collection.
|
|
49
|
-
* @param options.transform - A transformation function to apply to items when retrieving them.
|
|
50
|
-
* @param options.persistence - The persistence adapter for saving and loading items.
|
|
51
|
-
* @param options.indices - An array of index providers for optimized querying.
|
|
52
|
-
* @param options.enableDebugMode - A boolean to enable or disable debug mode.
|
|
53
|
-
* @param options.fieldTracking - A boolean to enable or disable field tracking by default.
|
|
8
|
+
* @param options {Object} - Options for the collection.
|
|
9
|
+
* @param options.fetchQueryItems {Function} - A function that fetches items from the server. It takes the selector as an argument and returns a promise that resolves to an object with an `items` property.
|
|
10
|
+
* @param options.purgeDelay {Number} - The delay in milliseconds before purging an item from the cache.
|
|
54
11
|
*/
|
|
55
12
|
constructor(options) {
|
|
56
|
-
var _a
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
__publicField(this, "batchOperationInProgress", false);
|
|
68
|
-
__publicField(this, "isDisposed", false);
|
|
69
|
-
__publicField(this, "postBatchCallbacks", /* @__PURE__ */ new Set());
|
|
70
|
-
__publicField(this, "fieldTracking", false);
|
|
71
|
-
__publicField(this, "persistenceReadyPromise");
|
|
72
|
-
_Collection.collections.push(this);
|
|
73
|
-
this.name = (options == null ? void 0 : options.name) ?? `${this.constructor.name}-${randomId()}`;
|
|
74
|
-
this.options = {
|
|
75
|
-
memory: [],
|
|
76
|
-
...options
|
|
77
|
-
};
|
|
78
|
-
this.fieldTracking = this.options.fieldTracking ?? _Collection.fieldTracking;
|
|
79
|
-
this.debugMode = this.options.enableDebugMode ?? _Collection.debugMode;
|
|
80
|
-
this.indexProviders = [
|
|
81
|
-
createExternalIndex("id", this.idIndex),
|
|
82
|
-
...this.options.indices || []
|
|
83
|
-
];
|
|
84
|
-
this.rebuildIndices();
|
|
85
|
-
this.isPullingSignal = createSignal((_a = this.options.reactivity) == null ? void 0 : _a.create(), !!(options == null ? void 0 : options.persistence));
|
|
86
|
-
this.isPushingSignal = createSignal((_b = this.options.reactivity) == null ? void 0 : _b.create(), false);
|
|
87
|
-
this.on("persistence.pullStarted", () => {
|
|
88
|
-
this.isPullingSignal.set(true);
|
|
89
|
-
});
|
|
90
|
-
this.on("persistence.pullCompleted", () => {
|
|
91
|
-
this.isPullingSignal.set(false);
|
|
92
|
-
});
|
|
93
|
-
this.on("persistence.pushStarted", () => {
|
|
94
|
-
this.isPushingSignal.set(true);
|
|
95
|
-
});
|
|
96
|
-
this.on("persistence.pushCompleted", () => {
|
|
97
|
-
this.isPushingSignal.set(false);
|
|
98
|
-
});
|
|
99
|
-
this.persistenceAdapter = this.options.persistence ?? null;
|
|
100
|
-
if (this.persistenceAdapter) {
|
|
101
|
-
let ongoingSaves = 0;
|
|
102
|
-
let isInitialized = false;
|
|
103
|
-
const pendingUpdates = { added: [], modified: [], removed: [] };
|
|
104
|
-
const loadPersistentData = async (data) => {
|
|
105
|
-
if (!this.persistenceAdapter)
|
|
106
|
-
throw new Error("Persistence adapter not found");
|
|
107
|
-
this.emit("persistence.pullStarted");
|
|
108
|
-
const { items, changes } = data ?? await this.persistenceAdapter.load();
|
|
109
|
-
if (items) {
|
|
110
|
-
if (ongoingSaves > 0)
|
|
111
|
-
return;
|
|
112
|
-
this.memory().splice(0, this.memoryArray().length, ...items);
|
|
113
|
-
this.idIndex.clear();
|
|
114
|
-
this.memory().map((item, index) => {
|
|
115
|
-
this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
|
|
116
|
-
});
|
|
117
|
-
} else if (changes) {
|
|
118
|
-
changes.added.forEach((item) => {
|
|
119
|
-
const index = this.memory().findIndex((document) => document.id === item.id);
|
|
120
|
-
if (index !== -1) {
|
|
121
|
-
this.memory().splice(index, 1, item);
|
|
13
|
+
var _a;
|
|
14
|
+
let triggerRemoteChange;
|
|
15
|
+
super({
|
|
16
|
+
...options,
|
|
17
|
+
pull: () => Promise.resolve({
|
|
18
|
+
items: [...this.itemsCache.values()].reduce((memo, items) => {
|
|
19
|
+
const newItems = [...memo];
|
|
20
|
+
items.forEach((item) => {
|
|
21
|
+
const index = newItems.findIndex((i) => i.id === item.id);
|
|
22
|
+
if (index === -1) {
|
|
23
|
+
newItems.push(item);
|
|
122
24
|
return;
|
|
123
25
|
}
|
|
124
|
-
this.
|
|
125
|
-
const itemIndex = this.memory().findIndex((document) => document === item);
|
|
126
|
-
this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([itemIndex]));
|
|
127
|
-
});
|
|
128
|
-
changes.modified.forEach((item) => {
|
|
129
|
-
const index = this.memory().findIndex((document) => document.id === item.id);
|
|
130
|
-
if (index === -1)
|
|
131
|
-
throw new Error("Cannot resolve index for item");
|
|
132
|
-
this.memory().splice(index, 1, item);
|
|
133
|
-
});
|
|
134
|
-
changes.removed.forEach((item) => {
|
|
135
|
-
const index = this.memory().findIndex((document) => document.id === item.id);
|
|
136
|
-
if (index === -1)
|
|
137
|
-
throw new Error("Cannot resolve index for item");
|
|
138
|
-
this.memory().splice(index, 1);
|
|
26
|
+
newItems[index] = this.mergeItems(newItems[index], item);
|
|
139
27
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
added: [],
|
|
147
|
-
modified: [],
|
|
148
|
-
removed: []
|
|
149
|
-
};
|
|
150
|
-
let isFlushing = false;
|
|
151
|
-
const flushQueue = () => {
|
|
152
|
-
if (!this.persistenceAdapter)
|
|
153
|
-
throw new Error("Persistence adapter not found");
|
|
154
|
-
if (ongoingSaves <= 0)
|
|
155
|
-
this.emit("persistence.pushStarted");
|
|
156
|
-
if (isFlushing)
|
|
157
|
-
return;
|
|
158
|
-
if (!hasPendingUpdates(saveQueue))
|
|
159
|
-
return;
|
|
160
|
-
isFlushing = true;
|
|
161
|
-
ongoingSaves += 1;
|
|
162
|
-
const currentItems = this.memoryArray();
|
|
163
|
-
const changes = { ...saveQueue };
|
|
164
|
-
saveQueue.added = [];
|
|
165
|
-
saveQueue.modified = [];
|
|
166
|
-
saveQueue.removed = [];
|
|
167
|
-
this.persistenceAdapter.save(currentItems, changes).then(() => {
|
|
168
|
-
this.emit("persistence.transmitted");
|
|
169
|
-
}).catch((error) => {
|
|
170
|
-
this.emit("persistence.error", error instanceof Error ? error : new Error(error));
|
|
171
|
-
}).finally(() => {
|
|
172
|
-
ongoingSaves -= 1;
|
|
173
|
-
isFlushing = false;
|
|
174
|
-
flushQueue();
|
|
175
|
-
if (ongoingSaves <= 0)
|
|
176
|
-
this.emit("persistence.pushCompleted");
|
|
177
|
-
});
|
|
178
|
-
};
|
|
179
|
-
this.on("added", (item) => {
|
|
180
|
-
if (!isInitialized) {
|
|
181
|
-
pendingUpdates.added.push(item);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
saveQueue.added.push(item);
|
|
185
|
-
flushQueue();
|
|
186
|
-
});
|
|
187
|
-
this.on("changed", (item) => {
|
|
188
|
-
if (!isInitialized) {
|
|
189
|
-
pendingUpdates.modified.push(item);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
saveQueue.modified.push(item);
|
|
193
|
-
flushQueue();
|
|
194
|
-
});
|
|
195
|
-
this.on("removed", (item) => {
|
|
196
|
-
if (!isInitialized) {
|
|
197
|
-
pendingUpdates.removed.push(item);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
saveQueue.removed.push(item);
|
|
201
|
-
flushQueue();
|
|
202
|
-
});
|
|
203
|
-
this.persistenceAdapter.register((data) => loadPersistentData(data)).then(async () => {
|
|
204
|
-
if (!this.persistenceAdapter)
|
|
205
|
-
throw new Error("Persistence adapter not found");
|
|
206
|
-
let currentItems = this.memoryArray();
|
|
207
|
-
await loadPersistentData();
|
|
208
|
-
while (hasPendingUpdates(pendingUpdates)) {
|
|
209
|
-
const added = pendingUpdates.added.splice(0);
|
|
210
|
-
const modified = pendingUpdates.modified.splice(0);
|
|
211
|
-
const removed = pendingUpdates.removed.splice(0);
|
|
212
|
-
currentItems = applyUpdates(this.memoryArray(), { added, modified, removed });
|
|
213
|
-
await this.persistenceAdapter.save(currentItems, { added, modified, removed }).then(() => {
|
|
214
|
-
this.emit("persistence.transmitted");
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
await loadPersistentData();
|
|
218
|
-
isInitialized = true;
|
|
219
|
-
setTimeout(() => this.emit("persistence.init"), 0);
|
|
220
|
-
}).catch((error) => {
|
|
221
|
-
this.emit("persistence.error", error instanceof Error ? error : new Error(error));
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
this.persistenceReadyPromise = new Promise((resolve, reject) => {
|
|
225
|
-
if (!this.persistenceAdapter)
|
|
226
|
-
return resolve();
|
|
227
|
-
this.once("persistence.init", resolve);
|
|
228
|
-
this.once("persistence.error", reject);
|
|
28
|
+
return newItems;
|
|
29
|
+
}, [])
|
|
30
|
+
}),
|
|
31
|
+
registerRemoteChange: async (onChange) => {
|
|
32
|
+
triggerRemoteChange = onChange;
|
|
33
|
+
}
|
|
229
34
|
});
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
* Checks whether the collection is currently performing a pull operation
|
|
255
|
-
* ⚡️ this function is reactive!
|
|
256
|
-
* (loading data from the persistence adapter).
|
|
257
|
-
* @returns A boolean indicating if the collection is in the process of pulling data.
|
|
258
|
-
*/
|
|
259
|
-
isPulling() {
|
|
260
|
-
return this.isPullingSignal.get() ?? false;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Checks whether the collection is currently performing a push operation
|
|
264
|
-
* ⚡️ this function is reactive!
|
|
265
|
-
* (saving data to the persistence adapter).
|
|
266
|
-
* @returns A boolean indicating if the collection is in the process of pushing data.
|
|
267
|
-
*/
|
|
268
|
-
isPushing() {
|
|
269
|
-
return this.isPushingSignal.get() ?? false;
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Checks whether the collection is currently performing either a pull or push operation,
|
|
273
|
-
* ⚡️ this function is reactive!
|
|
274
|
-
* indicating that it is loading or saving data.
|
|
275
|
-
* @returns A boolean indicating if the collection is in the process of loading or saving data.
|
|
276
|
-
*/
|
|
277
|
-
isLoading() {
|
|
278
|
-
const isPulling = this.isPulling();
|
|
279
|
-
const isPushing = this.isPushing();
|
|
280
|
-
return isPulling || isPushing;
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Retrieves the current debug mode status of the collection.
|
|
284
|
-
* @returns A boolean indicating whether debug mode is enabled for the collection.
|
|
285
|
-
*/
|
|
286
|
-
getDebugMode() {
|
|
287
|
-
return this.debugMode;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Enables or disables debug mode for the collection.
|
|
291
|
-
* When debug mode is enabled, additional debugging information and events are emitted.
|
|
292
|
-
* @param enable - A boolean indicating whether to enable (`true`) or disable (`false`) debug mode.
|
|
293
|
-
*/
|
|
294
|
-
setDebugMode(enable) {
|
|
295
|
-
this.debugMode = enable;
|
|
35
|
+
__publicField(this, "activeObservers", /* @__PURE__ */ new Map());
|
|
36
|
+
__publicField(this, "observerTimeouts", /* @__PURE__ */ new Map());
|
|
37
|
+
__publicField(this, "purgeDelay");
|
|
38
|
+
__publicField(this, "idQueryCache", /* @__PURE__ */ new Map());
|
|
39
|
+
__publicField(this, "itemsCache", /* @__PURE__ */ new Map());
|
|
40
|
+
__publicField(this, "fetchQueryItems");
|
|
41
|
+
__publicField(this, "triggerReload", null);
|
|
42
|
+
__publicField(this, "reactivityAdapter", null);
|
|
43
|
+
__publicField(this, "loadingSignals", /* @__PURE__ */ new Map());
|
|
44
|
+
__publicField(this, "isFetchingSignal");
|
|
45
|
+
__publicField(this, "mergeItems");
|
|
46
|
+
this.mergeItems = options.mergeItems ?? ((itemA, itemB) => ({ ...itemA, ...itemB }));
|
|
47
|
+
this.purgeDelay = options.purgeDelay ?? 1e4;
|
|
48
|
+
this.isFetchingSignal = createSignal((_a = options.reactivity) == null ? void 0 : _a.create(), false);
|
|
49
|
+
if (!triggerRemoteChange)
|
|
50
|
+
throw new Error("No triggerRemoteChange method found. Looks like your persistence adapter was not registered");
|
|
51
|
+
this.triggerReload = triggerRemoteChange;
|
|
52
|
+
this.reactivityAdapter = options.reactivity ?? null;
|
|
53
|
+
this.fetchQueryItems = options.fetchQueryItems;
|
|
54
|
+
this.on("observer.created", (selector) => this.handleObserverCreation(selector ?? {}));
|
|
55
|
+
this.on("observer.disposed", (selector) => setTimeout(() => this.handleObserverDisposal(selector ?? {}), 100));
|
|
56
|
+
if (options.registerRemoteChange) {
|
|
57
|
+
void options.registerRemoteChange(() => this.forceRefetch());
|
|
58
|
+
}
|
|
296
59
|
}
|
|
297
60
|
/**
|
|
298
|
-
*
|
|
299
|
-
* @param
|
|
61
|
+
* Registers a query manually that items should be fetched for it
|
|
62
|
+
* @param selector {Object} Selector of the query
|
|
300
63
|
*/
|
|
301
|
-
|
|
302
|
-
this.
|
|
64
|
+
registerQuery(selector) {
|
|
65
|
+
this.handleObserverCreation(selector);
|
|
303
66
|
}
|
|
304
67
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
* @returns A promise that resolves when the collection is ready.
|
|
308
|
-
* @example
|
|
309
|
-
* ```ts
|
|
310
|
-
* const collection = new Collection({
|
|
311
|
-
* persistence: // ...
|
|
312
|
-
* })
|
|
313
|
-
* await collection.isReady()
|
|
314
|
-
*
|
|
315
|
-
* collection.insert({ name: 'Item 1' })
|
|
68
|
+
* Unregisters a query manually that items are not fetched anymore for it
|
|
69
|
+
* @param selector {Object} Selector of the query
|
|
316
70
|
*/
|
|
317
|
-
|
|
318
|
-
|
|
71
|
+
unregisterQuery(selector) {
|
|
72
|
+
this.handleObserverDisposal(selector);
|
|
319
73
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return fn();
|
|
323
|
-
const startTime = performance.now();
|
|
324
|
-
const result = fn();
|
|
325
|
-
const endTime = performance.now();
|
|
326
|
-
measureFunction(endTime - startTime);
|
|
327
|
-
return result;
|
|
74
|
+
getKeyForSelector(selector) {
|
|
75
|
+
return JSON.stringify(selector);
|
|
328
76
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return;
|
|
332
|
-
const callstack = new Error().stack || "";
|
|
333
|
-
fn(callstack);
|
|
334
|
-
}
|
|
335
|
-
rebuildIndices() {
|
|
336
|
-
this.indicesOutdated = true;
|
|
337
|
-
if (this.batchOperationInProgress)
|
|
338
|
-
return;
|
|
339
|
-
this.rebuildAllIndices();
|
|
340
|
-
}
|
|
341
|
-
rebuildAllIndices() {
|
|
342
|
-
this.idIndex.clear();
|
|
343
|
-
this.memory().map((item, index) => {
|
|
344
|
-
this.idIndex.set(serializeValue(item.id), /* @__PURE__ */ new Set([index]));
|
|
77
|
+
async forceRefetch() {
|
|
78
|
+
return Promise.all([...this.activeObservers.values()].map(({ selector }) => this.fetchSelector(selector))).then(() => {
|
|
345
79
|
});
|
|
346
|
-
this.indexProviders.forEach((index) => index.rebuild(this.memoryArray()));
|
|
347
|
-
this.indicesOutdated = false;
|
|
348
|
-
}
|
|
349
|
-
getIndexInfo(selector) {
|
|
350
|
-
if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
|
|
351
|
-
return {
|
|
352
|
-
matched: true,
|
|
353
|
-
positions: [...this.idIndex.get(serializeValue(selector.id)) || []],
|
|
354
|
-
optimizedSelector: {}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (selector == null || this.indicesOutdated) {
|
|
358
|
-
return {
|
|
359
|
-
matched: false,
|
|
360
|
-
positions: [],
|
|
361
|
-
optimizedSelector: {}
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
return getIndexInfo(this.indexProviders, selector);
|
|
365
80
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
81
|
+
fetchSelector(selector) {
|
|
82
|
+
this.isFetchingSignal.set(true);
|
|
83
|
+
return this.fetchQueryItems(selector).then((response) => {
|
|
84
|
+
if (!response.items)
|
|
85
|
+
throw new Error("AutoFetchCollection currently only works with a full item response");
|
|
86
|
+
this.itemsCache.set(this.getKeyForSelector(selector), response.items);
|
|
87
|
+
response.items.forEach((item) => {
|
|
88
|
+
const queries = this.idQueryCache.get(item.id) ?? [];
|
|
89
|
+
queries.push(selector);
|
|
90
|
+
this.idQueryCache.set(item.id, queries);
|
|
91
|
+
});
|
|
92
|
+
this.setLoading(selector, true);
|
|
93
|
+
this.once("persistence.received", () => {
|
|
94
|
+
this.setLoading(selector, false);
|
|
95
|
+
});
|
|
96
|
+
if (!this.triggerReload)
|
|
97
|
+
throw new Error("No triggerReload method found. Looks like your persistence adapter was not registered");
|
|
98
|
+
void this.triggerReload();
|
|
99
|
+
}).catch((error) => {
|
|
100
|
+
this.emit("persistence.error", error);
|
|
101
|
+
}).finally(() => {
|
|
102
|
+
this.isFetchingSignal.set(false);
|
|
387
103
|
});
|
|
388
104
|
}
|
|
389
|
-
|
|
390
|
-
return this.options.memory;
|
|
391
|
-
}
|
|
392
|
-
memoryArray() {
|
|
393
|
-
return this.memory().map((item) => item);
|
|
394
|
-
}
|
|
395
|
-
transform(item) {
|
|
396
|
-
if (!this.options.transform)
|
|
397
|
-
return item;
|
|
398
|
-
return this.options.transform(item);
|
|
399
|
-
}
|
|
400
|
-
getItems(selector) {
|
|
401
|
-
return this.profile(() => {
|
|
402
|
-
const indexInfo = this.getIndexInfo(selector);
|
|
403
|
-
const matchItems = (item) => {
|
|
404
|
-
if (indexInfo.optimizedSelector == null)
|
|
405
|
-
return true;
|
|
406
|
-
if (Object.keys(indexInfo.optimizedSelector).length <= 0)
|
|
407
|
-
return true;
|
|
408
|
-
const matches = match(item, indexInfo.optimizedSelector);
|
|
409
|
-
return matches;
|
|
410
|
-
};
|
|
411
|
-
if (!indexInfo.matched)
|
|
412
|
-
return this.memory().filter(matchItems);
|
|
413
|
-
const memory = this.memoryArray();
|
|
414
|
-
const items = indexInfo.positions.map((index) => memory[index]);
|
|
415
|
-
this.emit("getItems", selector);
|
|
416
|
-
return items.filter(matchItems);
|
|
417
|
-
}, (measuredTime) => this.executeInDebugMode((callstack) => this.emit("_debug.getItems", callstack, selector, measuredTime)));
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* Disposes the collection, unregisters persistence adapters, clears memory, and
|
|
421
|
-
* cleans up all resources used by the collection.
|
|
422
|
-
* @returns A promise that resolves when the collection is disposed.
|
|
423
|
-
*/
|
|
424
|
-
async dispose() {
|
|
105
|
+
handleObserverCreation(selector) {
|
|
425
106
|
var _a;
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
this.idIndex.clear();
|
|
431
|
-
this.indexProviders = [];
|
|
432
|
-
this.isDisposed = true;
|
|
433
|
-
this.removeAllListeners();
|
|
434
|
-
_Collection.collections = _Collection.collections.filter((collection) => collection !== this);
|
|
435
|
-
_Collection.onDisposeCallbacks.forEach((callback) => callback(this));
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Finds multiple items in the collection based on a selector and optional options.
|
|
439
|
-
* Returns a cursor for reactive data queries.
|
|
440
|
-
* @template O - The options type for the find operation.
|
|
441
|
-
* @param [selector] - The criteria to select items.
|
|
442
|
-
* @param [options] - Options for the find operation, such as limit and sort.
|
|
443
|
-
* @returns A cursor to fetch and observe the matching items.
|
|
444
|
-
*/
|
|
445
|
-
find(selector, options) {
|
|
446
|
-
if (this.isDisposed)
|
|
447
|
-
throw new Error("Collection is disposed");
|
|
448
|
-
if (selector !== void 0 && (!selector || typeof selector !== "object"))
|
|
449
|
-
throw new Error("Invalid selector");
|
|
450
|
-
const cursor = new Cursor(() => this.getItems(selector), {
|
|
451
|
-
reactive: this.options.reactivity,
|
|
452
|
-
fieldTracking: this.fieldTracking,
|
|
453
|
-
...options,
|
|
454
|
-
transform: this.transform.bind(this),
|
|
455
|
-
bindEvents: (requery) => {
|
|
456
|
-
const handleRequery = () => {
|
|
457
|
-
if (this.batchOperationInProgress) {
|
|
458
|
-
this.postBatchCallbacks.add(requery);
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
requery();
|
|
462
|
-
};
|
|
463
|
-
this.addListener("persistence.received", handleRequery);
|
|
464
|
-
this.addListener("added", handleRequery);
|
|
465
|
-
this.addListener("changed", handleRequery);
|
|
466
|
-
this.addListener("removed", handleRequery);
|
|
467
|
-
this.emit("observer.created", selector, options);
|
|
468
|
-
return () => {
|
|
469
|
-
this.removeListener("persistence.received", handleRequery);
|
|
470
|
-
this.removeListener("added", handleRequery);
|
|
471
|
-
this.removeListener("changed", handleRequery);
|
|
472
|
-
this.removeListener("removed", handleRequery);
|
|
473
|
-
this.emit("observer.disposed", selector, options);
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
this.emit("find", selector, options, cursor);
|
|
478
|
-
this.executeInDebugMode((callstack) => this.emit("_debug.find", callstack, selector, options, cursor));
|
|
479
|
-
return cursor;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* Finds a single item in the collection based on a selector and optional options.
|
|
483
|
-
* ⚡️ this function is reactive!
|
|
484
|
-
* Returns the found item or undefined if no item matches.
|
|
485
|
-
* @template O - The options type for the find operation.
|
|
486
|
-
* @param selector - The criteria to select the item.
|
|
487
|
-
* @param [options] - Options for the find operation, such as projection.
|
|
488
|
-
* @returns The found item or `undefined`.
|
|
489
|
-
*/
|
|
490
|
-
findOne(selector, options) {
|
|
491
|
-
if (this.isDisposed)
|
|
492
|
-
throw new Error("Collection is disposed");
|
|
493
|
-
const cursor = this.find(selector, {
|
|
494
|
-
limit: 1,
|
|
495
|
-
...options
|
|
107
|
+
const activeObservers = ((_a = this.activeObservers.get(this.getKeyForSelector(selector))) == null ? void 0 : _a.count) ?? 0;
|
|
108
|
+
this.activeObservers.set(this.getKeyForSelector(selector), {
|
|
109
|
+
selector,
|
|
110
|
+
count: activeObservers + 1
|
|
496
111
|
});
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Performs a batch operation, deferring index rebuilds and allowing multiple
|
|
504
|
-
* modifications to be made atomically. Executes any post-batch callbacks afterwards.
|
|
505
|
-
* @param callback - The batch operation to execute.
|
|
506
|
-
*/
|
|
507
|
-
batch(callback) {
|
|
508
|
-
this.batchOperationInProgress = true;
|
|
509
|
-
callback();
|
|
510
|
-
this.batchOperationInProgress = false;
|
|
511
|
-
this.rebuildAllIndices();
|
|
512
|
-
this.postBatchCallbacks.forEach((callback_) => callback_());
|
|
513
|
-
this.postBatchCallbacks.clear();
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Inserts a single item into the collection. Generates a unique ID if not provided.
|
|
517
|
-
* @param item - The item to insert.
|
|
518
|
-
* @returns The ID of the inserted item.
|
|
519
|
-
* @throws {Error} If the collection is disposed or the item has an invalid ID.
|
|
520
|
-
*/
|
|
521
|
-
insert(item) {
|
|
522
|
-
if (this.isDisposed)
|
|
523
|
-
throw new Error("Collection is disposed");
|
|
524
|
-
if (!item)
|
|
525
|
-
throw new Error("Invalid item");
|
|
526
|
-
const newItem = { id: randomId(), ...item };
|
|
527
|
-
if (this.idIndex.has(serializeValue(newItem.id)))
|
|
528
|
-
throw new Error("Item with same id already exists");
|
|
529
|
-
this.memory().push(newItem);
|
|
530
|
-
const itemIndex = this.memory().findIndex((document) => document === newItem);
|
|
531
|
-
this.idIndex.set(serializeValue(newItem.id), /* @__PURE__ */ new Set([itemIndex]));
|
|
532
|
-
this.rebuildIndices();
|
|
533
|
-
this.emit("added", newItem);
|
|
534
|
-
this.emit("insert", newItem);
|
|
535
|
-
this.executeInDebugMode((callstack) => this.emit("_debug.insert", callstack, newItem));
|
|
536
|
-
return newItem.id;
|
|
112
|
+
const timeout = this.observerTimeouts.get(this.getKeyForSelector(selector));
|
|
113
|
+
if (timeout)
|
|
114
|
+
clearTimeout(timeout);
|
|
115
|
+
if (activeObservers === 0)
|
|
116
|
+
void this.fetchSelector(selector);
|
|
537
117
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
throw new Error("Collection is disposed");
|
|
547
|
-
if (!items)
|
|
548
|
-
throw new Error("Invalid items");
|
|
549
|
-
if (items.length === 0) {
|
|
550
|
-
return [];
|
|
551
|
-
}
|
|
552
|
-
const ids = [];
|
|
553
|
-
this.batch(() => {
|
|
554
|
-
items.forEach((item) => {
|
|
555
|
-
ids.push(this.insert(item));
|
|
118
|
+
handleObserverDisposal(selector) {
|
|
119
|
+
var _a;
|
|
120
|
+
const currentObservers = ((_a = this.activeObservers.get(this.getKeyForSelector(selector))) == null ? void 0 : _a.count) ?? 0;
|
|
121
|
+
const activeObservers = currentObservers - 1;
|
|
122
|
+
if (activeObservers > 0) {
|
|
123
|
+
this.activeObservers.set(this.getKeyForSelector(selector), {
|
|
124
|
+
selector,
|
|
125
|
+
count: activeObservers
|
|
556
126
|
});
|
|
557
|
-
|
|
558
|
-
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const timeout = this.observerTimeouts.get(this.getKeyForSelector(selector));
|
|
130
|
+
if (timeout)
|
|
131
|
+
clearTimeout(timeout);
|
|
132
|
+
const removeObserver = () => {
|
|
133
|
+
this.activeObservers.delete(this.getKeyForSelector(selector));
|
|
134
|
+
this.itemsCache.delete(this.getKeyForSelector(selector));
|
|
135
|
+
if (!this.triggerReload)
|
|
136
|
+
throw new Error("No triggerReload method found. Looks like your persistence adapter was not registered");
|
|
137
|
+
void this.triggerReload();
|
|
138
|
+
};
|
|
139
|
+
if (this.purgeDelay === 0) {
|
|
140
|
+
removeObserver();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
this.observerTimeouts.set(this.getKeyForSelector(selector), setTimeout(removeObserver, this.purgeDelay));
|
|
559
144
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
updateOne(selector, modifier) {
|
|
568
|
-
if (this.isDisposed)
|
|
569
|
-
throw new Error("Collection is disposed");
|
|
570
|
-
if (!selector)
|
|
571
|
-
throw new Error("Invalid selector");
|
|
572
|
-
if (!modifier)
|
|
573
|
-
throw new Error("Invalid modifier");
|
|
574
|
-
const { item, index } = this.getItemAndIndex(selector);
|
|
575
|
-
if (item == null)
|
|
576
|
-
return 0;
|
|
577
|
-
const modifiedItem = modify(deepClone(item), modifier);
|
|
578
|
-
const existingItem = this.findOne({ id: modifiedItem.id }, { reactive: false });
|
|
579
|
-
if (!isEqual(existingItem, { ...existingItem, id: modifiedItem.id }))
|
|
580
|
-
throw new Error("Item with same id already exists");
|
|
581
|
-
this.memory().splice(index, 1, modifiedItem);
|
|
582
|
-
this.rebuildIndices();
|
|
583
|
-
this.emit("changed", modifiedItem, modifier);
|
|
584
|
-
this.emit("updateOne", selector, modifier);
|
|
585
|
-
this.executeInDebugMode((callstack) => this.emit("_debug.updateOne", callstack, selector, modifier));
|
|
586
|
-
return 1;
|
|
145
|
+
ensureSignal(selector) {
|
|
146
|
+
if (!this.reactivityAdapter)
|
|
147
|
+
throw new Error("No reactivity adapter found");
|
|
148
|
+
if (!this.loadingSignals.has(this.getKeyForSelector(selector))) {
|
|
149
|
+
this.loadingSignals.set(this.getKeyForSelector(selector), createSignal(this.reactivityAdapter.create(), false));
|
|
150
|
+
}
|
|
151
|
+
return this.loadingSignals.get(this.getKeyForSelector(selector));
|
|
587
152
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
* @param modifier - The modifications to apply to the items.
|
|
592
|
-
* @returns The number of items updated.
|
|
593
|
-
* @throws {Error} If the collection is disposed or invalid arguments are provided.
|
|
594
|
-
*/
|
|
595
|
-
updateMany(selector, modifier) {
|
|
596
|
-
if (this.isDisposed)
|
|
597
|
-
throw new Error("Collection is disposed");
|
|
598
|
-
if (!selector)
|
|
599
|
-
throw new Error("Invalid selector");
|
|
600
|
-
if (!modifier)
|
|
601
|
-
throw new Error("Invalid modifier");
|
|
602
|
-
const items = this.getItems(selector);
|
|
603
|
-
const modifiedItems = [];
|
|
604
|
-
items.forEach((item) => {
|
|
605
|
-
const { index } = this.getItemAndIndex({ id: item.id });
|
|
606
|
-
if (index === -1)
|
|
607
|
-
throw new Error("Cannot resolve index for item");
|
|
608
|
-
const modifiedItem = modify(deepClone(item), modifier);
|
|
609
|
-
this.memory().splice(index, 1, modifiedItem);
|
|
610
|
-
modifiedItems.push(modifiedItem);
|
|
611
|
-
});
|
|
612
|
-
this.rebuildIndices();
|
|
613
|
-
modifiedItems.forEach((modifiedItem) => {
|
|
614
|
-
this.emit("changed", modifiedItem, modifier);
|
|
615
|
-
});
|
|
616
|
-
this.emit("updateMany", selector, modifier);
|
|
617
|
-
this.executeInDebugMode((callstack) => this.emit("_debug.updateMany", callstack, selector, modifier));
|
|
618
|
-
return modifiedItems.length;
|
|
153
|
+
setLoading(selector, value) {
|
|
154
|
+
const signal = this.ensureSignal(selector);
|
|
155
|
+
signal.set(value);
|
|
619
156
|
}
|
|
620
157
|
/**
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
* @
|
|
624
|
-
* @
|
|
158
|
+
* Indicates wether a query is currently been loaded
|
|
159
|
+
* ⚡️ this function is reactive!
|
|
160
|
+
* @param selector {Object} Selector of the query
|
|
161
|
+
* @returns The loading state
|
|
625
162
|
*/
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
throw new Error("Invalid selector");
|
|
631
|
-
const { item, index } = this.getItemAndIndex(selector);
|
|
632
|
-
if (item != null) {
|
|
633
|
-
this.memory().splice(index, 1);
|
|
634
|
-
this.deleteFromIdIndex(item.id, index);
|
|
635
|
-
this.rebuildIndices();
|
|
636
|
-
this.emit("removed", item);
|
|
163
|
+
isLoading(selector) {
|
|
164
|
+
const isPushing = this.isPushing();
|
|
165
|
+
if (!selector) {
|
|
166
|
+
return this.isFetchingSignal.get() || isPushing;
|
|
637
167
|
}
|
|
638
|
-
this.
|
|
639
|
-
|
|
640
|
-
return item == null ? 0 : 1;
|
|
168
|
+
const signal = this.ensureSignal(selector);
|
|
169
|
+
return signal.get() || isPushing;
|
|
641
170
|
}
|
|
642
|
-
|
|
643
|
-
* Removes multiple items from the collection that match the given selector.
|
|
644
|
-
* @param selector - The criteria to select the items to remove.
|
|
645
|
-
* @returns The number of items removed.
|
|
646
|
-
* @throws {Error} If the collection is disposed or invalid arguments are provided.
|
|
647
|
-
*/
|
|
648
|
-
removeMany(selector) {
|
|
649
|
-
if (this.isDisposed)
|
|
650
|
-
throw new Error("Collection is disposed");
|
|
651
|
-
if (!selector)
|
|
652
|
-
throw new Error("Invalid selector");
|
|
653
|
-
const items = this.getItems(selector);
|
|
654
|
-
items.forEach((item) => {
|
|
655
|
-
const index = this.memory().findIndex((document) => document === item);
|
|
656
|
-
if (index === -1)
|
|
657
|
-
throw new Error("Cannot resolve index for item");
|
|
658
|
-
this.memory().splice(index, 1);
|
|
659
|
-
this.deleteFromIdIndex(item.id, index);
|
|
660
|
-
this.rebuildIndices();
|
|
661
|
-
});
|
|
662
|
-
items.forEach((item) => {
|
|
663
|
-
this.emit("removed", item);
|
|
664
|
-
});
|
|
665
|
-
this.emit("removeMany", selector);
|
|
666
|
-
this.executeInDebugMode((callstack) => this.emit("_debug.removeMany", callstack, selector));
|
|
667
|
-
return items.length;
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
__publicField(_Collection, "collections", []);
|
|
671
|
-
__publicField(_Collection, "debugMode", false);
|
|
672
|
-
__publicField(_Collection, "batchOperationInProgress", false);
|
|
673
|
-
__publicField(_Collection, "fieldTracking", false);
|
|
674
|
-
__publicField(_Collection, "onCreationCallbacks", []);
|
|
675
|
-
__publicField(_Collection, "onDisposeCallbacks", []);
|
|
676
|
-
/**
|
|
677
|
-
* Enables debug mode for all collections.
|
|
678
|
-
*/
|
|
679
|
-
__publicField(_Collection, "enableDebugMode", () => {
|
|
680
|
-
_Collection.debugMode = true;
|
|
681
|
-
_Collection.collections.forEach((collection) => {
|
|
682
|
-
collection.setDebugMode(true);
|
|
683
|
-
});
|
|
684
|
-
});
|
|
685
|
-
/**
|
|
686
|
-
* Enables field tracking for all collections.
|
|
687
|
-
* @param enable - A boolean indicating whether to enable field tracking.
|
|
688
|
-
*/
|
|
689
|
-
__publicField(_Collection, "setFieldTracking", (enable) => {
|
|
690
|
-
_Collection.fieldTracking = enable;
|
|
691
|
-
_Collection.collections.forEach((collection) => {
|
|
692
|
-
collection.setFieldTracking(enable);
|
|
693
|
-
});
|
|
694
|
-
});
|
|
695
|
-
let Collection = _Collection;
|
|
171
|
+
}
|
|
696
172
|
export {
|
|
697
|
-
|
|
698
|
-
Collection as default
|
|
173
|
+
AutoFetchCollection as default
|
|
699
174
|
};
|