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