@signaldb/core 2.0.0-beta.4 → 2.0.0-beta.5
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 +5 -3
- package/dist/index.cjs31.js +40 -5
- package/dist/index.cjs33.js +3 -40
- package/dist/index.cjs34.js +9 -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 +5 -3
- package/dist/index31.mjs +40 -5
- package/dist/index33.mjs +3 -40
- package/dist/index34.mjs +10 -0
- package/dist/index7.mjs +1 -1
- package/dist/utils/reactiveOrAsync.d.ts +59 -0
- package/package.json +1 -1
package/dist/index.cjs16.js
CHANGED
|
@@ -1,386 +1,124 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const deepClone = require("./index.
|
|
3
|
-
const match = require("./index.
|
|
2
|
+
const deepClone = require("./index.cjs20.js");
|
|
3
|
+
const match = require("./index.cjs24.js");
|
|
4
4
|
const modify = require("./index.cjs7.js");
|
|
5
|
-
const queryId = require("./index.
|
|
5
|
+
const queryId = require("./index.cjs21.js");
|
|
6
6
|
const isEqual = require("./index.cjs6.js");
|
|
7
|
-
const getIndexInfo = require("./index.
|
|
8
|
-
const getMatchingKeys = require("./index.
|
|
9
|
-
const sortItems = require("./index.
|
|
10
|
-
const project = require("./index.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function selectorId(selector) {
|
|
15
|
-
return JSON.stringify(selector ?? {});
|
|
16
|
-
}
|
|
17
|
-
class AutoFetchDataAdapter {
|
|
7
|
+
const getIndexInfo = require("./index.cjs23.js");
|
|
8
|
+
const getMatchingKeys = require("./index.cjs27.js");
|
|
9
|
+
const sortItems = require("./index.cjs26.js");
|
|
10
|
+
const project = require("./index.cjs25.js");
|
|
11
|
+
const compact = require("./index.cjs29.js");
|
|
12
|
+
class WorkerDataAdapterHost {
|
|
13
|
+
workerContext;
|
|
18
14
|
options;
|
|
19
15
|
id;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
mergeItems;
|
|
23
|
-
purgeDelay;
|
|
24
|
-
// Per-collection resources
|
|
16
|
+
log = () => {
|
|
17
|
+
};
|
|
25
18
|
storageAdapters = /* @__PURE__ */ new Map();
|
|
26
19
|
storageAdapterReady = /* @__PURE__ */ new Map();
|
|
27
20
|
collectionIndices = /* @__PURE__ */ new Map();
|
|
28
|
-
// Per-collection query registries and auto-fetch bookkeeping
|
|
29
21
|
queries = /* @__PURE__ */ new Map();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// per-collection: selectorKey -> Set<id>
|
|
36
|
-
idRefCounts = /* @__PURE__ */ new Map();
|
|
37
|
-
// per-collection: id -> refcount (for auto-fetched items)
|
|
38
|
-
autoloadIds = /* @__PURE__ */ new Map();
|
|
39
|
-
// per-collection: ids that were introduced via auto-fetch
|
|
40
|
-
constructor(options) {
|
|
22
|
+
onError = (error) => {
|
|
23
|
+
console.error(error);
|
|
24
|
+
};
|
|
25
|
+
constructor(workerContext, options) {
|
|
26
|
+
this.workerContext = workerContext;
|
|
41
27
|
this.options = options;
|
|
42
|
-
this.id = options.id || "
|
|
43
|
-
this.
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
this.fetchQueryItems = options.fetchQueryItems;
|
|
47
|
-
this.mergeItems = options.mergeItems ?? defaultMergeItems;
|
|
48
|
-
this.purgeDelay = options.purgeDelay ?? 1e4;
|
|
49
|
-
if (options.registerRemoteChange) {
|
|
50
|
-
void options.registerRemoteChange(async () => {
|
|
51
|
-
await this.forceRefetchAll();
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
createCollectionBackend(collection, indices) {
|
|
56
|
-
this.collectionIndices.set(collection.name, indices);
|
|
57
|
-
this.queries.set(collection.name, /* @__PURE__ */ new Map());
|
|
58
|
-
this.ensureStorageAdapter(collection.name);
|
|
59
|
-
this.activeObservers.set(collection.name, /* @__PURE__ */ new Map());
|
|
60
|
-
this.observerTimeouts.set(collection.name, /* @__PURE__ */ new Map());
|
|
61
|
-
this.selectorIds.set(collection.name, /* @__PURE__ */ new Map());
|
|
62
|
-
this.idRefCounts.set(collection.name, /* @__PURE__ */ new Map());
|
|
63
|
-
this.autoloadIds.set(collection.name, /* @__PURE__ */ new Set());
|
|
64
|
-
const ready = this.setupStorage(collection.name, indices);
|
|
65
|
-
this.storageAdapterReady.set(collection.name, ready);
|
|
66
|
-
const registerQuery = (selector, options) => {
|
|
67
|
-
const qid = queryId(selector, options);
|
|
68
|
-
const registry = this.queries.get(collection.name);
|
|
69
|
-
if (!registry)
|
|
70
|
-
throw new Error(`Collection ${collection.name} not initialized!`);
|
|
71
|
-
registry.set(qid, {
|
|
72
|
-
selector,
|
|
73
|
-
options,
|
|
74
|
-
items: [],
|
|
75
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
76
|
-
...registry.get(qid),
|
|
77
|
-
state: "active",
|
|
78
|
-
error: null
|
|
79
|
-
});
|
|
80
|
-
const key = selectorId(selector);
|
|
81
|
-
const perColObservers = this.activeObservers.get(collection.name);
|
|
82
|
-
const current = perColObservers?.get(key)?.count ?? 0;
|
|
83
|
-
perColObservers?.set(key, { selector, count: current + 1 });
|
|
84
|
-
const timeouts = this.observerTimeouts.get(collection.name);
|
|
85
|
-
const t = timeouts?.get(key);
|
|
86
|
-
if (t)
|
|
87
|
-
clearTimeout(t);
|
|
88
|
-
if (current === 0) {
|
|
89
|
-
void this.fetchAndIngest(collection.name, selector).catch(this.onError);
|
|
90
|
-
}
|
|
91
|
-
void this.fulfillQuery(collection.name, selector, options).catch(this.onError);
|
|
92
|
-
};
|
|
93
|
-
const unregisterQuery = (selector, options) => {
|
|
94
|
-
const qid = queryId(selector, options);
|
|
95
|
-
const registry = this.queries.get(collection.name);
|
|
96
|
-
registry?.delete(qid);
|
|
97
|
-
const key = selectorId(selector);
|
|
98
|
-
const perColObservers = this.activeObservers.get(collection.name);
|
|
99
|
-
const current = perColObservers?.get(key)?.count ?? 0;
|
|
100
|
-
const remaining = Math.max(0, current - 1);
|
|
101
|
-
if (remaining > 0) {
|
|
102
|
-
perColObservers?.set(key, { selector, count: remaining });
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const doPurge = () => {
|
|
106
|
-
perColObservers?.delete(key);
|
|
107
|
-
void this.purgeSelector(collection.name, selector).catch(this.onError);
|
|
108
|
-
};
|
|
109
|
-
if (this.purgeDelay === 0) {
|
|
110
|
-
doPurge();
|
|
111
|
-
} else {
|
|
112
|
-
const timeouts = this.observerTimeouts.get(collection.name);
|
|
113
|
-
const t = timeouts?.get(key);
|
|
114
|
-
if (t)
|
|
115
|
-
clearTimeout(t);
|
|
116
|
-
timeouts?.set(key, setTimeout(doPurge, this.purgeDelay));
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
const getQueryState = (selector, options) => {
|
|
120
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
121
|
-
return q?.state ?? "active";
|
|
122
|
-
};
|
|
123
|
-
const getQueryError = (selector, options) => {
|
|
124
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
125
|
-
return q?.error ?? null;
|
|
126
|
-
};
|
|
127
|
-
const getQueryResult = (selector, options) => {
|
|
128
|
-
const q = this.queries.get(collection.name)?.get(queryId(selector, options));
|
|
129
|
-
return q?.items ?? [];
|
|
130
|
-
};
|
|
131
|
-
const onQueryStateChange = (selector, options, callback) => {
|
|
132
|
-
const qid = queryId(selector, options);
|
|
133
|
-
const registry = this.queries.get(collection.name);
|
|
134
|
-
if (!registry)
|
|
135
|
-
throw new Error(`Collection ${collection.name} not initialized!`);
|
|
136
|
-
if (!registry.has(qid)) {
|
|
137
|
-
registry.set(qid, {
|
|
138
|
-
selector,
|
|
139
|
-
options,
|
|
140
|
-
state: "active",
|
|
141
|
-
error: null,
|
|
142
|
-
items: [],
|
|
143
|
-
listeners: /* @__PURE__ */ new Set()
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
registry.get(qid)?.listeners.add(callback);
|
|
147
|
-
return () => {
|
|
148
|
-
registry.get(qid)?.listeners.delete(callback);
|
|
149
|
-
};
|
|
150
|
-
};
|
|
151
|
-
return {
|
|
152
|
-
insert: async (item) => {
|
|
153
|
-
await ready;
|
|
154
|
-
const inserted = await this.insert(collection.name, item);
|
|
155
|
-
return inserted;
|
|
156
|
-
},
|
|
157
|
-
updateOne: async (selector, modifier) => {
|
|
158
|
-
await ready;
|
|
159
|
-
return this.updateOne(collection.name, selector, modifier);
|
|
160
|
-
},
|
|
161
|
-
updateMany: async (selector, modifier) => {
|
|
162
|
-
await ready;
|
|
163
|
-
return this.updateMany(collection.name, selector, modifier);
|
|
164
|
-
},
|
|
165
|
-
replaceOne: async (selector, replacement) => {
|
|
166
|
-
await ready;
|
|
167
|
-
return this.replaceOne(collection.name, selector, replacement);
|
|
168
|
-
},
|
|
169
|
-
removeOne: async (selector) => {
|
|
170
|
-
await ready;
|
|
171
|
-
return this.removeOne(collection.name, selector);
|
|
172
|
-
},
|
|
173
|
-
removeMany: async (selector) => {
|
|
174
|
-
await ready;
|
|
175
|
-
return this.removeMany(collection.name, selector);
|
|
176
|
-
},
|
|
177
|
-
registerQuery,
|
|
178
|
-
unregisterQuery,
|
|
179
|
-
getQueryState,
|
|
180
|
-
getQueryError,
|
|
181
|
-
getQueryResult,
|
|
182
|
-
onQueryStateChange,
|
|
183
|
-
executeQuery: async (selector, options) => {
|
|
184
|
-
await ready;
|
|
185
|
-
registerQuery(selector, options);
|
|
186
|
-
await new Promise((resolve) => {
|
|
187
|
-
let stop = () => {
|
|
188
|
-
};
|
|
189
|
-
stop = onQueryStateChange(selector, options, (state) => {
|
|
190
|
-
if (state === "active")
|
|
191
|
-
return;
|
|
192
|
-
resolve();
|
|
193
|
-
stop();
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
const result = getQueryResult(selector, options);
|
|
197
|
-
unregisterQuery(selector, options);
|
|
198
|
-
return result;
|
|
199
|
-
},
|
|
200
|
-
dispose: async () => {
|
|
201
|
-
this.storageAdapters.delete(collection.name);
|
|
202
|
-
this.queries.delete(collection.name);
|
|
203
|
-
this.collectionIndices.delete(collection.name);
|
|
204
|
-
this.storageAdapterReady.delete(collection.name);
|
|
205
|
-
this.activeObservers.delete(collection.name);
|
|
206
|
-
this.observerTimeouts.delete(collection.name);
|
|
207
|
-
this.selectorIds.delete(collection.name);
|
|
208
|
-
this.idRefCounts.delete(collection.name);
|
|
209
|
-
this.autoloadIds.delete(collection.name);
|
|
210
|
-
},
|
|
211
|
-
isReady: async () => {
|
|
212
|
-
await ready;
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
// ===== Auto-fetch mechanics =====
|
|
217
|
-
async forceRefetchAll() {
|
|
218
|
-
const tasks = [];
|
|
219
|
-
for (const [collectionName, observers] of this.activeObservers.entries()) {
|
|
220
|
-
for (const { selector, count } of observers.values()) {
|
|
221
|
-
if (count > 0)
|
|
222
|
-
tasks.push(this.fetchAndIngest(collectionName, selector));
|
|
223
|
-
}
|
|
28
|
+
this.id = this.options.id || "default-worker-data-adapter";
|
|
29
|
+
if (this.options.onError) {
|
|
30
|
+
this.onError = this.options.onError;
|
|
224
31
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
try {
|
|
230
|
-
const items = await this.fetchQueryItems(collectionName, selector);
|
|
231
|
-
if (!items || !Array.isArray(items)) {
|
|
232
|
-
throw new Error("AutoFetchDataAdapter: fetchQueryItems must resolve to { items: T[] }");
|
|
233
|
-
}
|
|
234
|
-
const ids = items.map((i) => i.id);
|
|
235
|
-
const selectorKey = selectorId(selector);
|
|
236
|
-
const selMap = this.selectorIds.get(collectionName);
|
|
237
|
-
const previous = selMap?.get(selectorKey) ?? /* @__PURE__ */ new Set();
|
|
238
|
-
ids.forEach((id) => previous.add(id));
|
|
239
|
-
selMap?.set(selectorKey, previous);
|
|
240
|
-
await this.upsertMerged(collectionName, items);
|
|
241
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
this.publishForSelector(collectionName, selector, "error", error);
|
|
244
|
-
this.onError(error);
|
|
32
|
+
if (this.options.log)
|
|
33
|
+
this.log = this.options.log;
|
|
34
|
+
if (typeof addEventListener === "undefined" || typeof postMessage === "undefined") {
|
|
35
|
+
throw new TypeError("WorkerDataAdapterHost can only be used in a Web Worker context");
|
|
245
36
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (!ids || ids.size === 0)
|
|
253
|
-
return;
|
|
254
|
-
const referenceMap = this.idRefCounts.get(collectionName);
|
|
255
|
-
const autoload = this.autoloadIds.get(collectionName);
|
|
256
|
-
const toRemove = [];
|
|
257
|
-
for (const id of ids) {
|
|
258
|
-
const current = referenceMap?.get(id) ?? 0;
|
|
259
|
-
const next = Math.max(0, current - 1);
|
|
260
|
-
if (next === 0) {
|
|
261
|
-
referenceMap?.delete(id);
|
|
262
|
-
if (autoload?.has(id))
|
|
263
|
-
toRemove.push(id);
|
|
264
|
-
} else {
|
|
265
|
-
referenceMap?.set(id, next);
|
|
37
|
+
this.workerContext.addEventListener("message", async (event) => {
|
|
38
|
+
try {
|
|
39
|
+
const { workerId, id, method, args } = event.data;
|
|
40
|
+
await this.handleMessage(workerId, id, method, args);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
this.onError(error);
|
|
266
43
|
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return;
|
|
270
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
271
|
-
if (!storage)
|
|
272
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
273
|
-
const items = await storage.readIds(toRemove);
|
|
274
|
-
if (items.length > 0) {
|
|
275
|
-
await storage.remove(items);
|
|
276
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
277
|
-
}
|
|
278
|
-
toRemove.forEach((id) => autoload?.delete(id));
|
|
279
|
-
}
|
|
280
|
-
// ===== Core query + CRUD plumbing (index-aware, same semantics as AsyncDataAdapter) =====
|
|
281
|
-
async setupStorage(collectionName, indices) {
|
|
282
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
283
|
-
if (!storage)
|
|
284
|
-
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
285
|
-
await Promise.all(indices.map((field) => storage.createIndex(field)));
|
|
286
|
-
await storage.setup();
|
|
44
|
+
});
|
|
45
|
+
this.respond("ready", null, null, "ready");
|
|
287
46
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return;
|
|
291
|
-
const adapter = this.options.storage && this.options.storage(name);
|
|
292
|
-
if (!adapter)
|
|
293
|
-
return;
|
|
294
|
-
this.storageAdapters.set(name, adapter);
|
|
47
|
+
respond(id, data, error = null, type = "response") {
|
|
48
|
+
this.workerContext.postMessage({ id, workerId: this.id, type, data, error });
|
|
295
49
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (!registry)
|
|
50
|
+
async handleMessage(workerId, id, method, args) {
|
|
51
|
+
if (workerId !== this.id)
|
|
299
52
|
return;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
this.publishState(collectionName, queryId(query.selector, query.options), state, error);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
publishState(collectionName, qid, state, error) {
|
|
307
|
-
const query = this.queries.get(collectionName)?.get(qid);
|
|
308
|
-
if (!query)
|
|
53
|
+
const fn = this[method];
|
|
54
|
+
if (typeof fn !== "function") {
|
|
55
|
+
this.respond(id, null, new Error(`Method ${method} not found`));
|
|
309
56
|
return;
|
|
310
|
-
query.state = state;
|
|
311
|
-
query.error = error;
|
|
312
|
-
for (const callback of query.listeners) {
|
|
313
|
-
try {
|
|
314
|
-
callback(state);
|
|
315
|
-
} catch (error_) {
|
|
316
|
-
this.onError(error_);
|
|
317
|
-
}
|
|
318
57
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const query = this.queries.get(collectionName)?.get(qid);
|
|
322
|
-
if (!query)
|
|
323
|
-
return;
|
|
324
|
-
query.items = items;
|
|
325
|
-
this.queries.get(collectionName)?.set(qid, query);
|
|
326
|
-
}
|
|
327
|
-
async fulfillQuery(collectionName, selector, options) {
|
|
328
|
-
const qid = queryId(selector, options);
|
|
329
|
-
const registry = this.queries.get(collectionName);
|
|
330
|
-
if (!registry)
|
|
331
|
-
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
332
|
-
const rec = registry.get(qid);
|
|
333
|
-
if (!rec)
|
|
334
|
-
return;
|
|
335
|
-
this.publishState(collectionName, qid, "active", null);
|
|
58
|
+
this.log(method, ...args);
|
|
59
|
+
await this.isReady(args[0]);
|
|
336
60
|
try {
|
|
337
|
-
const
|
|
338
|
-
this.
|
|
339
|
-
this.publishState(collectionName, qid, "complete", null);
|
|
61
|
+
const result = await fn.apply(this, args);
|
|
62
|
+
this.respond(id, result);
|
|
340
63
|
} catch (error) {
|
|
341
|
-
this.
|
|
64
|
+
this.respond(id, null, error);
|
|
342
65
|
}
|
|
343
66
|
}
|
|
344
67
|
async getIndexInfo(collectionName, selector) {
|
|
345
|
-
const
|
|
346
|
-
if (!
|
|
68
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
69
|
+
if (!storageAdapter)
|
|
347
70
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
348
71
|
if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
|
|
349
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
matched: true,
|
|
74
|
+
ids: compact([selector.id]),
|
|
75
|
+
optimizedSelector: {}
|
|
76
|
+
};
|
|
350
77
|
}
|
|
351
78
|
if (selector == null) {
|
|
352
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
matched: false,
|
|
81
|
+
ids: [],
|
|
82
|
+
optimizedSelector: {}
|
|
83
|
+
};
|
|
353
84
|
}
|
|
354
85
|
const indices = this.collectionIndices.get(collectionName) ?? [];
|
|
355
86
|
return getIndexInfo.default(indices.map((field) => async (flatSelector) => {
|
|
356
|
-
if (!Object.hasOwnProperty.call(flatSelector, field))
|
|
87
|
+
if (!Object.hasOwnProperty.call(flatSelector, field)) {
|
|
357
88
|
return { matched: false };
|
|
358
|
-
|
|
89
|
+
}
|
|
90
|
+
const index = await storageAdapter.readIndex(field);
|
|
359
91
|
const fieldSelector = flatSelector[field];
|
|
360
|
-
const
|
|
361
|
-
const keys =
|
|
92
|
+
const filteresForNull = fieldSelector == null || fieldSelector.$exists === false;
|
|
93
|
+
const keys = filteresForNull ? { include: null, exclude: [...index.keys()].filter((key) => key != null) } : getMatchingKeys(field, flatSelector);
|
|
362
94
|
if (keys.include == null && keys.exclude == null)
|
|
363
95
|
return { matched: false };
|
|
364
96
|
let includedIds = [];
|
|
365
97
|
if (keys.include == null) {
|
|
366
|
-
for (const set of index.values())
|
|
367
|
-
for (const pos of set)
|
|
98
|
+
for (const set of index.values()) {
|
|
99
|
+
for (const pos of set) {
|
|
368
100
|
includedIds.push(pos);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
369
103
|
} else {
|
|
370
104
|
for (const key of keys.include) {
|
|
371
105
|
const idSet = index.get(key);
|
|
372
|
-
if (idSet)
|
|
373
|
-
for (const id of idSet)
|
|
106
|
+
if (idSet) {
|
|
107
|
+
for (const id of idSet) {
|
|
374
108
|
includedIds.push(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
375
111
|
}
|
|
376
112
|
}
|
|
377
113
|
if (keys.exclude != null) {
|
|
378
114
|
const excludeIds = /* @__PURE__ */ new Set();
|
|
379
115
|
for (const key of keys.exclude) {
|
|
380
116
|
const idSet = index.get(key);
|
|
381
|
-
if (idSet)
|
|
382
|
-
for (const id of idSet)
|
|
117
|
+
if (idSet) {
|
|
118
|
+
for (const id of idSet) {
|
|
383
119
|
excludeIds.add(id);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
384
122
|
}
|
|
385
123
|
includedIds = includedIds.filter((pos) => !excludeIds.has(pos));
|
|
386
124
|
}
|
|
@@ -388,35 +126,38 @@ class AutoFetchDataAdapter {
|
|
|
388
126
|
matched: true,
|
|
389
127
|
ids: includedIds,
|
|
390
128
|
fields: [field],
|
|
391
|
-
keepSelector:
|
|
129
|
+
keepSelector: filteresForNull
|
|
392
130
|
};
|
|
393
131
|
}), selector);
|
|
394
132
|
}
|
|
395
133
|
async queryItems(collectionName, selector) {
|
|
396
|
-
const
|
|
397
|
-
if (!
|
|
134
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
135
|
+
if (!storageAdapter)
|
|
398
136
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
399
|
-
const
|
|
137
|
+
const indexInfo = await this.getIndexInfo(collectionName, selector);
|
|
400
138
|
const matchItems = (item) => {
|
|
401
|
-
if (
|
|
139
|
+
if (indexInfo.optimizedSelector == null)
|
|
402
140
|
return true;
|
|
403
|
-
if (Object.keys(
|
|
141
|
+
if (Object.keys(indexInfo.optimizedSelector).length <= 0)
|
|
404
142
|
return true;
|
|
405
|
-
|
|
143
|
+
const matches = match(item, indexInfo.optimizedSelector);
|
|
144
|
+
return matches;
|
|
406
145
|
};
|
|
407
|
-
if (
|
|
408
|
-
const items = await
|
|
409
|
-
if (isEqual(
|
|
146
|
+
if (indexInfo.matched) {
|
|
147
|
+
const items = await storageAdapter.readIds(indexInfo.ids);
|
|
148
|
+
if (isEqual(indexInfo.optimizedSelector, {}))
|
|
410
149
|
return items;
|
|
411
150
|
return items.filter(matchItems);
|
|
412
151
|
} else {
|
|
413
|
-
const allItems = await
|
|
152
|
+
const allItems = await storageAdapter.readAll();
|
|
414
153
|
if (isEqual(selector, {}))
|
|
415
154
|
return allItems;
|
|
416
155
|
return allItems.filter(matchItems);
|
|
417
156
|
}
|
|
418
157
|
}
|
|
419
158
|
async executeQuery(collectionName, selector, options) {
|
|
159
|
+
if (selector === null)
|
|
160
|
+
return [];
|
|
420
161
|
const items = await this.queryItems(collectionName, selector || {});
|
|
421
162
|
const { sort, skip, limit, fields } = options || {};
|
|
422
163
|
const sorted = sort ? sortItems(items, sort) : items;
|
|
@@ -426,154 +167,222 @@ class AutoFetchDataAdapter {
|
|
|
426
167
|
return limited.map((item) => {
|
|
427
168
|
if (!fields)
|
|
428
169
|
return item;
|
|
429
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
...idExcluded ? {} : { id: item.id },
|
|
172
|
+
...project(item, fields)
|
|
173
|
+
};
|
|
430
174
|
});
|
|
431
175
|
}
|
|
432
|
-
|
|
433
|
-
const
|
|
434
|
-
if (!
|
|
176
|
+
ensureQuery(collectionName, selector, options) {
|
|
177
|
+
const id = queryId(selector, options);
|
|
178
|
+
if (!this.queries.get(collectionName)) {
|
|
435
179
|
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
436
|
-
|
|
180
|
+
}
|
|
181
|
+
let query = this.queries.get(collectionName)?.get(id);
|
|
182
|
+
if (!query) {
|
|
183
|
+
query = { selector, options };
|
|
184
|
+
this.queries.get(collectionName)?.set(id, query);
|
|
185
|
+
}
|
|
186
|
+
return query;
|
|
187
|
+
}
|
|
188
|
+
emitQueryUpdate(collectionName, selector, options, state, error, items) {
|
|
189
|
+
const id = queryId(selector, options);
|
|
190
|
+
const collectionQueries = this.queries.get(collectionName);
|
|
191
|
+
if (!collectionQueries)
|
|
192
|
+
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
193
|
+
this.respond(id, { collectionName, selector, options, state, error, items }, null, "queryUpdate");
|
|
194
|
+
}
|
|
195
|
+
ensureStorageAdapter(name) {
|
|
196
|
+
if (this.storageAdapters.has(name))
|
|
437
197
|
return;
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
198
|
+
const adapter = this.options.storage(name);
|
|
199
|
+
if (!adapter)
|
|
440
200
|
return;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
201
|
+
this.storageAdapters.set(name, adapter);
|
|
202
|
+
}
|
|
203
|
+
async checkQueryUpdates(collectionName, items) {
|
|
204
|
+
const queries = this.queries.get(collectionName);
|
|
205
|
+
if (!queries)
|
|
206
|
+
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
207
|
+
const affectedQueries = [...queries.values()].filter(({ selector }) => items.some((item) => match(item, selector))) ?? [];
|
|
208
|
+
if (affectedQueries.length === 0)
|
|
209
|
+
return;
|
|
210
|
+
affectedQueries.forEach(({ selector, options }) => {
|
|
211
|
+
this.emitQueryUpdate(collectionName, selector, options, "active", null);
|
|
212
|
+
});
|
|
213
|
+
await Promise.all(affectedQueries.map(async ({ selector, options }) => {
|
|
214
|
+
const queryItems = await this.executeQuery(collectionName, selector, options);
|
|
215
|
+
this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
|
|
454
216
|
}));
|
|
455
217
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
218
|
+
registerCollection = async (collectionName, indices) => {
|
|
219
|
+
this.collectionIndices.set(collectionName, indices);
|
|
220
|
+
this.queries.set(collectionName, /* @__PURE__ */ new Map());
|
|
221
|
+
this.ensureStorageAdapter(collectionName);
|
|
222
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
223
|
+
if (!storageAdapter)
|
|
460
224
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
async
|
|
469
|
-
|
|
470
|
-
|
|
225
|
+
const setupPromise = (async () => {
|
|
226
|
+
await Promise.all(indices.map((index) => storageAdapter.createIndex(index)));
|
|
227
|
+
await storageAdapter.setup();
|
|
228
|
+
})();
|
|
229
|
+
this.storageAdapterReady.set(collectionName, setupPromise);
|
|
230
|
+
await setupPromise;
|
|
231
|
+
};
|
|
232
|
+
unregisterCollection = async (collectionName) => {
|
|
233
|
+
this.storageAdapters.delete(collectionName);
|
|
234
|
+
this.queries.delete(collectionName);
|
|
235
|
+
};
|
|
236
|
+
registerQuery = async (collectionName, selector, options) => {
|
|
237
|
+
this.ensureQuery(collectionName, selector, options);
|
|
238
|
+
const queryItems = await this.executeQuery(collectionName, selector, options);
|
|
239
|
+
this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
|
|
240
|
+
};
|
|
241
|
+
unregisterQuery = async (collectionName, selector, options) => {
|
|
242
|
+
const id = queryId(selector, options);
|
|
243
|
+
if (!this.queries.get(collectionName))
|
|
244
|
+
throw new Error(`Collection ${collectionName} not initialized!`);
|
|
245
|
+
this.queries.get(collectionName)?.delete(id);
|
|
246
|
+
};
|
|
247
|
+
insert = async (collectionName, input) => {
|
|
248
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
249
|
+
if (!storageAdapter)
|
|
471
250
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
472
|
-
const
|
|
473
|
-
const [item]
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
await
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (!storage)
|
|
251
|
+
const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map((i) => i[0].id) } });
|
|
252
|
+
const result = input.map(([item]) => {
|
|
253
|
+
if (item.id == null)
|
|
254
|
+
return new Error("Item must have an id");
|
|
255
|
+
if (existingItems.some((existing) => existing.id === item.id)) {
|
|
256
|
+
return new Error(`Item with id ${item.id} already exists`);
|
|
257
|
+
}
|
|
258
|
+
return item;
|
|
259
|
+
});
|
|
260
|
+
const newItems = result.filter((item) => !(item instanceof Error));
|
|
261
|
+
await storageAdapter.insert(newItems);
|
|
262
|
+
await this.checkQueryUpdates(collectionName, newItems);
|
|
263
|
+
return result;
|
|
264
|
+
};
|
|
265
|
+
updateOne = async (collectionName, parameters) => {
|
|
266
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
267
|
+
if (!storageAdapter)
|
|
490
268
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
269
|
+
const previousItems = [];
|
|
270
|
+
const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
|
|
271
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
|
|
272
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
273
|
+
if (item == null)
|
|
274
|
+
return [];
|
|
275
|
+
previousItems.push(item);
|
|
276
|
+
const modifiedItem = modify(deepClone.default(item), restModifier);
|
|
277
|
+
if (item.id !== modifiedItem.id) {
|
|
278
|
+
const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
|
|
279
|
+
if (existingItems.length > 0) {
|
|
280
|
+
return new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
281
|
+
}
|
|
501
282
|
}
|
|
502
|
-
return
|
|
283
|
+
return [modifiedItem];
|
|
503
284
|
}));
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
285
|
+
const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
|
|
286
|
+
if (modifiedItems.length > 0) {
|
|
287
|
+
await storageAdapter.replace(modifiedItems);
|
|
288
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
};
|
|
292
|
+
updateMany = async (collectionName, parameters) => {
|
|
293
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
294
|
+
if (!storageAdapter)
|
|
511
295
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
512
|
-
const
|
|
513
|
-
const [
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
296
|
+
const previousItems = [];
|
|
297
|
+
const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
|
|
298
|
+
const items = await this.executeQuery(collectionName, selector);
|
|
299
|
+
if (items.length === 0)
|
|
300
|
+
return [];
|
|
301
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
302
|
+
try {
|
|
303
|
+
const changedItems = await Promise.all(items.map(async (item) => {
|
|
304
|
+
const modifiedItem = modify(deepClone.default(item), restModifier);
|
|
305
|
+
if (item.id !== modifiedItem.id) {
|
|
306
|
+
const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
|
|
307
|
+
if (existingItems.length > 0) {
|
|
308
|
+
throw new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
previousItems.push(item);
|
|
312
|
+
return modifiedItem;
|
|
313
|
+
}));
|
|
314
|
+
return changedItems;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return error;
|
|
317
|
+
}
|
|
318
|
+
}));
|
|
319
|
+
const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
|
|
320
|
+
if (modifiedItems.length > 0) {
|
|
321
|
+
await storageAdapter.replace(modifiedItems);
|
|
322
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
521
323
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
528
|
-
if (!storage)
|
|
324
|
+
return result;
|
|
325
|
+
};
|
|
326
|
+
replaceOne = async (collectionName, parameters) => {
|
|
327
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
328
|
+
if (!storageAdapter)
|
|
529
329
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
530
|
-
const
|
|
531
|
-
const [
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
330
|
+
const previousItems = [];
|
|
331
|
+
const result = await Promise.all(parameters.map(async ([selector, replacement]) => {
|
|
332
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
|
|
333
|
+
if (item == null)
|
|
334
|
+
return [];
|
|
335
|
+
previousItems.push(item);
|
|
336
|
+
const modifiedItem = {
|
|
337
|
+
...replacement,
|
|
338
|
+
id: replacement.id ?? item.id
|
|
339
|
+
};
|
|
340
|
+
if (item.id !== modifiedItem.id) {
|
|
341
|
+
const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
|
|
342
|
+
if (existingItems.length > 0) {
|
|
343
|
+
return new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return [modifiedItem];
|
|
347
|
+
}));
|
|
348
|
+
const modifiedItems = compact(result.filter((item) => !(item instanceof Error)).flat());
|
|
349
|
+
if (modifiedItems.length > 0) {
|
|
350
|
+
await storageAdapter.replace(modifiedItems);
|
|
351
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
};
|
|
355
|
+
removeOne = async (collectionName, selectors) => {
|
|
356
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
357
|
+
if (!storageAdapter)
|
|
541
358
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
359
|
+
const result = await Promise.all(selectors.map(async ([selector]) => {
|
|
360
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items2) => items2[0] ?? null);
|
|
361
|
+
if (item == null)
|
|
362
|
+
return [];
|
|
363
|
+
return [item];
|
|
364
|
+
}));
|
|
365
|
+
const items = result.flat();
|
|
366
|
+
if (items.length > 0) {
|
|
367
|
+
await storageAdapter.remove(items);
|
|
368
|
+
await this.checkQueryUpdates(collectionName, items);
|
|
369
|
+
}
|
|
370
|
+
return result;
|
|
371
|
+
};
|
|
372
|
+
removeMany = async (collectionName, selectors) => {
|
|
373
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
374
|
+
if (!storageAdapter)
|
|
555
375
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const referenceMap = this.idRefCounts.get(collectionName);
|
|
562
|
-
const autoload = this.autoloadIds.get(collectionName);
|
|
563
|
-
for (const item of incoming) {
|
|
564
|
-
const previous = existingById.get(item.id);
|
|
565
|
-
if (previous) {
|
|
566
|
-
toReplace.push(this.mergeItems(previous, item));
|
|
567
|
-
} else {
|
|
568
|
-
toInsert.push(item);
|
|
569
|
-
}
|
|
570
|
-
autoload?.add(item.id);
|
|
571
|
-
referenceMap?.set(item.id, (referenceMap?.get(item.id) ?? 0) + 1);
|
|
376
|
+
const result = await Promise.all(selectors.map(async ([selector]) => this.executeQuery(collectionName, selector)));
|
|
377
|
+
const items = result.flat();
|
|
378
|
+
if (items.length > 0) {
|
|
379
|
+
await storageAdapter.remove(items);
|
|
380
|
+
await this.checkQueryUpdates(collectionName, items);
|
|
572
381
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
382
|
+
return result;
|
|
383
|
+
};
|
|
384
|
+
isReady = async (collectionName) => {
|
|
385
|
+
return this.storageAdapterReady.get(collectionName);
|
|
386
|
+
};
|
|
578
387
|
}
|
|
579
|
-
module.exports =
|
|
388
|
+
module.exports = WorkerDataAdapterHost;
|