@signaldb/core 2.0.0-beta.4 → 2.0.0-beta.6
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 +27 -21
- package/dist/Collection/Observer.d.ts +2 -0
- package/dist/Collection/index.d.ts +11 -10
- package/dist/Collection/types.d.ts +7 -0
- package/dist/DefaultDataAdapter.d.ts +2 -0
- package/dist/index.cjs.js +8 -5
- package/dist/index.cjs12.js +41 -346
- package/dist/index.cjs13.js +308 -359
- package/dist/index.cjs14.js +387 -177
- package/dist/index.cjs15.js +182 -364
- package/dist/index.cjs16.js +287 -478
- package/dist/index.cjs17.js +563 -136
- package/dist/index.cjs18.js +154 -23
- package/dist/index.cjs19.js +23 -39
- package/dist/index.cjs2.js +1 -1
- package/dist/index.cjs20.js +39 -13
- package/dist/index.cjs21.js +14 -102
- package/dist/index.cjs22.js +93 -118
- package/dist/index.cjs23.js +127 -5
- package/dist/index.cjs24.js +5 -25
- package/dist/index.cjs25.js +24 -7
- package/dist/index.cjs26.js +8 -27
- package/dist/index.cjs27.js +25 -42
- package/dist/index.cjs28.js +44 -6
- package/dist/index.cjs29.js +6 -7
- package/dist/index.cjs3.js +9 -26
- package/dist/index.cjs30.js +7 -3
- package/dist/index.cjs32.js +40 -27
- package/dist/index.cjs33.js +27 -40
- package/dist/index.cjs34.js +5 -0
- package/dist/index.cjs7.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +10 -7
- package/dist/index12.mjs +40 -346
- package/dist/index13.mjs +308 -359
- package/dist/index14.mjs +387 -177
- package/dist/index15.mjs +182 -364
- package/dist/index16.mjs +287 -478
- package/dist/index17.mjs +563 -136
- package/dist/index18.mjs +154 -23
- package/dist/index19.mjs +23 -38
- package/dist/index2.mjs +1 -1
- package/dist/index20.mjs +38 -13
- package/dist/index21.mjs +14 -102
- package/dist/index22.mjs +93 -117
- package/dist/index23.mjs +126 -5
- package/dist/index24.mjs +5 -25
- package/dist/index25.mjs +24 -7
- package/dist/index26.mjs +8 -27
- package/dist/index27.mjs +25 -42
- package/dist/index28.mjs +44 -6
- package/dist/index29.mjs +6 -7
- package/dist/index3.mjs +9 -26
- package/dist/index30.mjs +7 -3
- package/dist/index32.mjs +40 -27
- package/dist/index33.mjs +27 -40
- package/dist/index34.mjs +6 -0
- package/dist/index7.mjs +1 -1
- package/dist/utils/reactiveOrAsync.d.ts +59 -0
- package/package.json +1 -1
package/dist/index.cjs13.js
CHANGED
|
@@ -1,213 +1,69 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
2
|
+
const createIndex = require("./index.cjs22.js");
|
|
3
|
+
const getIndexInfo = require("./index.cjs23.js");
|
|
4
|
+
const deepClone = require("./index.cjs20.js");
|
|
5
|
+
const EventEmitter = require("./index.cjs9.js");
|
|
6
6
|
const isEqual = require("./index.cjs6.js");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
7
|
+
const match = require("./index.cjs24.js");
|
|
8
|
+
const modify = require("./index.cjs7.js");
|
|
9
|
+
const project = require("./index.cjs25.js");
|
|
10
|
+
const queryId = require("./index.cjs21.js");
|
|
11
|
+
const serializeValue = require("./index.cjs11.js");
|
|
12
|
+
const sortItems = require("./index.cjs26.js");
|
|
13
|
+
function hasPendingUpdates(pendingUpdates) {
|
|
14
|
+
return pendingUpdates.added.length > 0 || pendingUpdates.modified.length > 0 || pendingUpdates.removed.length > 0;
|
|
15
|
+
}
|
|
16
|
+
class DefaultDataAdapter {
|
|
17
|
+
items = /* @__PURE__ */ new Map();
|
|
12
18
|
options;
|
|
13
|
-
id;
|
|
14
|
-
onError;
|
|
15
|
-
// Per-collection resources
|
|
16
19
|
storageAdapters = /* @__PURE__ */ new Map();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
collections = /* @__PURE__ */ new Set();
|
|
21
|
+
indices = /* @__PURE__ */ new Map();
|
|
22
|
+
activeQueries = /* @__PURE__ */ new Map();
|
|
23
|
+
queryEmitters = /* @__PURE__ */ new Map();
|
|
24
|
+
queuedQueryUpdates = /* @__PURE__ */ new Map();
|
|
25
|
+
cachedQueryResults = /* @__PURE__ */ new Map();
|
|
21
26
|
constructor(options) {
|
|
22
|
-
this.options = options;
|
|
23
|
-
this.id = options.id || "async-data-adapter";
|
|
24
|
-
this.onError = options.onError ?? ((error) => {
|
|
25
|
-
console.error(error);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
createCollectionBackend(collection, indices) {
|
|
29
|
-
this.collectionIndices.set(collection.name, indices);
|
|
30
|
-
this.queries.set(collection.name, /* @__PURE__ */ new Map());
|
|
31
|
-
this.ensureStorageAdapter(collection.name);
|
|
32
|
-
const ready = (async () => {
|
|
33
|
-
try {
|
|
34
|
-
await this.setupStorage(collection.name, indices);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
this.onError(error);
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
})();
|
|
40
|
-
this.storageAdapterReady.set(collection.name, ready);
|
|
41
|
-
const registerQuery = (selector, options) => {
|
|
42
|
-
const qid = queryId(selector, options);
|
|
43
|
-
const registry = this.queries.get(collection.name);
|
|
44
|
-
if (!registry)
|
|
45
|
-
throw new Error(`Collection ${collection.name} not initialized!`);
|
|
46
|
-
registry.set(qid, {
|
|
47
|
-
selector,
|
|
48
|
-
options,
|
|
49
|
-
items: [],
|
|
50
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
51
|
-
...registry.get(qid),
|
|
52
|
-
state: "active",
|
|
53
|
-
error: null
|
|
54
|
-
});
|
|
55
|
-
void this.fulfillQuery(collection.name, selector, options).catch(this.onError);
|
|
56
|
-
};
|
|
57
|
-
const unregisterQuery = (selector, options) => {
|
|
58
|
-
const qid = queryId(selector, options);
|
|
59
|
-
this.queries.get(collection.name)?.delete(qid);
|
|
60
|
-
};
|
|
61
|
-
const getQueryState = (selector, options) => {
|
|
62
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
63
|
-
return q?.state ?? "active";
|
|
64
|
-
};
|
|
65
|
-
const getQueryError = (selector, options) => {
|
|
66
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
67
|
-
return q?.error ?? null;
|
|
68
|
-
};
|
|
69
|
-
const getQueryResult = (selector, options) => {
|
|
70
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
71
|
-
return q?.items ?? [];
|
|
72
|
-
};
|
|
73
|
-
const onQueryStateChange = (selector, options, callback) => {
|
|
74
|
-
const qid = queryId(selector, options);
|
|
75
|
-
const registry = this.queries.get(collection.name);
|
|
76
|
-
if (!registry)
|
|
77
|
-
throw new Error(`Collection ${collection.name} not initialized!`);
|
|
78
|
-
if (!registry.has(qid)) {
|
|
79
|
-
registry.set(qid, {
|
|
80
|
-
selector,
|
|
81
|
-
options,
|
|
82
|
-
state: "active",
|
|
83
|
-
error: null,
|
|
84
|
-
items: [],
|
|
85
|
-
listeners: /* @__PURE__ */ new Set()
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
registry.get(qid)?.listeners.add(callback);
|
|
89
|
-
return () => registry.get(qid)?.listeners.delete(callback);
|
|
90
|
-
};
|
|
91
|
-
return {
|
|
92
|
-
insert: async (item) => {
|
|
93
|
-
await ready;
|
|
94
|
-
const inserted = await this.insert(collection.name, item);
|
|
95
|
-
return inserted;
|
|
96
|
-
},
|
|
97
|
-
updateOne: async (selector, modifier) => {
|
|
98
|
-
await ready;
|
|
99
|
-
return this.updateOne(collection.name, selector, modifier);
|
|
100
|
-
},
|
|
101
|
-
updateMany: async (selector, modifier) => {
|
|
102
|
-
await ready;
|
|
103
|
-
return this.updateMany(collection.name, selector, modifier);
|
|
104
|
-
},
|
|
105
|
-
replaceOne: async (selector, replacement) => {
|
|
106
|
-
await ready;
|
|
107
|
-
return this.replaceOne(collection.name, selector, replacement);
|
|
108
|
-
},
|
|
109
|
-
removeOne: async (selector) => {
|
|
110
|
-
await ready;
|
|
111
|
-
return this.removeOne(collection.name, selector);
|
|
112
|
-
},
|
|
113
|
-
removeMany: async (selector) => {
|
|
114
|
-
await ready;
|
|
115
|
-
return this.removeMany(collection.name, selector);
|
|
116
|
-
},
|
|
117
|
-
registerQuery,
|
|
118
|
-
unregisterQuery,
|
|
119
|
-
getQueryState,
|
|
120
|
-
getQueryError,
|
|
121
|
-
getQueryResult,
|
|
122
|
-
onQueryStateChange,
|
|
123
|
-
executeQuery: async (selector, options) => {
|
|
124
|
-
await ready;
|
|
125
|
-
return this.executeQuery(collection.name, selector, options);
|
|
126
|
-
},
|
|
127
|
-
dispose: async () => {
|
|
128
|
-
this.storageAdapters.delete(collection.name);
|
|
129
|
-
this.queries.delete(collection.name);
|
|
130
|
-
this.collectionIndices.delete(collection.name);
|
|
131
|
-
this.storageAdapterReady.delete(collection.name);
|
|
132
|
-
},
|
|
133
|
-
isReady: async () => {
|
|
134
|
-
await ready;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
async setupStorage(collectionName, indices) {
|
|
139
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
140
|
-
if (!storage)
|
|
141
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
142
|
-
await storage.setup();
|
|
143
|
-
await Promise.all(indices.map((index) => storage.createIndex(index)));
|
|
27
|
+
this.options = options || {};
|
|
144
28
|
}
|
|
145
29
|
ensureStorageAdapter(name) {
|
|
146
|
-
if (this.storageAdapters.
|
|
30
|
+
if (this.storageAdapters.get(name))
|
|
31
|
+
return;
|
|
32
|
+
if (!this.options.storage)
|
|
147
33
|
return;
|
|
148
34
|
const adapter = this.options.storage(name);
|
|
149
35
|
if (!adapter)
|
|
150
36
|
return;
|
|
151
37
|
this.storageAdapters.set(name, adapter);
|
|
152
38
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const registry = this.queries.get(collectionName);
|
|
162
|
-
if (!registry)
|
|
163
|
-
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
164
|
-
const rec = registry.get(qid);
|
|
165
|
-
if (!rec)
|
|
166
|
-
return;
|
|
167
|
-
this.publishState(collectionName, qid, "active", null);
|
|
168
|
-
try {
|
|
169
|
-
const items = await this.executeQuery(collectionName, selector, options);
|
|
170
|
-
this.publishResult(collectionName, qid, items);
|
|
171
|
-
this.publishState(collectionName, qid, "complete", null);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
this.publishState(collectionName, qid, "error", error);
|
|
174
|
-
}
|
|
39
|
+
rebuildIndices(collection) {
|
|
40
|
+
const items = this.items.get(collection.name);
|
|
41
|
+
if (!items)
|
|
42
|
+
throw new Error(`Items not found for collection ${collection.name}`);
|
|
43
|
+
const indices = this.indices.get(collection.name);
|
|
44
|
+
if (!indices)
|
|
45
|
+
throw new Error(`Indices not found for collection ${collection.name}`);
|
|
46
|
+
indices.forEach((index) => index.rebuild([...items.values()]));
|
|
175
47
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
* @param qid - query id
|
|
180
|
-
* @param state - new state
|
|
181
|
-
* @param error - error if state is 'error', null otherwise
|
|
182
|
-
*/
|
|
183
|
-
publishState(collectionName, qid, state, error) {
|
|
184
|
-
const rec = this.queries.get(collectionName)?.get(qid);
|
|
185
|
-
if (!rec)
|
|
48
|
+
async setupStorageAdapter(collection) {
|
|
49
|
+
const storageAdapter = this.storageAdapters.get(collection.name);
|
|
50
|
+
if (!storageAdapter)
|
|
186
51
|
return;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
this.onError(error_);
|
|
52
|
+
return storageAdapter.setup().then(async () => {
|
|
53
|
+
await this.fetchItemsFromStorage(collection);
|
|
54
|
+
}).catch((error) => {
|
|
55
|
+
if (!this.options.onError) {
|
|
56
|
+
console.error(`Error during data persistence operation in collection ${collection.name}`, error);
|
|
57
|
+
return;
|
|
194
58
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
publishResult(collectionName, qid, items) {
|
|
198
|
-
const rec = this.queries.get(collectionName)?.get(qid);
|
|
199
|
-
if (!rec)
|
|
200
|
-
return;
|
|
201
|
-
rec.items = items;
|
|
59
|
+
this.options.onError(collection.name, error instanceof Error ? error : new Error(error));
|
|
60
|
+
});
|
|
202
61
|
}
|
|
203
|
-
|
|
204
|
-
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
205
|
-
if (!storageAdapter)
|
|
206
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
62
|
+
getIndexInfo(collection, selector) {
|
|
207
63
|
if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
|
|
208
64
|
return {
|
|
209
65
|
matched: true,
|
|
210
|
-
ids: [selector.id]
|
|
66
|
+
ids: [serializeValue(selector.id)],
|
|
211
67
|
optimizedSelector: {}
|
|
212
68
|
};
|
|
213
69
|
}
|
|
@@ -218,68 +74,49 @@ class AsyncDataAdapter {
|
|
|
218
74
|
optimizedSelector: {}
|
|
219
75
|
};
|
|
220
76
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
for (const id of idSet)
|
|
241
|
-
includedIds.push(id);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
if (keys.exclude != null) {
|
|
245
|
-
const excludeIds = /* @__PURE__ */ new Set();
|
|
246
|
-
for (const key of keys.exclude) {
|
|
247
|
-
const idSet = index.get(key);
|
|
248
|
-
if (idSet)
|
|
249
|
-
for (const id of idSet)
|
|
250
|
-
excludeIds.add(id);
|
|
251
|
-
}
|
|
252
|
-
includedIds = includedIds.filter((pos) => !excludeIds.has(pos));
|
|
253
|
-
}
|
|
254
|
-
return { matched: true, ids: includedIds, fields: [field], keepSelector: filtersForNull };
|
|
255
|
-
}), selector);
|
|
77
|
+
return getIndexInfo.default((this.indices.get(collection.name) ?? []).map((i) => i.query.bind(i)), selector);
|
|
78
|
+
}
|
|
79
|
+
applyIndexDeltas(collection, changes) {
|
|
80
|
+
const indices = this.indices.get(collection.name) ?? [];
|
|
81
|
+
for (const index of indices) {
|
|
82
|
+
if (changes.modified)
|
|
83
|
+
index.update(changes.modified);
|
|
84
|
+
if (changes.added && changes.added.length > 0)
|
|
85
|
+
index.insert(changes.added);
|
|
86
|
+
if (changes.removed && changes.removed.length > 0)
|
|
87
|
+
index.remove(changes.removed);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
getItem(collection, selector) {
|
|
91
|
+
const memory = this.items.get(collection.name) ?? /* @__PURE__ */ new Map();
|
|
92
|
+
const indexInfo = this.getIndexInfo(collection, selector);
|
|
93
|
+
const items = indexInfo.matched ? indexInfo.ids.map((id) => memory.get(serializeValue(id))).filter((i) => i != null) : [...memory.values()];
|
|
94
|
+
const item = items.find((document) => match(document, selector));
|
|
95
|
+
return item;
|
|
256
96
|
}
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
if (!storage)
|
|
260
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
261
|
-
const index = await this.getIndexInfo(collectionName, selector);
|
|
97
|
+
queryItems(collection, selector) {
|
|
98
|
+
const indexInfo = this.getIndexInfo(collection, selector);
|
|
262
99
|
const matchItems = (item) => {
|
|
263
|
-
if (
|
|
100
|
+
if (indexInfo.optimizedSelector == null)
|
|
264
101
|
return true;
|
|
265
|
-
if (Object.keys(
|
|
102
|
+
if (Object.keys(indexInfo.optimizedSelector).length <= 0)
|
|
266
103
|
return true;
|
|
267
|
-
|
|
104
|
+
const matches = match(item, indexInfo.optimizedSelector);
|
|
105
|
+
return matches;
|
|
268
106
|
};
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (isEqual(index.optimizedSelector, {}))
|
|
272
|
-
return items;
|
|
273
|
-
return items.filter(matchItems);
|
|
274
|
-
} else {
|
|
275
|
-
const allItems = await storage.readAll();
|
|
107
|
+
const items = this.items.get(collection.name);
|
|
108
|
+
if (!indexInfo.matched) {
|
|
276
109
|
if (isEqual(selector, {}))
|
|
277
|
-
return
|
|
278
|
-
return
|
|
110
|
+
return [...items.values()];
|
|
111
|
+
return [...items.values()].filter(matchItems);
|
|
279
112
|
}
|
|
113
|
+
const foundItems = indexInfo.ids.map((ids) => items.get(serializeValue(ids))).filter((i) => i != null);
|
|
114
|
+
if (isEqual(indexInfo.optimizedSelector, {}))
|
|
115
|
+
return foundItems;
|
|
116
|
+
return foundItems.filter(matchItems);
|
|
280
117
|
}
|
|
281
|
-
|
|
282
|
-
const items =
|
|
118
|
+
executeQuery(collection, selector, options) {
|
|
119
|
+
const items = this.queryItems(collection, selector || {});
|
|
283
120
|
const { sort, skip, limit, fields } = options || {};
|
|
284
121
|
const sorted = sort ? sortItems(items, sort) : items;
|
|
285
122
|
const skipped = skip ? sorted.slice(skip) : sorted;
|
|
@@ -288,129 +125,241 @@ class AsyncDataAdapter {
|
|
|
288
125
|
return limited.map((item) => {
|
|
289
126
|
if (!fields)
|
|
290
127
|
return item;
|
|
291
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
...idExcluded ? {} : { id: item.id },
|
|
130
|
+
...project(item, fields)
|
|
131
|
+
};
|
|
292
132
|
});
|
|
293
133
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
* @param collectionName - name of the collection
|
|
297
|
-
* @param changedItems - items that were inserted/updated/replaced/removed
|
|
298
|
-
*/
|
|
299
|
-
async checkQueryUpdates(collectionName, changedItems) {
|
|
300
|
-
const registry = this.queries.get(collectionName);
|
|
301
|
-
if (!registry)
|
|
302
|
-
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
303
|
-
if (registry.size === 0)
|
|
134
|
+
flushQueuedQueryUpdates(collection) {
|
|
135
|
+
if (!this.queuedQueryUpdates.get(collection.name))
|
|
304
136
|
return;
|
|
305
|
-
const
|
|
306
|
-
if (
|
|
137
|
+
const changes = this.queuedQueryUpdates.get(collection.name);
|
|
138
|
+
if (!changes || !hasPendingUpdates(changes))
|
|
307
139
|
return;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
})
|
|
140
|
+
this.queuedQueryUpdates.set(collection.name, { added: [], modified: [], removed: [] });
|
|
141
|
+
const flatItems = [...changes.added, ...changes.modified, ...changes.removed];
|
|
142
|
+
const itemIds = new Set(flatItems.map((i) => i.id));
|
|
143
|
+
const queries = [
|
|
144
|
+
...this.activeQueries.get(collection.name)?.values() ?? []
|
|
145
|
+
].filter(({ selector, options }) => {
|
|
146
|
+
const idsInQuery = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options))?.map((i) => i.id) ?? [];
|
|
147
|
+
if (idsInQuery.some((id) => itemIds.has(id)))
|
|
148
|
+
return true;
|
|
149
|
+
return flatItems.some((item) => match(item, selector));
|
|
150
|
+
});
|
|
151
|
+
queries.forEach(({ selector, options }) => {
|
|
152
|
+
this.executeAndCacheQuery(collection, selector, options);
|
|
153
|
+
});
|
|
322
154
|
}
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
await this.checkQueryUpdates(collectionName, [newItem]);
|
|
332
|
-
return newItem;
|
|
155
|
+
executeAndCacheQuery(collection, selector, options) {
|
|
156
|
+
const result = this.executeQuery(collection, selector, options);
|
|
157
|
+
this.cachedQueryResults.set(collection.name, this.cachedQueryResults.get(collection.name) || /* @__PURE__ */ new Map());
|
|
158
|
+
this.cachedQueryResults.get(collection.name)?.set(queryId(selector, options), result);
|
|
159
|
+
const emitter = this.queryEmitters.get(collection.name);
|
|
160
|
+
if (!emitter)
|
|
161
|
+
return;
|
|
162
|
+
emitter.emit("change", selector, options, "complete");
|
|
333
163
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const { $setOnInsert, ...rest } = modifier;
|
|
341
|
-
if (item == null)
|
|
342
|
-
return [];
|
|
343
|
-
const modified = modify(deepClone.default(item), rest);
|
|
344
|
-
if (item.id !== modified.id) {
|
|
345
|
-
const existing = await storage.readIds([modified.id]);
|
|
346
|
-
if (existing.length > 0)
|
|
347
|
-
throw new Error(`Item with id ${String(modified.id)} already exists`);
|
|
348
|
-
}
|
|
349
|
-
await storage.replace([modified]);
|
|
350
|
-
await this.checkQueryUpdates(collectionName, [modified]);
|
|
351
|
-
return [modified];
|
|
164
|
+
updateQueries(collection, changes) {
|
|
165
|
+
this.queuedQueryUpdates.set(collection.name, this.queuedQueryUpdates.get(collection.name) || { added: [], modified: [], removed: [] });
|
|
166
|
+
this.queuedQueryUpdates.get(collection.name)?.added.push(...changes.added);
|
|
167
|
+
this.queuedQueryUpdates.get(collection.name)?.modified.push(...changes.modified);
|
|
168
|
+
this.queuedQueryUpdates.get(collection.name)?.removed.push(...changes.removed);
|
|
169
|
+
this.flushQueuedQueryUpdates(collection);
|
|
352
170
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
171
|
+
createCollectionBackend(collection, indices) {
|
|
172
|
+
this.collections.add(collection);
|
|
173
|
+
this.ensureStorageAdapter(collection.name);
|
|
174
|
+
this.items.set(collection.name, this.items.get(collection.name) ?? /* @__PURE__ */ new Map());
|
|
175
|
+
this.queryEmitters.set(collection.name, this.queryEmitters.get(collection.name) ?? new EventEmitter());
|
|
176
|
+
this.queryEmitters.get(collection.name)?.setMaxListeners(Infinity);
|
|
177
|
+
this.activeQueries.set(collection.name, this.activeQueries.get(collection.name) || /* @__PURE__ */ new Map());
|
|
178
|
+
this.indices.set(collection.name, indices.map((field) => createIndex(field)));
|
|
179
|
+
this.rebuildIndices(collection);
|
|
180
|
+
const persistenceReadyPromise = this.setupStorageAdapter(collection);
|
|
181
|
+
const backend = {
|
|
182
|
+
// CRUD operations
|
|
183
|
+
insert: async (newItem) => {
|
|
184
|
+
const items = this.items.get(collection.name);
|
|
185
|
+
if (!items)
|
|
186
|
+
throw new Error(`Items not found for collection ${collection.name}`);
|
|
187
|
+
if (items.has(serializeValue(newItem.id))) {
|
|
188
|
+
throw new Error(`Item with id '${newItem.id}' already exists`);
|
|
189
|
+
}
|
|
190
|
+
this.items.get(collection.name)?.set(serializeValue(newItem.id), newItem);
|
|
191
|
+
await this.storageAdapters.get(collection.name)?.insert([newItem]);
|
|
192
|
+
this.applyIndexDeltas(collection, { added: [newItem] });
|
|
193
|
+
this.updateQueries(collection, {
|
|
194
|
+
added: [],
|
|
195
|
+
modified: [newItem],
|
|
196
|
+
removed: []
|
|
197
|
+
});
|
|
198
|
+
return newItem;
|
|
199
|
+
},
|
|
200
|
+
updateOne: async (selector, modifier) => {
|
|
201
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
202
|
+
const item = this.getItem(collection, selector);
|
|
203
|
+
if (item == null)
|
|
204
|
+
return [];
|
|
205
|
+
const modifiedItem = modify(deepClone.default(item), restModifier);
|
|
206
|
+
const hasItemWithSameId = item.id !== modifiedItem.id && this.getItem(collection, { id: modifiedItem.id }) != null;
|
|
207
|
+
if (hasItemWithSameId) {
|
|
208
|
+
throw new Error(`Item with id '${modifiedItem.id}' already exists`);
|
|
209
|
+
}
|
|
210
|
+
this.items.get(collection.name)?.set(serializeValue(modifiedItem.id), modifiedItem);
|
|
211
|
+
await this.storageAdapters.get(collection.name)?.replace([modifiedItem]);
|
|
212
|
+
this.applyIndexDeltas(collection, { modified: [{ oldItem: item, newItem: modifiedItem }] });
|
|
213
|
+
this.updateQueries(collection, {
|
|
214
|
+
added: [],
|
|
215
|
+
modified: [modifiedItem],
|
|
216
|
+
removed: []
|
|
217
|
+
});
|
|
218
|
+
return [modifiedItem];
|
|
219
|
+
},
|
|
220
|
+
updateMany: async (selector, modifier) => {
|
|
221
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
222
|
+
const items = this.executeQuery(collection, selector, {});
|
|
223
|
+
const changedItems = items.map((item) => {
|
|
224
|
+
const modifiedItem = modify(deepClone.default(item), restModifier);
|
|
225
|
+
const hasItemWithSameId = item.id !== modifiedItem.id && this.getItem(collection, { id: modifiedItem.id }) != null;
|
|
226
|
+
if (hasItemWithSameId) {
|
|
227
|
+
throw new Error(`Item with id '${modifiedItem.id}' already exists`);
|
|
228
|
+
}
|
|
229
|
+
return modifiedItem;
|
|
230
|
+
});
|
|
231
|
+
changedItems.forEach((item) => {
|
|
232
|
+
this.items.get(collection.name)?.set(serializeValue(item.id), item);
|
|
233
|
+
});
|
|
234
|
+
await this.storageAdapters.get(collection.name)?.replace(changedItems);
|
|
235
|
+
const pairs = items.map((oldItem, index) => ({ oldItem, newItem: changedItems[index] }));
|
|
236
|
+
this.applyIndexDeltas(collection, { modified: pairs });
|
|
237
|
+
this.updateQueries(collection, {
|
|
238
|
+
added: [],
|
|
239
|
+
modified: changedItems,
|
|
240
|
+
removed: []
|
|
241
|
+
});
|
|
242
|
+
return changedItems;
|
|
243
|
+
},
|
|
244
|
+
replaceOne: async (selector, replacement) => {
|
|
245
|
+
const item = this.getItem(collection, selector);
|
|
246
|
+
if (item == null)
|
|
247
|
+
return [];
|
|
248
|
+
const hasItemWithSameId = item.id !== replacement.id && replacement.id != null && this.getItem(collection, { id: replacement.id }) != null;
|
|
249
|
+
if (hasItemWithSameId) {
|
|
250
|
+
throw new Error(`Item with id '${replacement.id}' already exists`);
|
|
251
|
+
}
|
|
252
|
+
const modifiedItem = { id: item.id, ...replacement };
|
|
253
|
+
this.items.get(collection.name)?.set(serializeValue(modifiedItem.id), modifiedItem);
|
|
254
|
+
await this.storageAdapters.get(collection.name)?.replace([modifiedItem]);
|
|
255
|
+
this.applyIndexDeltas(collection, { modified: [{ oldItem: item, newItem: modifiedItem }] });
|
|
256
|
+
this.updateQueries(collection, {
|
|
257
|
+
added: [],
|
|
258
|
+
modified: [modifiedItem],
|
|
259
|
+
removed: []
|
|
260
|
+
});
|
|
261
|
+
return [modifiedItem];
|
|
262
|
+
},
|
|
263
|
+
removeOne: async (selector) => {
|
|
264
|
+
const item = this.getItem(collection, selector);
|
|
265
|
+
if (item == null)
|
|
266
|
+
return [];
|
|
267
|
+
this.items.get(collection.name)?.delete(serializeValue(item.id));
|
|
268
|
+
await this.storageAdapters.get(collection.name)?.remove([item]);
|
|
269
|
+
this.applyIndexDeltas(collection, { removed: [item] });
|
|
270
|
+
this.updateQueries(collection, {
|
|
271
|
+
added: [],
|
|
272
|
+
modified: [],
|
|
273
|
+
removed: [item]
|
|
274
|
+
});
|
|
275
|
+
return [item];
|
|
276
|
+
},
|
|
277
|
+
removeMany: async (selector) => {
|
|
278
|
+
const items = backend.getQueryResult(selector, {});
|
|
279
|
+
items.forEach((item) => {
|
|
280
|
+
this.items.get(collection.name)?.delete(serializeValue(item.id));
|
|
281
|
+
});
|
|
282
|
+
await this.storageAdapters.get(collection.name)?.remove(items);
|
|
283
|
+
this.applyIndexDeltas(collection, { removed: items });
|
|
284
|
+
this.updateQueries(collection, {
|
|
285
|
+
added: [],
|
|
286
|
+
modified: [],
|
|
287
|
+
removed: items
|
|
288
|
+
});
|
|
289
|
+
return items;
|
|
290
|
+
},
|
|
291
|
+
// methods for registering and unregistering queries that will be called from the collection during find/findOne
|
|
292
|
+
registerQuery: (selector, options) => {
|
|
293
|
+
this.activeQueries.set(collection.name, this.activeQueries.get(collection.name) || /* @__PURE__ */ new Map());
|
|
294
|
+
this.activeQueries.get(collection.name)?.set(queryId(selector, options), {
|
|
295
|
+
selector,
|
|
296
|
+
options
|
|
297
|
+
});
|
|
298
|
+
this.executeAndCacheQuery(collection, selector, options);
|
|
299
|
+
},
|
|
300
|
+
unregisterQuery: (selector, options) => {
|
|
301
|
+
if (!this.activeQueries.get(collection.name))
|
|
302
|
+
return;
|
|
303
|
+
this.activeQueries.get(collection.name)?.delete(queryId(selector, options));
|
|
304
|
+
},
|
|
305
|
+
getQueryState: () => "complete",
|
|
306
|
+
onQueryStateChange: (selector, options, callback) => {
|
|
307
|
+
const emitter = this.queryEmitters.get(collection.name);
|
|
308
|
+
if (!emitter)
|
|
309
|
+
throw new Error(`Query emitter not found for collection ${collection.name}`);
|
|
310
|
+
const handler = (querySelector, queryOptions, state) => {
|
|
311
|
+
if (queryId(querySelector, queryOptions) !== queryId(selector, options))
|
|
312
|
+
return;
|
|
313
|
+
callback(state);
|
|
314
|
+
};
|
|
315
|
+
emitter.on("change", handler);
|
|
316
|
+
return () => {
|
|
317
|
+
emitter.off("change", handler);
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
getQueryError: () => null,
|
|
321
|
+
getQueryResult: (selector, options) => {
|
|
322
|
+
const isQueryActive = this.activeQueries.get(collection.name)?.has(queryId(selector, options));
|
|
323
|
+
if (isQueryActive) {
|
|
324
|
+
const results = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options));
|
|
325
|
+
if (!results)
|
|
326
|
+
throw new Error("Cached query results are not defined!");
|
|
327
|
+
return results;
|
|
328
|
+
}
|
|
329
|
+
return this.executeQuery(collection, selector, options);
|
|
330
|
+
},
|
|
331
|
+
executeQuery: (selector, options) => Promise.resolve(this.executeQuery(collection, selector, options)),
|
|
332
|
+
// lifecycle methods
|
|
333
|
+
dispose: async () => {
|
|
334
|
+
const adapter = this.storageAdapters.get(collection.name);
|
|
335
|
+
if (adapter?.teardown)
|
|
336
|
+
await adapter.teardown();
|
|
337
|
+
this.storageAdapters.delete(collection.name);
|
|
338
|
+
this.items.delete(collection.name);
|
|
339
|
+
this.indices.delete(collection.name);
|
|
340
|
+
this.activeQueries.delete(collection.name);
|
|
341
|
+
this.queryEmitters.delete(collection.name);
|
|
342
|
+
this.queuedQueryUpdates.delete(collection.name);
|
|
343
|
+
this.cachedQueryResults.delete(collection.name);
|
|
344
|
+
},
|
|
345
|
+
isReady: () => persistenceReadyPromise
|
|
346
|
+
};
|
|
347
|
+
return backend;
|
|
373
348
|
}
|
|
374
|
-
async
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const items = await this.executeQuery(collectionName, selector, { limit: 1 });
|
|
379
|
-
const [item] = items;
|
|
380
|
-
if (item == null)
|
|
381
|
-
return [];
|
|
382
|
-
const modified = { ...replacement, id: replacement.id ?? item.id };
|
|
383
|
-
if (item.id !== modified.id) {
|
|
384
|
-
const existing = await storage.readIds([modified.id]);
|
|
385
|
-
if (existing.length > 0)
|
|
386
|
-
throw new Error(`Item with id ${String(modified.id)} already exists`);
|
|
349
|
+
async fetchItemsFromStorage(collection) {
|
|
350
|
+
if (!collection) {
|
|
351
|
+
await Promise.all([...this.collections].map((currentCollection) => this.fetchItemsFromStorage(currentCollection)));
|
|
352
|
+
return;
|
|
387
353
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const [item] = items;
|
|
398
|
-
if (item == null)
|
|
399
|
-
return [];
|
|
400
|
-
await storage.remove([item]);
|
|
401
|
-
await this.checkQueryUpdates(collectionName, [item]);
|
|
402
|
-
return [item];
|
|
403
|
-
}
|
|
404
|
-
async removeMany(collectionName, selector) {
|
|
405
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
406
|
-
if (!storage)
|
|
407
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
408
|
-
const items = await this.executeQuery(collectionName, selector);
|
|
409
|
-
if (items.length === 0)
|
|
410
|
-
return [];
|
|
411
|
-
await storage.remove(items);
|
|
412
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
413
|
-
return items;
|
|
354
|
+
const storageAdapter = this.storageAdapters.get(collection.name);
|
|
355
|
+
if (!storageAdapter)
|
|
356
|
+
return;
|
|
357
|
+
const items = await storageAdapter.readAll();
|
|
358
|
+
this.items.set(collection.name, items.reduce((map, item) => {
|
|
359
|
+
map.set(serializeValue(item.id), item);
|
|
360
|
+
return map;
|
|
361
|
+
}, /* @__PURE__ */ new Map()));
|
|
362
|
+
this.rebuildIndices(collection);
|
|
414
363
|
}
|
|
415
364
|
}
|
|
416
|
-
module.exports =
|
|
365
|
+
module.exports = DefaultDataAdapter;
|