@signaldb/core 2.0.0-beta.11 → 2.0.0-beta.13
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 +19 -4
- package/dist/Collection/Cursor.d.ts +42 -0
- package/dist/Collection/index.d.ts +2 -1
- package/dist/WorkerDataAdapter.d.ts +20 -0
- package/dist/index.cjs.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index25.cjs.js +17 -1
- package/dist/index25.mjs +17 -1
- package/dist/index29.cjs.js +2 -1
- package/dist/index29.mjs +2 -1
- package/dist/index31.cjs.js +29 -188
- package/dist/index31.mjs +29 -188
- package/dist/index32.cjs.js +310 -20
- package/dist/index32.mjs +310 -20
- package/dist/index33.cjs.js +20 -334
- package/dist/index33.mjs +20 -334
- package/dist/index34.cjs.js +237 -441
- package/dist/index34.mjs +237 -441
- package/dist/index35.cjs.js +539 -0
- package/dist/index35.mjs +539 -0
- package/dist/index5.cjs.js +35 -1
- package/dist/index5.mjs +35 -1
- package/dist/utils/applyQueryOptions.d.ts +15 -0
- package/package.json +1 -1
package/dist/index34.cjs.js
CHANGED
|
@@ -7,328 +7,67 @@ const require_modify = require("./index19.cjs.js");
|
|
|
7
7
|
const require_project = require("./index21.cjs.js");
|
|
8
8
|
const require_queryId = require("./index22.cjs.js");
|
|
9
9
|
const require_sortItems = require("./index23.cjs.js");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @param b second item
|
|
15
|
-
* @returns merged item
|
|
16
|
-
*/
|
|
17
|
-
function defaultMergeItems(a, b) {
|
|
18
|
-
return {
|
|
19
|
-
...a,
|
|
20
|
-
...b
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Generates a stable key for a selector
|
|
25
|
-
* @param selector - the selector
|
|
26
|
-
* @returns the key
|
|
27
|
-
*/
|
|
28
|
-
function selectorId(selector) {
|
|
29
|
-
return JSON.stringify(selector ?? {});
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* AutoFetchDataAdapter
|
|
33
|
-
*
|
|
34
|
-
* A DataAdapter that:
|
|
35
|
-
* - Mirrors the CollectionBackend surface (CRUD + query registry + lifecycle)
|
|
36
|
-
* - Executes queries against a provided StorageAdapter (local cache)
|
|
37
|
-
* - On first registration of a selector, auto-fetches from a remote source and
|
|
38
|
-
* ingests the result into storage (upsert), then pushes query result updates
|
|
39
|
-
* - Optionally purges auto-fetched items for a selector once no observers remain
|
|
40
|
-
* - Can subscribe to remote change notifications to re-fetch active selectors
|
|
41
|
-
*
|
|
42
|
-
* IMPORTANT: Purging only ever deletes items that were introduced via the
|
|
43
|
-
* auto-fetch path and are no longer referenced by any active selector. Items
|
|
44
|
-
* inserted through CRUD calls are never purged.
|
|
45
|
-
*/
|
|
46
|
-
var AutoFetchDataAdapter = class {
|
|
10
|
+
const require_compact = require("./index33.cjs.js");
|
|
11
|
+
//#region src/WorkerDataAdapterHost.ts
|
|
12
|
+
var WorkerDataAdapterHost = class {
|
|
13
|
+
workerContext;
|
|
47
14
|
options;
|
|
48
15
|
id;
|
|
49
|
-
|
|
50
|
-
fetchQueryItems;
|
|
51
|
-
mergeItems;
|
|
52
|
-
purgeDelay;
|
|
16
|
+
log = () => {};
|
|
53
17
|
storageAdapters = /* @__PURE__ */ new Map();
|
|
54
18
|
storageAdapterReady = /* @__PURE__ */ new Map();
|
|
55
19
|
collectionIndices = /* @__PURE__ */ new Map();
|
|
56
20
|
queries = /* @__PURE__ */ new Map();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
constructor(options) {
|
|
21
|
+
onError = (error) => {
|
|
22
|
+
console.error(error);
|
|
23
|
+
};
|
|
24
|
+
constructor(workerContext, options) {
|
|
25
|
+
this.workerContext = workerContext;
|
|
63
26
|
this.options = options;
|
|
64
|
-
this.id = options.id || "
|
|
65
|
-
this.onError = options.onError
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
createCollectionBackend(collection, indices) {
|
|
76
|
-
this.collectionIndices.set(collection.name, indices);
|
|
77
|
-
this.queries.set(collection.name, /* @__PURE__ */ new Map());
|
|
78
|
-
this.ensureStorageAdapter(collection.name);
|
|
79
|
-
this.activeObservers.set(collection.name, /* @__PURE__ */ new Map());
|
|
80
|
-
this.observerTimeouts.set(collection.name, /* @__PURE__ */ new Map());
|
|
81
|
-
this.selectorIds.set(collection.name, /* @__PURE__ */ new Map());
|
|
82
|
-
this.idRefCounts.set(collection.name, /* @__PURE__ */ new Map());
|
|
83
|
-
this.autoloadIds.set(collection.name, /* @__PURE__ */ new Set());
|
|
84
|
-
const ready = this.setupStorage(collection.name, indices);
|
|
85
|
-
this.storageAdapterReady.set(collection.name, ready);
|
|
86
|
-
const registerQuery = (selector, options) => {
|
|
87
|
-
const qid = require_queryId.default(selector, options);
|
|
88
|
-
const registry = this.queries.get(collection.name);
|
|
89
|
-
if (!registry) throw new Error(`Collection ${collection.name} not initialized!`);
|
|
90
|
-
registry.set(qid, {
|
|
91
|
-
selector,
|
|
92
|
-
options,
|
|
93
|
-
items: [],
|
|
94
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
95
|
-
...registry.get(qid),
|
|
96
|
-
state: "active",
|
|
97
|
-
error: null
|
|
98
|
-
});
|
|
99
|
-
const key = selectorId(selector);
|
|
100
|
-
const perColObservers = this.activeObservers.get(collection.name);
|
|
101
|
-
const current = perColObservers?.get(key)?.count ?? 0;
|
|
102
|
-
perColObservers?.set(key, {
|
|
103
|
-
selector,
|
|
104
|
-
count: current + 1
|
|
105
|
-
});
|
|
106
|
-
const t = this.observerTimeouts.get(collection.name)?.get(key);
|
|
107
|
-
if (t) clearTimeout(t);
|
|
108
|
-
if (current === 0) this.fetchAndIngest(collection.name, selector).catch(this.onError);
|
|
109
|
-
this.fulfillQuery(collection.name, selector, options).catch(this.onError);
|
|
110
|
-
};
|
|
111
|
-
const unregisterQuery = (selector, options) => {
|
|
112
|
-
const qid = require_queryId.default(selector, options);
|
|
113
|
-
this.queries.get(collection.name)?.delete(qid);
|
|
114
|
-
const key = selectorId(selector);
|
|
115
|
-
const perColObservers = this.activeObservers.get(collection.name);
|
|
116
|
-
const current = perColObservers?.get(key)?.count ?? 0;
|
|
117
|
-
const remaining = Math.max(0, current - 1);
|
|
118
|
-
if (remaining > 0) {
|
|
119
|
-
perColObservers?.set(key, {
|
|
120
|
-
selector,
|
|
121
|
-
count: remaining
|
|
122
|
-
});
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const doPurge = () => {
|
|
126
|
-
perColObservers?.delete(key);
|
|
127
|
-
this.purgeSelector(collection.name, selector).catch(this.onError);
|
|
128
|
-
};
|
|
129
|
-
if (this.purgeDelay === 0) doPurge();
|
|
130
|
-
else {
|
|
131
|
-
const timeouts = this.observerTimeouts.get(collection.name);
|
|
132
|
-
const t = timeouts?.get(key);
|
|
133
|
-
if (t) clearTimeout(t);
|
|
134
|
-
timeouts?.set(key, setTimeout(doPurge, this.purgeDelay));
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
const getQueryState = (selector, options) => {
|
|
138
|
-
return (this.queries.get(collection.name)?.get(require_queryId.default(selector, options)))?.state ?? "active";
|
|
139
|
-
};
|
|
140
|
-
const getQueryError = (selector, options) => {
|
|
141
|
-
return (this.queries.get(collection.name)?.get(require_queryId.default(selector, options)))?.error ?? null;
|
|
142
|
-
};
|
|
143
|
-
const getQueryResult = (selector, options) => {
|
|
144
|
-
return (this.queries.get(collection.name)?.get(require_queryId.default(selector, options)))?.items ?? [];
|
|
145
|
-
};
|
|
146
|
-
const onQueryStateChange = (selector, options, callback) => {
|
|
147
|
-
const qid = require_queryId.default(selector, options);
|
|
148
|
-
const registry = this.queries.get(collection.name);
|
|
149
|
-
if (!registry) throw new Error(`Collection ${collection.name} not initialized!`);
|
|
150
|
-
if (!registry.has(qid)) registry.set(qid, {
|
|
151
|
-
selector,
|
|
152
|
-
options,
|
|
153
|
-
state: "active",
|
|
154
|
-
error: null,
|
|
155
|
-
items: [],
|
|
156
|
-
listeners: /* @__PURE__ */ new Set()
|
|
157
|
-
});
|
|
158
|
-
registry.get(qid)?.listeners.add(callback);
|
|
159
|
-
return () => {
|
|
160
|
-
registry.get(qid)?.listeners.delete(callback);
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
return {
|
|
164
|
-
insert: async (item) => {
|
|
165
|
-
await ready;
|
|
166
|
-
return await this.insert(collection.name, item);
|
|
167
|
-
},
|
|
168
|
-
updateOne: async (selector, modifier) => {
|
|
169
|
-
await ready;
|
|
170
|
-
return this.updateOne(collection.name, selector, modifier);
|
|
171
|
-
},
|
|
172
|
-
updateMany: async (selector, modifier) => {
|
|
173
|
-
await ready;
|
|
174
|
-
return this.updateMany(collection.name, selector, modifier);
|
|
175
|
-
},
|
|
176
|
-
replaceOne: async (selector, replacement) => {
|
|
177
|
-
await ready;
|
|
178
|
-
return this.replaceOne(collection.name, selector, replacement);
|
|
179
|
-
},
|
|
180
|
-
removeOne: async (selector) => {
|
|
181
|
-
await ready;
|
|
182
|
-
return this.removeOne(collection.name, selector);
|
|
183
|
-
},
|
|
184
|
-
removeMany: async (selector) => {
|
|
185
|
-
await ready;
|
|
186
|
-
return this.removeMany(collection.name, selector);
|
|
187
|
-
},
|
|
188
|
-
registerQuery,
|
|
189
|
-
unregisterQuery,
|
|
190
|
-
getQueryState,
|
|
191
|
-
getQueryError,
|
|
192
|
-
getQueryResult,
|
|
193
|
-
onQueryStateChange,
|
|
194
|
-
executeQuery: async (selector, options) => {
|
|
195
|
-
await ready;
|
|
196
|
-
registerQuery(selector, options);
|
|
197
|
-
await new Promise((resolve) => {
|
|
198
|
-
let stop = () => {};
|
|
199
|
-
stop = onQueryStateChange(selector, options, (state) => {
|
|
200
|
-
if (state === "active") return;
|
|
201
|
-
resolve();
|
|
202
|
-
stop();
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
const result = getQueryResult(selector, options);
|
|
206
|
-
unregisterQuery(selector, options);
|
|
207
|
-
return result;
|
|
208
|
-
},
|
|
209
|
-
dispose: async () => {
|
|
210
|
-
this.storageAdapters.delete(collection.name);
|
|
211
|
-
this.queries.delete(collection.name);
|
|
212
|
-
this.collectionIndices.delete(collection.name);
|
|
213
|
-
this.storageAdapterReady.delete(collection.name);
|
|
214
|
-
this.activeObservers.delete(collection.name);
|
|
215
|
-
this.observerTimeouts.delete(collection.name);
|
|
216
|
-
this.selectorIds.delete(collection.name);
|
|
217
|
-
this.idRefCounts.delete(collection.name);
|
|
218
|
-
this.autoloadIds.delete(collection.name);
|
|
219
|
-
},
|
|
220
|
-
isReady: async () => {
|
|
221
|
-
await ready;
|
|
27
|
+
this.id = this.options.id || "default-worker-data-adapter";
|
|
28
|
+
if (this.options.onError) this.onError = this.options.onError;
|
|
29
|
+
if (this.options.log) this.log = this.options.log;
|
|
30
|
+
this.workerContext.addEventListener("message", async (event) => {
|
|
31
|
+
try {
|
|
32
|
+
const { workerId, id, method, args } = event.data;
|
|
33
|
+
await this.handleMessage(workerId, id, method, args);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
this.onError(error);
|
|
222
36
|
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
async forceRefetchAll() {
|
|
226
|
-
const tasks = [];
|
|
227
|
-
for (const [collectionName, observers] of this.activeObservers.entries()) for (const { selector, count } of observers.values()) if (count > 0) tasks.push(this.fetchAndIngest(collectionName, selector));
|
|
228
|
-
await Promise.all(tasks);
|
|
229
|
-
}
|
|
230
|
-
async fetchAndIngest(collectionName, selector) {
|
|
231
|
-
this.publishForSelector(collectionName, selector, "active", null);
|
|
232
|
-
try {
|
|
233
|
-
const items = await this.fetchQueryItems(collectionName, selector);
|
|
234
|
-
if (!items || !Array.isArray(items)) throw new Error("AutoFetchDataAdapter: fetchQueryItems must resolve to { items: T[] }");
|
|
235
|
-
const ids = items.map((i) => i.id);
|
|
236
|
-
const selectorKey = selectorId(selector);
|
|
237
|
-
const selMap = this.selectorIds.get(collectionName);
|
|
238
|
-
const previous = selMap?.get(selectorKey) ?? /* @__PURE__ */ new Set();
|
|
239
|
-
ids.forEach((id) => previous.add(id));
|
|
240
|
-
selMap?.set(selectorKey, previous);
|
|
241
|
-
await this.upsertMerged(collectionName, items);
|
|
242
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
this.publishForSelector(collectionName, selector, "error", error);
|
|
245
|
-
this.onError(error);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
async purgeSelector(collectionName, selector) {
|
|
249
|
-
const selectorKey = selectorId(selector);
|
|
250
|
-
const selMap = this.selectorIds.get(collectionName);
|
|
251
|
-
const ids = selMap?.get(selectorKey);
|
|
252
|
-
selMap?.delete(selectorKey);
|
|
253
|
-
if (!ids || ids.size === 0) 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)) toRemove.push(id);
|
|
263
|
-
} else referenceMap?.set(id, next);
|
|
264
|
-
}
|
|
265
|
-
if (toRemove.length === 0) return;
|
|
266
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
267
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
268
|
-
const items = await storage.readIds(toRemove);
|
|
269
|
-
if (items.length > 0) {
|
|
270
|
-
await storage.remove(items);
|
|
271
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
272
|
-
}
|
|
273
|
-
toRemove.forEach((id) => autoload?.delete(id));
|
|
274
|
-
}
|
|
275
|
-
async setupStorage(collectionName, indices) {
|
|
276
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
277
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
278
|
-
await storage.setup();
|
|
279
|
-
await Promise.all(indices.map((field) => storage.createIndex(field)));
|
|
280
|
-
}
|
|
281
|
-
ensureStorageAdapter(name) {
|
|
282
|
-
if (this.storageAdapters.has(name)) return;
|
|
283
|
-
const adapter = this.options.storage && this.options.storage(name);
|
|
284
|
-
if (!adapter) return;
|
|
285
|
-
this.storageAdapters.set(name, adapter);
|
|
37
|
+
});
|
|
38
|
+
this.respond("ready", null, null, "ready");
|
|
286
39
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
40
|
+
respond(id, data, error = null, type = "response") {
|
|
41
|
+
this.workerContext.postMessage({
|
|
42
|
+
id,
|
|
43
|
+
workerId: this.id,
|
|
44
|
+
type,
|
|
45
|
+
data,
|
|
46
|
+
error
|
|
47
|
+
});
|
|
294
48
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
callback(state);
|
|
302
|
-
} catch (error_) {
|
|
303
|
-
this.onError(error_);
|
|
49
|
+
async handleMessage(workerId, id, method, args) {
|
|
50
|
+
if (workerId !== this.id) return;
|
|
51
|
+
const fn = this[method];
|
|
52
|
+
if (typeof fn !== "function") {
|
|
53
|
+
this.respond(id, null, /* @__PURE__ */ new Error(`Method ${method} not found`));
|
|
54
|
+
return;
|
|
304
55
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const query = this.queries.get(collectionName)?.get(qid);
|
|
308
|
-
if (!query) return;
|
|
309
|
-
query.items = items;
|
|
310
|
-
this.queries.get(collectionName)?.set(qid, query);
|
|
311
|
-
}
|
|
312
|
-
async fulfillQuery(collectionName, selector, options) {
|
|
313
|
-
const qid = require_queryId.default(selector, options);
|
|
314
|
-
const registry = this.queries.get(collectionName);
|
|
315
|
-
if (!registry) throw new Error(`Collection ${collectionName} not initialized!`);
|
|
316
|
-
if (!registry.get(qid)) return;
|
|
317
|
-
this.publishState(collectionName, qid, "active", null);
|
|
56
|
+
this.log(method, ...args);
|
|
57
|
+
await this.isReady(args[0]);
|
|
318
58
|
try {
|
|
319
|
-
const
|
|
320
|
-
this.
|
|
321
|
-
this.publishState(collectionName, qid, "complete", null);
|
|
59
|
+
const result = await fn.apply(this, args);
|
|
60
|
+
this.respond(id, result);
|
|
322
61
|
} catch (error) {
|
|
323
|
-
this.
|
|
62
|
+
this.respond(id, null, error);
|
|
324
63
|
}
|
|
325
64
|
}
|
|
326
65
|
async getIndexInfo(collectionName, selector) {
|
|
327
|
-
const
|
|
328
|
-
if (!
|
|
66
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
67
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
329
68
|
if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") return {
|
|
330
69
|
matched: true,
|
|
331
|
-
ids: [selector.id],
|
|
70
|
+
ids: require_compact.default([selector.id]),
|
|
332
71
|
optimizedSelector: {}
|
|
333
72
|
};
|
|
334
73
|
if (selector == null) return {
|
|
@@ -338,10 +77,10 @@ var AutoFetchDataAdapter = class {
|
|
|
338
77
|
};
|
|
339
78
|
return require_getIndexInfo.default((this.collectionIndices.get(collectionName) ?? []).map((field) => async (flatSelector) => {
|
|
340
79
|
if (!Object.hasOwnProperty.call(flatSelector, field)) return { matched: false };
|
|
341
|
-
const index = await
|
|
80
|
+
const index = await storageAdapter.readIndex(field);
|
|
342
81
|
const fieldSelector = flatSelector[field];
|
|
343
|
-
const
|
|
344
|
-
const keys =
|
|
82
|
+
const filteresForNull = fieldSelector == null || fieldSelector.$exists === false;
|
|
83
|
+
const keys = filteresForNull ? {
|
|
345
84
|
include: null,
|
|
346
85
|
exclude: [...index.keys()].filter((key) => key != null)
|
|
347
86
|
} : require_getMatchingKeys.default(field, flatSelector);
|
|
@@ -364,30 +103,31 @@ var AutoFetchDataAdapter = class {
|
|
|
364
103
|
matched: true,
|
|
365
104
|
ids: includedIds,
|
|
366
105
|
fields: [field],
|
|
367
|
-
keepSelector:
|
|
106
|
+
keepSelector: filteresForNull
|
|
368
107
|
};
|
|
369
108
|
}), selector);
|
|
370
109
|
}
|
|
371
110
|
async queryItems(collectionName, selector) {
|
|
372
|
-
const
|
|
373
|
-
if (!
|
|
374
|
-
const
|
|
111
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
112
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
113
|
+
const indexInfo = await this.getIndexInfo(collectionName, selector);
|
|
375
114
|
const matchItems = (item) => {
|
|
376
|
-
if (
|
|
377
|
-
if (Object.keys(
|
|
378
|
-
return require_match.default(item,
|
|
115
|
+
if (indexInfo.optimizedSelector == null) return true;
|
|
116
|
+
if (Object.keys(indexInfo.optimizedSelector).length <= 0) return true;
|
|
117
|
+
return require_match.default(item, indexInfo.optimizedSelector);
|
|
379
118
|
};
|
|
380
|
-
if (
|
|
381
|
-
const items = await
|
|
382
|
-
if (require_isEqual.default(
|
|
119
|
+
if (indexInfo.matched) {
|
|
120
|
+
const items = await storageAdapter.readIds(indexInfo.ids);
|
|
121
|
+
if (require_isEqual.default(indexInfo.optimizedSelector, {})) return items;
|
|
383
122
|
return items.filter(matchItems);
|
|
384
123
|
} else {
|
|
385
|
-
const allItems = await
|
|
124
|
+
const allItems = await storageAdapter.readAll();
|
|
386
125
|
if (require_isEqual.default(selector, {})) return allItems;
|
|
387
126
|
return allItems.filter(matchItems);
|
|
388
127
|
}
|
|
389
128
|
}
|
|
390
129
|
async executeQuery(collectionName, selector, options) {
|
|
130
|
+
if (selector === null) return [];
|
|
391
131
|
const items = await this.queryItems(collectionName, selector || {});
|
|
392
132
|
const { sort, skip, limit, fields } = options || {};
|
|
393
133
|
const sorted = sort ? require_sortItems.default(items, sort) : items;
|
|
@@ -402,138 +142,194 @@ var AutoFetchDataAdapter = class {
|
|
|
402
142
|
};
|
|
403
143
|
});
|
|
404
144
|
}
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
if (!
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
145
|
+
ensureQuery(collectionName, selector, options) {
|
|
146
|
+
const id = require_queryId.default(selector, options);
|
|
147
|
+
if (!this.queries.get(collectionName)) throw new Error(`Collection ${collectionName} not initialized!`);
|
|
148
|
+
let query = this.queries.get(collectionName)?.get(id);
|
|
149
|
+
if (!query) {
|
|
150
|
+
query = {
|
|
151
|
+
selector,
|
|
152
|
+
options
|
|
153
|
+
};
|
|
154
|
+
this.queries.get(collectionName)?.set(id, query);
|
|
155
|
+
}
|
|
156
|
+
return query;
|
|
157
|
+
}
|
|
158
|
+
emitQueryUpdate(collectionName, selector, options, state, error, items) {
|
|
159
|
+
const id = require_queryId.default(selector, options);
|
|
160
|
+
if (!this.queries.get(collectionName)) throw new Error(`Collection ${collectionName} not initialized!`);
|
|
161
|
+
this.respond(id, {
|
|
162
|
+
collectionName,
|
|
163
|
+
selector,
|
|
164
|
+
options,
|
|
165
|
+
state,
|
|
166
|
+
error,
|
|
167
|
+
items
|
|
168
|
+
}, null, "queryUpdate");
|
|
169
|
+
}
|
|
170
|
+
ensureStorageAdapter(name) {
|
|
171
|
+
if (this.storageAdapters.has(name)) return;
|
|
172
|
+
const adapter = this.options.storage(name);
|
|
173
|
+
if (!adapter) return;
|
|
174
|
+
this.storageAdapters.set(name, adapter);
|
|
175
|
+
}
|
|
176
|
+
async checkQueryUpdates(collectionName, items) {
|
|
177
|
+
const queries = this.queries.get(collectionName);
|
|
178
|
+
if (!queries) throw new Error(`Collection ${collectionName} not initialized!`);
|
|
179
|
+
const affectedQueries = [...queries.values()].filter(({ selector }) => items.some((item) => require_match.default(item, selector))) ?? [];
|
|
180
|
+
if (affectedQueries.length === 0) return;
|
|
181
|
+
affectedQueries.forEach(({ selector, options }) => {
|
|
182
|
+
this.emitQueryUpdate(collectionName, selector, options, "active", null);
|
|
183
|
+
});
|
|
184
|
+
await Promise.all(affectedQueries.map(async ({ selector, options }) => {
|
|
185
|
+
const queryItems = await this.executeQuery(collectionName, selector, options);
|
|
186
|
+
this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
registerCollection = async (collectionName, indices) => {
|
|
190
|
+
this.collectionIndices.set(collectionName, indices);
|
|
191
|
+
this.queries.set(collectionName, /* @__PURE__ */ new Map());
|
|
192
|
+
this.ensureStorageAdapter(collectionName);
|
|
193
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
194
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
195
|
+
const setupPromise = (async () => {
|
|
196
|
+
await storageAdapter.setup();
|
|
197
|
+
await Promise.all(indices.map((index) => storageAdapter.createIndex(index)));
|
|
198
|
+
})();
|
|
199
|
+
this.storageAdapterReady.set(collectionName, setupPromise);
|
|
200
|
+
await setupPromise;
|
|
201
|
+
};
|
|
202
|
+
unregisterCollection = async (collectionName) => {
|
|
203
|
+
this.storageAdapters.delete(collectionName);
|
|
204
|
+
this.queries.delete(collectionName);
|
|
205
|
+
};
|
|
206
|
+
registerQuery = async (collectionName, selector, options) => {
|
|
207
|
+
this.ensureQuery(collectionName, selector, options);
|
|
208
|
+
const queryItems = await this.executeQuery(collectionName, selector, options);
|
|
209
|
+
this.emitQueryUpdate(collectionName, selector, options, "complete", null, queryItems);
|
|
210
|
+
};
|
|
211
|
+
unregisterQuery = async (collectionName, selector, options) => {
|
|
212
|
+
const id = require_queryId.default(selector, options);
|
|
213
|
+
if (!this.queries.get(collectionName)) throw new Error(`Collection ${collectionName} not initialized!`);
|
|
214
|
+
this.queries.get(collectionName)?.delete(id);
|
|
215
|
+
};
|
|
216
|
+
insert = async (collectionName, input) => {
|
|
217
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
218
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
219
|
+
const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map((i) => i[0].id) } });
|
|
220
|
+
const result = input.map(([item]) => {
|
|
221
|
+
if (item.id == null) return /* @__PURE__ */ new Error("Item must have an id");
|
|
222
|
+
if (existingItems.some((existing) => existing.id === item.id)) return /* @__PURE__ */ new Error(`Item with id ${item.id} already exists`);
|
|
223
|
+
return item;
|
|
224
|
+
});
|
|
225
|
+
const newItems = result.filter((item) => !(item instanceof Error));
|
|
226
|
+
await storageAdapter.insert(newItems);
|
|
227
|
+
await this.checkQueryUpdates(collectionName, newItems);
|
|
228
|
+
return result;
|
|
229
|
+
};
|
|
230
|
+
updateOne = async (collectionName, parameters) => {
|
|
231
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
232
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
233
|
+
const previousItems = [];
|
|
234
|
+
const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
|
|
235
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
|
|
236
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
237
|
+
if (item == null) return [];
|
|
238
|
+
previousItems.push(item);
|
|
239
|
+
const modifiedItem = require_modify.default(require_deepClone.default(item), restModifier);
|
|
240
|
+
if (item.id !== modifiedItem.id) {
|
|
241
|
+
if ((await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 })).length > 0) return /* @__PURE__ */ new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
242
|
+
}
|
|
243
|
+
return [modifiedItem];
|
|
244
|
+
}));
|
|
245
|
+
const modifiedItems = require_compact.default(result.filter((item) => !(item instanceof Error)).flat());
|
|
246
|
+
if (modifiedItems.length > 0) {
|
|
247
|
+
await storageAdapter.replace(modifiedItems);
|
|
248
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
414
249
|
}
|
|
415
|
-
|
|
416
|
-
|
|
250
|
+
return result;
|
|
251
|
+
};
|
|
252
|
+
updateMany = async (collectionName, parameters) => {
|
|
253
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
254
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
255
|
+
const previousItems = [];
|
|
256
|
+
const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
|
|
257
|
+
const items = await this.executeQuery(collectionName, selector);
|
|
258
|
+
if (items.length === 0) return [];
|
|
259
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
417
260
|
try {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
261
|
+
return await Promise.all(items.map(async (item) => {
|
|
262
|
+
const modifiedItem = require_modify.default(require_deepClone.default(item), restModifier);
|
|
263
|
+
if (item.id !== modifiedItem.id) {
|
|
264
|
+
if ((await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 })).length > 0) throw new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
265
|
+
}
|
|
266
|
+
previousItems.push(item);
|
|
267
|
+
return modifiedItem;
|
|
268
|
+
}));
|
|
421
269
|
} catch (error) {
|
|
422
|
-
|
|
270
|
+
return error;
|
|
423
271
|
}
|
|
424
272
|
}));
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if ((await this.executeQuery(collectionName, { id: newItem.id }, { limit: 1 })).length > 0) throw new Error(`Item with id ${String(newItem.id)} already exists`);
|
|
430
|
-
await storage.insert([newItem]);
|
|
431
|
-
await this.checkQueryUpdates(collectionName, [newItem]);
|
|
432
|
-
return newItem;
|
|
433
|
-
}
|
|
434
|
-
async updateOne(collectionName, selector, modifier) {
|
|
435
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
436
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
437
|
-
const [item] = await this.executeQuery(collectionName, selector, { limit: 1 });
|
|
438
|
-
const { $setOnInsert, ...rest } = modifier;
|
|
439
|
-
if (item == null) return [];
|
|
440
|
-
const modified = require_modify.default(require_deepClone.default(item), rest);
|
|
441
|
-
if (item.id !== modified.id) {
|
|
442
|
-
if ((await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 })).length > 0) throw new Error(`Item with id ${String(modified.id)} already exists`);
|
|
273
|
+
const modifiedItems = require_compact.default(result.filter((item) => !(item instanceof Error)).flat());
|
|
274
|
+
if (modifiedItems.length > 0) {
|
|
275
|
+
await storageAdapter.replace(modifiedItems);
|
|
276
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
443
277
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
278
|
+
return result;
|
|
279
|
+
};
|
|
280
|
+
replaceOne = async (collectionName, parameters) => {
|
|
281
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
282
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
283
|
+
const previousItems = [];
|
|
284
|
+
const result = await Promise.all(parameters.map(async ([selector, replacement]) => {
|
|
285
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
|
|
286
|
+
if (item == null) return [];
|
|
287
|
+
previousItems.push(item);
|
|
288
|
+
const modifiedItem = {
|
|
289
|
+
...replacement,
|
|
290
|
+
id: replacement.id ?? item.id
|
|
291
|
+
};
|
|
292
|
+
if (item.id !== modifiedItem.id) {
|
|
293
|
+
if ((await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 })).length > 0) return /* @__PURE__ */ new Error(`Item with id ${modifiedItem.id} already exists`);
|
|
458
294
|
}
|
|
459
|
-
return
|
|
295
|
+
return [modifiedItem];
|
|
460
296
|
}));
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
async replaceOne(collectionName, selector, replacement) {
|
|
466
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
467
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
468
|
-
const [item] = await this.executeQuery(collectionName, selector, { limit: 1 });
|
|
469
|
-
if (item == null) return [];
|
|
470
|
-
const modified = {
|
|
471
|
-
...replacement,
|
|
472
|
-
id: replacement.id ?? item.id
|
|
473
|
-
};
|
|
474
|
-
if (item.id !== modified.id) {
|
|
475
|
-
if ((await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 })).length > 0) throw new Error(`Item with id ${String(modified.id)} already exists`);
|
|
297
|
+
const modifiedItems = require_compact.default(result.filter((item) => !(item instanceof Error)).flat());
|
|
298
|
+
if (modifiedItems.length > 0) {
|
|
299
|
+
await storageAdapter.replace(modifiedItems);
|
|
300
|
+
await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
|
|
476
301
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
492
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
493
|
-
const items = await this.executeQuery(collectionName, selector);
|
|
494
|
-
if (items.length === 0) return [];
|
|
495
|
-
await storage.remove(items);
|
|
496
|
-
await this.checkQueryUpdates(collectionName, items);
|
|
497
|
-
return items;
|
|
498
|
-
}
|
|
499
|
-
async upsertMerged(collectionName, incoming) {
|
|
500
|
-
if (incoming.length === 0) return;
|
|
501
|
-
const storage = this.storageAdapters.get(collectionName);
|
|
502
|
-
if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
503
|
-
const ids = incoming.map((i) => i.id);
|
|
504
|
-
const existing = await storage.readIds(ids);
|
|
505
|
-
const existingById = new Map(existing.map((it) => [it.id, it]));
|
|
506
|
-
const toInsert = [];
|
|
507
|
-
const toReplace = [];
|
|
508
|
-
const referenceMap = this.idRefCounts.get(collectionName);
|
|
509
|
-
const autoload = this.autoloadIds.get(collectionName);
|
|
510
|
-
for (const item of incoming) {
|
|
511
|
-
const previous = existingById.get(item.id);
|
|
512
|
-
if (previous) toReplace.push(this.mergeItems(previous, item));
|
|
513
|
-
else toInsert.push(item);
|
|
514
|
-
autoload?.add(item.id);
|
|
515
|
-
referenceMap?.set(item.id, (referenceMap?.get(item.id) ?? 0) + 1);
|
|
302
|
+
return result;
|
|
303
|
+
};
|
|
304
|
+
removeOne = async (collectionName, selectors) => {
|
|
305
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
306
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
307
|
+
const result = await Promise.all(selectors.map(async ([selector]) => {
|
|
308
|
+
const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then((items) => items[0] ?? null);
|
|
309
|
+
if (item == null) return [];
|
|
310
|
+
return [item];
|
|
311
|
+
}));
|
|
312
|
+
const items = result.flat();
|
|
313
|
+
if (items.length > 0) {
|
|
314
|
+
await storageAdapter.remove(items);
|
|
315
|
+
await this.checkQueryUpdates(collectionName, items);
|
|
516
316
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
317
|
+
return result;
|
|
318
|
+
};
|
|
319
|
+
removeMany = async (collectionName, selectors) => {
|
|
320
|
+
const storageAdapter = this.storageAdapters.get(collectionName);
|
|
321
|
+
if (!storageAdapter) throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
322
|
+
const result = await Promise.all(selectors.map(async ([selector]) => this.executeQuery(collectionName, selector)));
|
|
323
|
+
const items = result.flat();
|
|
324
|
+
if (items.length > 0) {
|
|
325
|
+
await storageAdapter.remove(items);
|
|
326
|
+
await this.checkQueryUpdates(collectionName, items);
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
};
|
|
330
|
+
isReady = async (collectionName) => {
|
|
331
|
+
return this.storageAdapterReady.get(collectionName);
|
|
332
|
+
};
|
|
520
333
|
};
|
|
521
|
-
/**
|
|
522
|
-
* Usage (example):
|
|
523
|
-
*
|
|
524
|
-
* const adapter = new AutoFetchDataAdapter({
|
|
525
|
-
* storage: (name) => new IndexedDBStorage(name),
|
|
526
|
-
* fetchQueryItems: async (collectionName, selector) => {
|
|
527
|
-
* const res = await fetch(`/api/${collectionName}?q=${encodeURIComponent(JSON.stringify(selector||{}))}`)
|
|
528
|
-
* const items = await res.json()
|
|
529
|
-
* return { items }
|
|
530
|
-
* },
|
|
531
|
-
* registerRemoteChange: (onChange) => subscribeToWS(onChange),
|
|
532
|
-
* mergeItems: (a, b) => ({ ...a, ...b }),
|
|
533
|
-
* purgeDelay: 10_000,
|
|
534
|
-
* })
|
|
535
|
-
*
|
|
536
|
-
* const backend = adapter.createCollectionBackend(myCollection, ['status', 'projectId'])
|
|
537
|
-
*/
|
|
538
334
|
//#endregion
|
|
539
|
-
exports.default =
|
|
335
|
+
exports.default = WorkerDataAdapterHost;
|