@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.
@@ -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
- //#region src/AutoFetchDataAdapter.ts
11
- /**
12
- * Default merge strategy: shallow spread (right wins)
13
- * @param a first item
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
- onError;
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
- activeObservers = /* @__PURE__ */ new Map();
58
- observerTimeouts = /* @__PURE__ */ new Map();
59
- selectorIds = /* @__PURE__ */ new Map();
60
- idRefCounts = /* @__PURE__ */ new Map();
61
- autoloadIds = /* @__PURE__ */ new Map();
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 || "autofetch-data-adapter";
65
- this.onError = options.onError ?? ((error) => {
66
- console.error(error);
67
- });
68
- this.fetchQueryItems = options.fetchQueryItems;
69
- this.mergeItems = options.mergeItems ?? defaultMergeItems;
70
- this.purgeDelay = options.purgeDelay ?? 1e4;
71
- if (options.registerRemoteChange) options.registerRemoteChange(async () => {
72
- await this.forceRefetchAll();
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
- publishForSelector(collectionName, selector, state, error) {
288
- const registry = this.queries.get(collectionName);
289
- if (!registry) return;
290
- for (const query of registry.values()) {
291
- if (!require_isEqual.default(query.selector, selector)) continue;
292
- this.publishState(collectionName, require_queryId.default(query.selector, query.options), state, error);
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
- publishState(collectionName, qid, state, error) {
296
- const query = this.queries.get(collectionName)?.get(qid);
297
- if (!query) return;
298
- query.state = state;
299
- query.error = error;
300
- for (const callback of query.listeners) try {
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
- publishResult(collectionName, qid, items) {
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 items = await this.executeQuery(collectionName, selector, options);
320
- this.publishResult(collectionName, qid, items);
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.publishState(collectionName, qid, "error", error);
62
+ this.respond(id, null, error);
324
63
  }
325
64
  }
326
65
  async getIndexInfo(collectionName, selector) {
327
- const storage = this.storageAdapters.get(collectionName);
328
- if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
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 storage.readIndex(field);
80
+ const index = await storageAdapter.readIndex(field);
342
81
  const fieldSelector = flatSelector[field];
343
- const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
344
- const keys = filtersForNull ? {
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: filtersForNull
106
+ keepSelector: filteresForNull
368
107
  };
369
108
  }), selector);
370
109
  }
371
110
  async queryItems(collectionName, selector) {
372
- const storage = this.storageAdapters.get(collectionName);
373
- if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
374
- const index = await this.getIndexInfo(collectionName, selector);
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 (index.optimizedSelector == null) return true;
377
- if (Object.keys(index.optimizedSelector).length <= 0) return true;
378
- return require_match.default(item, index.optimizedSelector);
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 (index.matched) {
381
- const items = await storage.readIds(index.ids);
382
- if (require_isEqual.default(index.optimizedSelector, {})) return items;
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 storage.readAll();
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
- async checkQueryUpdates(collectionName, changedItems) {
406
- const registry = this.queries.get(collectionName);
407
- if (!registry) throw new Error(`Collection ${collectionName} not initialized!`);
408
- if (registry.size === 0) return;
409
- const affected = [...registry.values()].filter(({ selector }) => changedItems.some((item) => require_match.default(item, selector)));
410
- if (affected.length === 0) return;
411
- for (const { selector, options } of affected) {
412
- const qid = require_queryId.default(selector, options);
413
- this.publishState(collectionName, qid, "active", null);
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
- await Promise.all(affected.map(async ({ selector, options }) => {
416
- const qid = require_queryId.default(selector, options);
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
- const items = await this.executeQuery(collectionName, selector, options);
419
- this.publishResult(collectionName, qid, items);
420
- this.publishState(collectionName, qid, "complete", null);
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
- this.publishState(collectionName, qid, "error", error);
270
+ return error;
423
271
  }
424
272
  }));
425
- }
426
- async insert(collectionName, newItem) {
427
- const storage = this.storageAdapters.get(collectionName);
428
- if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
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
- await storage.replace([modified]);
445
- await this.checkQueryUpdates(collectionName, [item, modified]);
446
- return [modified];
447
- }
448
- async updateMany(collectionName, selector, modifier) {
449
- const storage = this.storageAdapters.get(collectionName);
450
- if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
451
- const items = await this.executeQuery(collectionName, selector);
452
- if (items.length === 0) return [];
453
- const { $setOnInsert, ...rest } = modifier;
454
- const changed = await Promise.all(items.map(async (item) => {
455
- const modified = require_modify.default(require_deepClone.default(item), rest);
456
- if (item.id !== modified.id) {
457
- if ((await this.executeQuery(collectionName, { id: modified.id }, { limit: 1 })).length > 0) throw new Error(`Item with id ${String(modified.id)} already exists`);
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 modified;
295
+ return [modifiedItem];
460
296
  }));
461
- await storage.replace(changed);
462
- await this.checkQueryUpdates(collectionName, [...items, ...changed]);
463
- return changed;
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
- await storage.replace([modified]);
478
- await this.checkQueryUpdates(collectionName, [item, modified]);
479
- return [modified];
480
- }
481
- async removeOne(collectionName, selector) {
482
- const storage = this.storageAdapters.get(collectionName);
483
- if (!storage) throw new Error(`No persistence adapter for collection ${collectionName}`);
484
- const [item] = await this.executeQuery(collectionName, selector, { limit: 1 });
485
- if (item == null) return [];
486
- await storage.remove([item]);
487
- await this.checkQueryUpdates(collectionName, [item]);
488
- return [item];
489
- }
490
- async removeMany(collectionName, selector) {
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
- if (toInsert.length > 0) await storage.insert(toInsert);
518
- if (toReplace.length > 0) await storage.replace(toReplace);
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 = AutoFetchDataAdapter;
335
+ exports.default = WorkerDataAdapterHost;