@signaldb/core 2.0.0-beta.0 → 2.0.0-beta.1

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.
Files changed (52) hide show
  1. package/dist/.vite/manifest.json +25 -12
  2. package/dist/AutoFetchDataAdapter.d.ts +0 -1
  3. package/dist/Collection/index.d.ts +2 -0
  4. package/dist/DataAdapter.d.ts +7 -6
  5. package/dist/DefaultDataAdapter.d.ts +1 -0
  6. package/dist/WorkerDataAdapter.d.ts +4 -1
  7. package/dist/WorkerDataAdapterHost.d.ts +9 -6
  8. package/dist/index.cjs12.js +32 -21
  9. package/dist/index.cjs13.js +25 -13
  10. package/dist/index.cjs14.js +31 -37
  11. package/dist/index.cjs15.js +120 -75
  12. package/dist/index.cjs16.js +45 -36
  13. package/dist/index.cjs17.js +1 -1
  14. package/dist/index.cjs2.js +5 -2
  15. package/dist/index.cjs20.js +5 -102
  16. package/dist/index.cjs21.js +93 -118
  17. package/dist/index.cjs22.js +127 -5
  18. package/dist/index.cjs23.js +5 -25
  19. package/dist/index.cjs24.js +25 -5
  20. package/dist/index.cjs26.js +1 -1
  21. package/dist/index.cjs27.js +44 -3
  22. package/dist/index.cjs28.js +6 -5
  23. package/dist/index.cjs29.js +7 -27
  24. package/dist/index.cjs3.js +25 -21
  25. package/dist/index.cjs30.js +3 -40
  26. package/dist/index.cjs31.js +5 -7
  27. package/dist/index.cjs32.js +29 -0
  28. package/dist/index.cjs33.js +42 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index12.mjs +32 -21
  31. package/dist/index13.mjs +25 -13
  32. package/dist/index14.mjs +31 -37
  33. package/dist/index15.mjs +120 -75
  34. package/dist/index16.mjs +45 -36
  35. package/dist/index17.mjs +1 -1
  36. package/dist/index2.mjs +5 -2
  37. package/dist/index20.mjs +5 -102
  38. package/dist/index21.mjs +93 -117
  39. package/dist/index22.mjs +126 -5
  40. package/dist/index23.mjs +5 -25
  41. package/dist/index24.mjs +25 -5
  42. package/dist/index26.mjs +1 -1
  43. package/dist/index27.mjs +44 -3
  44. package/dist/index28.mjs +6 -5
  45. package/dist/index29.mjs +7 -27
  46. package/dist/index3.mjs +25 -21
  47. package/dist/index30.mjs +3 -40
  48. package/dist/index31.mjs +5 -7
  49. package/dist/index32.mjs +30 -0
  50. package/dist/index33.mjs +43 -0
  51. package/dist/utils/batchOnNextTick.d.ts +16 -0
  52. package/package.json +1 -1
@@ -59,6 +59,7 @@
59
59
  "src/DefaultDataAdapter.ts",
60
60
  "src/utils/modify.ts",
61
61
  "src/utils/deepClone.ts",
62
+ "src/utils/queryId.ts",
62
63
  "src/Collection/Cursor.ts"
63
64
  ]
64
65
  },
@@ -86,7 +87,8 @@
86
87
  "src": "src/WorkerDataAdapter.ts",
87
88
  "imports": [
88
89
  "src/utils/queryId.ts",
89
- "src/utils/randomId.ts"
90
+ "src/utils/randomId.ts",
91
+ "src/utils/batchOnNextTick.ts"
90
92
  ]
91
93
  },
92
94
  "src/WorkerDataAdapterHost.ts": {
@@ -102,11 +104,12 @@
102
104
  "src/getIndexInfo.ts",
103
105
  "src/utils/getMatchingKeys.ts",
104
106
  "src/utils/sortItems.ts",
105
- "src/utils/project.ts"
107
+ "src/utils/project.ts",
108
+ "src/utils/compact.ts"
106
109
  ]
107
110
  },
108
111
  "src/createIndex.ts": {
109
- "file": "index.cjs20.js",
112
+ "file": "index.cjs21.js",
110
113
  "name": "createIndex",
111
114
  "src": "src/createIndex.ts",
112
115
  "imports": [
@@ -117,7 +120,7 @@
117
120
  ]
118
121
  },
119
122
  "src/createIndexProvider.ts": {
120
- "file": "index.cjs27.js",
123
+ "file": "index.cjs30.js",
121
124
  "name": "createIndexProvider",
122
125
  "src": "src/createIndexProvider.ts"
123
126
  },
@@ -132,7 +135,7 @@
132
135
  "src": "src/createStorageAdapter.ts"
133
136
  },
134
137
  "src/getIndexInfo.ts": {
135
- "file": "index.cjs21.js",
138
+ "file": "index.cjs22.js",
136
139
  "name": "getIndexInfo",
137
140
  "src": "src/getIndexInfo.ts",
138
141
  "imports": [
@@ -167,6 +170,16 @@
167
170
  "name": "utils/EventEmitter",
168
171
  "src": "src/utils/EventEmitter.ts"
169
172
  },
173
+ "src/utils/batchOnNextTick.ts": {
174
+ "file": "index.cjs27.js",
175
+ "name": "utils/batchOnNextTick",
176
+ "src": "src/utils/batchOnNextTick.ts"
177
+ },
178
+ "src/utils/compact.ts": {
179
+ "file": "index.cjs28.js",
180
+ "name": "utils/compact",
181
+ "src": "src/utils/compact.ts"
182
+ },
170
183
  "src/utils/createSignal.ts": {
171
184
  "file": "index.cjs18.js",
172
185
  "name": "utils/createSignal",
@@ -192,7 +205,7 @@
192
205
  ]
193
206
  },
194
207
  "src/utils/intersection.ts": {
195
- "file": "index.cjs28.js",
208
+ "file": "index.cjs31.js",
196
209
  "name": "utils/intersection",
197
210
  "src": "src/utils/intersection.ts"
198
211
  },
@@ -202,12 +215,12 @@
202
215
  "src": "src/utils/isEqual.ts"
203
216
  },
204
217
  "src/utils/isFieldExpression.ts": {
205
- "file": "index.cjs30.js",
218
+ "file": "index.cjs33.js",
206
219
  "name": "utils/isFieldExpression",
207
220
  "src": "src/utils/isFieldExpression.ts"
208
221
  },
209
222
  "src/utils/match.ts": {
210
- "file": "index.cjs22.js",
223
+ "file": "index.cjs23.js",
211
224
  "name": "utils/match",
212
225
  "src": "src/utils/match.ts"
213
226
  },
@@ -220,7 +233,7 @@
220
233
  ]
221
234
  },
222
235
  "src/utils/project.ts": {
223
- "file": "index.cjs23.js",
236
+ "file": "index.cjs24.js",
224
237
  "name": "utils/project",
225
238
  "src": "src/utils/project.ts",
226
239
  "imports": [
@@ -229,7 +242,7 @@
229
242
  ]
230
243
  },
231
244
  "src/utils/queryId.ts": {
232
- "file": "index.cjs24.js",
245
+ "file": "index.cjs20.js",
233
246
  "name": "utils/queryId",
234
247
  "src": "src/utils/queryId.ts"
235
248
  },
@@ -244,7 +257,7 @@
244
257
  "src": "src/utils/serializeValue.ts"
245
258
  },
246
259
  "src/utils/set.ts": {
247
- "file": "index.cjs29.js",
260
+ "file": "index.cjs32.js",
248
261
  "name": "utils/set",
249
262
  "src": "src/utils/set.ts"
250
263
  },
@@ -257,7 +270,7 @@
257
270
  ]
258
271
  },
259
272
  "src/utils/uniqueBy.ts": {
260
- "file": "index.cjs31.js",
273
+ "file": "index.cjs29.js",
261
274
  "name": "utils/uniqueBy",
262
275
  "src": "src/utils/uniqueBy.ts"
263
276
  }
@@ -72,7 +72,6 @@ export default class AutoFetchDataAdapter implements DataAdapter {
72
72
  private autoloadIds;
73
73
  constructor(options: AutoFetchDataAdapterOptions);
74
74
  createCollectionBackend<T extends BaseItem<I>, I = any, U = T>(collection: Collection<T, I, U>, indices: string[]): CollectionBackend<T, I>;
75
- private selectorKey;
76
75
  private forceRefetchAll;
77
76
  private fetchAndIngest;
78
77
  private purgeSelector;
@@ -102,6 +102,7 @@ export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U = T
102
102
  private isDisposed;
103
103
  private postBatchCallbacks;
104
104
  private fieldTracking;
105
+ private queryListenersMap;
105
106
  /**
106
107
  * Initializes a new instance of the `Collection` class with optional configuration.
107
108
  * Sets up memory, persistence, reactivity, and indices as specified in the options.
@@ -186,6 +187,7 @@ export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U = T
186
187
  private getItem;
187
188
  private getItems;
188
189
  private withPushState;
190
+ private queryListeners;
189
191
  /**
190
192
  * Disposes the collection, unregisters persistence adapters, clears memory, and
191
193
  * cleans up all resources used by the collection.
@@ -19,12 +19,13 @@ export interface CollectionBackend<T extends BaseItem<I>, I> {
19
19
  replaceOne(selector: Selector<T>, replacement: Omit<T, 'id'> & Partial<Pick<T, 'id'>>): Promise<T[]>;
20
20
  removeOne(selector: Selector<T>): Promise<T[]>;
21
21
  removeMany(selector: Selector<T>): Promise<T[]>;
22
- registerQuery<O extends QueryOptions<T>>(selector: Selector<T>, options?: O): void;
23
- unregisterQuery<O extends QueryOptions<T>>(selector: Selector<T>, options?: O): void;
24
- getQueryState<O extends QueryOptions<T>>(selector: Selector<T>, options?: O): 'active' | 'complete' | 'error';
25
- getQueryError<O extends QueryOptions<T>>(selector: Selector<T>, options?: O): Error | null;
26
- getQueryResult<O extends QueryOptions<T>>(selector: Selector<T>, options?: O): T[];
27
- onQueryStateChange<O extends QueryOptions<T>>(selector: Selector<T> | undefined, options: O | undefined, callback: (state: 'active' | 'complete' | 'error') => void): () => void;
22
+ registerQuery<O extends QueryOptions<T>>(selector: Selector<T>, options: O): void;
23
+ unregisterQuery<O extends QueryOptions<T>>(selector: Selector<T>, options: O): void;
24
+ getQueryState<O extends QueryOptions<T>>(selector: Selector<T>, options: O): 'active' | 'complete' | 'error';
25
+ getQueryError<O extends QueryOptions<T>>(selector: Selector<T>, options: O): Error | null;
26
+ getQueryResult<O extends QueryOptions<T>>(selector: Selector<T>, options: O): T[];
27
+ executeQuery<O extends QueryOptions<T>>(selector: Selector<T>, options: O): Promise<T[]>;
28
+ onQueryStateChange<O extends QueryOptions<T>>(selector: Selector<T>, options: O, callback: (state: 'active' | 'complete' | 'error') => void): () => void;
28
29
  dispose(): Promise<void>;
29
30
  isReady(): Promise<void>;
30
31
  }
@@ -26,6 +26,7 @@ export default class DefaultDataAdapter implements DataAdapter {
26
26
  private queryItems;
27
27
  private executeQuery;
28
28
  private flushQueuedQueryUpdates;
29
+ private executeAndCacheQuery;
29
30
  private updateQueries;
30
31
  createCollectionBackend<T extends BaseItem<I>, I = any, U = T>(collection: Collection<T, I, U>, indices: string[]): CollectionBackend<T, I>;
31
32
  }
@@ -4,6 +4,7 @@ import type DataAdapter from './DataAdapter';
4
4
  import type { CollectionBackend } from './DataAdapter';
5
5
  interface WorkerDataAdapterOptions {
6
6
  id?: string;
7
+ log?: (message: string, ...args: any[]) => void;
7
8
  }
8
9
  export default class WorkerDataAdapter implements DataAdapter {
9
10
  private worker;
@@ -11,11 +12,13 @@ export default class WorkerDataAdapter implements DataAdapter {
11
12
  private id;
12
13
  private isDisposed;
13
14
  private workerReady;
15
+ private log;
14
16
  private collectionReady;
17
+ private batchExecutionHelpers;
15
18
  private queries;
16
19
  constructor(worker: Worker, options: WorkerDataAdapterOptions);
17
20
  private exec;
18
- private queryListeners;
21
+ private enqueueBatched;
19
22
  private updateQuery;
20
23
  createCollectionBackend<T extends BaseItem<I>, I = any, U = T>(collection: Collection<T, I, U>, indices?: string[]): CollectionBackend<T, I>;
21
24
  }
@@ -11,24 +11,27 @@ interface WorkerDataAdapterHostOptions {
11
11
  id?: string;
12
12
  storage: (name: string) => StorageAdapter<any, any>;
13
13
  onError?: (error: Error) => void;
14
+ log?: (message: string, ...args: any[]) => void;
14
15
  }
15
16
  type CollectionMethods<T extends BaseItem<I>, I = any> = {
16
17
  registerCollection: (collectionName: string, indices: string[]) => Promise<void>;
17
18
  unregisterCollection: (collectionName: string) => Promise<void>;
18
19
  registerQuery: <O extends QueryOptions<T>>(collectionName: string, selector: Selector<T>, options?: O) => Promise<void>;
19
20
  unregisterQuery: <O extends QueryOptions<T>>(collectionName: string, selector: Selector<T>, options?: O) => Promise<void>;
20
- insert: (collectionName: string, item: T) => Promise<T>;
21
- updateOne: (collectionName: string, selector: Selector<T>, modifier: Modifier<T>) => Promise<T[]>;
22
- updateMany: (collectionName: string, selector: Selector<T>, modifier: Modifier<T>) => Promise<T[]>;
23
- replaceOne: (collectionName: string, selector: Selector<T>, replacement: Omit<T, 'id'> & Partial<Pick<T, 'id'>>) => Promise<T[]>;
24
- removeOne: (collectionName: string, selector: Selector<T>) => Promise<T[]>;
25
- removeMany: (collectionName: string, selector: Selector<T>) => Promise<T[]>;
21
+ executeQuery: <O extends QueryOptions<T>>(collectionName: string, selector: Selector<T>, options?: O) => Promise<T[]>;
22
+ insert: (collectionName: string, items: [T][]) => Promise<(T | Error)[]>;
23
+ updateOne: (collectionName: string, args: [Selector<T>, Modifier<T>][]) => Promise<(T[] | Error)[]>;
24
+ updateMany: (collectionName: string, args: [Selector<T>, Modifier<T>][]) => Promise<(T[] | Error)[]>;
25
+ replaceOne: (collectionName: string, args: [Selector<T>, Omit<T, 'id'> & Partial<Pick<T, 'id'>>][]) => Promise<(T[] | Error)[]>;
26
+ removeOne: (collectionName: string, selectors: [Selector<T>][]) => Promise<(T[] | Error)[]>;
27
+ removeMany: (collectionName: string, selectors: [Selector<T>][]) => Promise<(T[] | Error)[]>;
26
28
  isReady: (collectionName: string) => Promise<void>;
27
29
  };
28
30
  export default class WorkerDataAdapterHost<T extends BaseItem<I>, I = any> {
29
31
  private workerContext;
30
32
  private options;
31
33
  private id;
34
+ private log;
32
35
  private storageAdapters;
33
36
  private storageAdapterReady;
34
37
  private collectionIndices;
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
- const createIndex = require("./index.cjs20.js");
3
- const getIndexInfo = require("./index.cjs21.js");
2
+ const createIndex = require("./index.cjs21.js");
3
+ const getIndexInfo = require("./index.cjs22.js");
4
4
  const deepClone = require("./index.cjs19.js");
5
5
  const EventEmitter = require("./index.cjs9.js");
6
6
  const isEqual = require("./index.cjs6.js");
7
- const match = require("./index.cjs22.js");
7
+ const match = require("./index.cjs23.js");
8
8
  const modify = require("./index.cjs7.js");
9
- const project = require("./index.cjs23.js");
10
- const queryId = require("./index.cjs24.js");
9
+ const project = require("./index.cjs24.js");
10
+ const queryId = require("./index.cjs20.js");
11
11
  const serializeValue = require("./index.cjs11.js");
12
12
  const sortItems = require("./index.cjs25.js");
13
13
  function hasPendingUpdates(pendingUpdates) {
@@ -143,14 +143,28 @@ class DefaultDataAdapter {
143
143
  return;
144
144
  this.queuedQueryUpdates.set(collection.name, { added: [], modified: [], removed: [] });
145
145
  const flatItems = [...changes.added, ...changes.modified, ...changes.removed];
146
- const queries = [...this.activeQueries.get(collection.name)?.values() ?? []].filter(({ selector }) => flatItems.some((item) => match(item, selector)));
146
+ const itemIds = new Set(flatItems.map((i) => i.id));
147
+ const queries = [
148
+ ...this.activeQueries.get(collection.name)?.values() ?? []
149
+ ].filter(({ selector, options }) => {
150
+ const idsInQuery = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options))?.map((i) => i.id) ?? [];
151
+ if (idsInQuery.some((id) => itemIds.has(id)))
152
+ return true;
153
+ return flatItems.some((item) => match(item, selector));
154
+ });
147
155
  queries.forEach(({ selector, options }) => {
148
- const emitter = this.queryEmitters.get(collection.name);
149
- if (!emitter)
150
- return;
151
- emitter.emit("change", selector, options, "complete");
156
+ this.executeAndCacheQuery(collection, selector, options);
152
157
  });
153
158
  }
159
+ executeAndCacheQuery(collection, selector, options) {
160
+ const result = this.executeQuery(collection, selector, options);
161
+ this.cachedQueryResults.set(collection.name, this.cachedQueryResults.get(collection.name) || /* @__PURE__ */ new Map());
162
+ this.cachedQueryResults.get(collection.name)?.set(queryId(selector, options), result);
163
+ const emitter = this.queryEmitters.get(collection.name);
164
+ if (!emitter)
165
+ return;
166
+ emitter.emit("change", selector, options, "complete");
167
+ }
154
168
  updateQueries(collection, changes) {
155
169
  this.queuedQueryUpdates.set(collection.name, this.queuedQueryUpdates.get(collection.name) || { added: [], modified: [], removed: [] });
156
170
  this.queuedQueryUpdates.get(collection.name)?.added.push(...changes.added);
@@ -162,6 +176,7 @@ class DefaultDataAdapter {
162
176
  this.ensureStorageAdapter(collection.name);
163
177
  this.items.set(collection.name, this.items.get(collection.name) ?? /* @__PURE__ */ new Map());
164
178
  this.queryEmitters.set(collection.name, this.queryEmitters.get(collection.name) ?? new EventEmitter());
179
+ this.queryEmitters.get(collection.name)?.setMaxListeners(Infinity);
165
180
  this.activeQueries.set(collection.name, this.activeQueries.get(collection.name) || /* @__PURE__ */ new Map());
166
181
  this.indices.set(collection.name, indices.map((field) => createIndex(field)));
167
182
  this.rebuildIndices(collection);
@@ -207,7 +222,7 @@ class DefaultDataAdapter {
207
222
  },
208
223
  updateMany: async (selector, modifier) => {
209
224
  const { $setOnInsert, ...restModifier } = modifier;
210
- const items = backend.getQueryResult(selector);
225
+ const items = this.executeQuery(collection, selector, {});
211
226
  const changedItems = items.map((item) => {
212
227
  const modifiedItem = modify(deepClone.default(item), restModifier);
213
228
  const hasItemWithSameId = item.id !== modifiedItem.id && this.getItem(collection, { id: modifiedItem.id }) != null;
@@ -263,7 +278,7 @@ class DefaultDataAdapter {
263
278
  return [item];
264
279
  },
265
280
  removeMany: async (selector) => {
266
- const items = backend.getQueryResult(selector);
281
+ const items = backend.getQueryResult(selector, {});
267
282
  items.forEach((item) => {
268
283
  this.items.get(collection.name)?.delete(serializeValue(item.id));
269
284
  });
@@ -283,10 +298,7 @@ class DefaultDataAdapter {
283
298
  selector,
284
299
  options
285
300
  });
286
- const emitter = this.queryEmitters.get(collection.name);
287
- if (!emitter)
288
- throw new Error(`Query emitter not found for collection ${collection.name}`);
289
- emitter.emit("change", selector, options, "complete");
301
+ this.executeAndCacheQuery(collection, selector, options);
290
302
  },
291
303
  unregisterQuery: (selector, options) => {
292
304
  if (!this.activeQueries.get(collection.name))
@@ -299,7 +311,7 @@ class DefaultDataAdapter {
299
311
  if (!emitter)
300
312
  throw new Error(`Query emitter not found for collection ${collection.name}`);
301
313
  const handler = (querySelector, queryOptions, state) => {
302
- if (querySelector !== selector || queryOptions !== options)
314
+ if (queryId(querySelector, queryOptions) !== queryId(selector, options))
303
315
  return;
304
316
  callback(state);
305
317
  };
@@ -310,14 +322,13 @@ class DefaultDataAdapter {
310
322
  },
311
323
  getQueryError: () => null,
312
324
  getQueryResult: (selector, options) => {
313
- const result = this.executeQuery(collection, selector, options);
314
325
  const isQueryActive = this.activeQueries.get(collection.name)?.has(queryId(selector, options));
315
326
  if (isQueryActive) {
316
- this.cachedQueryResults.set(collection.name, this.cachedQueryResults.get(collection.name) || /* @__PURE__ */ new Map());
317
- this.cachedQueryResults.get(collection.name)?.set(queryId(selector, options), result);
327
+ return this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options)) ?? [];
318
328
  }
319
- return result;
329
+ return this.executeQuery(collection, selector, options);
320
330
  },
331
+ executeQuery: (selector, options) => Promise.resolve(this.executeQuery(collection, selector, options)),
321
332
  // lifecycle methods
322
333
  dispose: async () => {
323
334
  const adapter = this.storageAdapters.get(collection.name);
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  const deepClone = require("./index.cjs19.js");
3
- const match = require("./index.cjs22.js");
3
+ const match = require("./index.cjs23.js");
4
4
  const modify = require("./index.cjs7.js");
5
- const queryId = require("./index.cjs24.js");
5
+ const queryId = require("./index.cjs20.js");
6
6
  const isEqual = require("./index.cjs6.js");
7
- const getIndexInfo = require("./index.cjs21.js");
7
+ const getIndexInfo = require("./index.cjs22.js");
8
8
  const getMatchingKeys = require("./index.cjs26.js");
9
9
  const sortItems = require("./index.cjs25.js");
10
- const project = require("./index.cjs23.js");
10
+ const project = require("./index.cjs24.js");
11
11
  class AsyncDataAdapter {
12
12
  options;
13
13
  id;
@@ -120,6 +120,13 @@ class AsyncDataAdapter {
120
120
  getQueryError,
121
121
  getQueryResult,
122
122
  onQueryStateChange,
123
+ executeQuery: async (selector, options) => {
124
+ await ready;
125
+ registerQuery(selector, options);
126
+ const result = getQueryResult(selector, options);
127
+ unregisterQuery(selector, options);
128
+ return result;
129
+ },
123
130
  dispose: async () => {
124
131
  this.storageAdapters.delete(collection.name);
125
132
  this.queries.delete(collection.name);
@@ -135,10 +142,7 @@ class AsyncDataAdapter {
135
142
  const storage = this.storageAdapters.get(collectionName);
136
143
  if (!storage)
137
144
  throw new Error(`No persistence adapter for collection ${collectionName}`);
138
- await Promise.all([
139
- storage.createIndex("id"),
140
- ...indices.map((index) => storage.createIndex(index))
141
- ]);
145
+ await Promise.all(indices.map((index) => storage.createIndex(index)));
142
146
  await storage.setup();
143
147
  }
144
148
  ensureStorageAdapter(name) {
@@ -200,20 +204,28 @@ class AsyncDataAdapter {
200
204
  rec.items = items;
201
205
  }
202
206
  async getIndexInfo(collectionName, selector) {
203
- const storage = this.storageAdapters.get(collectionName);
204
- if (!storage)
207
+ const storageAdapter = this.storageAdapters.get(collectionName);
208
+ if (!storageAdapter)
205
209
  throw new Error(`No persistence adapter for collection ${collectionName}`);
206
210
  if (selector != null && Object.keys(selector).length === 1 && "id" in selector && typeof selector.id !== "object") {
207
- return { matched: true, ids: [selector.id], optimizedSelector: {} };
211
+ return {
212
+ matched: true,
213
+ ids: [selector.id].filter(Boolean),
214
+ optimizedSelector: {}
215
+ };
208
216
  }
209
217
  if (selector == null) {
210
- return { matched: false, ids: [], optimizedSelector: {} };
218
+ return {
219
+ matched: false,
220
+ ids: [],
221
+ optimizedSelector: {}
222
+ };
211
223
  }
212
224
  const indices = this.collectionIndices.get(collectionName) ?? [];
213
225
  return getIndexInfo.default(indices.map((field) => async (flatSelector) => {
214
226
  if (!Object.hasOwnProperty.call(flatSelector, field))
215
227
  return { matched: false };
216
- const index = await storage.readIndex(field);
228
+ const index = await storageAdapter.readIndex(field);
217
229
  const fieldSelector = flatSelector[field];
218
230
  const filtersForNull = fieldSelector == null || fieldSelector.$exists === false;
219
231
  const keys = filtersForNull ? { include: null, exclude: [...index.keys()].filter((key) => key != null) } : getMatchingKeys(field, flatSelector);
@@ -1,18 +1,24 @@
1
1
  "use strict";
2
- const queryId = require("./index.cjs24.js");
2
+ const queryId = require("./index.cjs20.js");
3
3
  const randomId = require("./index.cjs8.js");
4
+ const batchOnNextTick = require("./index.cjs27.js");
4
5
  class WorkerDataAdapter {
5
6
  worker;
6
7
  options;
7
8
  id;
8
9
  isDisposed = false;
9
10
  workerReady;
11
+ log = () => {
12
+ };
10
13
  collectionReady = /* @__PURE__ */ new Map();
14
+ batchExecutionHelpers = /* @__PURE__ */ new Map();
11
15
  queries = {};
12
16
  constructor(worker, options) {
13
17
  this.worker = worker;
14
18
  this.options = options;
15
19
  this.id = this.options.id || "default-worker-data-adapter";
20
+ if (this.options.log)
21
+ this.log = this.options.log;
16
22
  this.workerReady = new Promise((resolve, reject) => {
17
23
  const timeoutId = setTimeout(() => {
18
24
  reject(new Error("WorkerDataAdapter initialization timed out"));
@@ -51,6 +57,7 @@ class WorkerDataAdapter {
51
57
  return;
52
58
  if (id !== messageId)
53
59
  return;
60
+ this.log(method, "result", data ?? error);
54
61
  if (error) {
55
62
  reject(error);
56
63
  } else {
@@ -67,74 +74,61 @@ class WorkerDataAdapter {
67
74
  });
68
75
  });
69
76
  }
70
- queryListeners(collectionName, query, listeners) {
71
- if (listeners != null) {
72
- return this.updateQuery(collectionName, query, { listeners });
73
- }
74
- const id = queryId(query.selector, query.options);
75
- const collectionQueries = this.queries[collectionName];
76
- if (!collectionQueries)
77
- return 0;
78
- const existing = collectionQueries.get(id);
79
- return existing?.listeners || 0;
77
+ enqueueBatched(collectionName, method, args) {
78
+ const helper = this.batchExecutionHelpers.get(collectionName);
79
+ if (!helper)
80
+ throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
81
+ return helper.enqueue(method, args);
80
82
  }
83
+ // ---------- end batching integration ----------
81
84
  updateQuery(collectionName, query, update) {
82
85
  const id = queryId(query.selector, query.options);
83
86
  const collectionQueries = this.queries[collectionName];
84
87
  if (!collectionQueries)
85
88
  return;
86
89
  const existing = collectionQueries.get(id);
87
- collectionQueries.set(id, {
88
- listeners: 0,
90
+ const newState = {
89
91
  state: "active",
90
92
  error: null,
91
93
  items: [],
92
94
  ...existing,
93
95
  ...update
94
- });
96
+ };
97
+ collectionQueries.set(id, newState);
95
98
  this.queries[collectionName] = collectionQueries;
96
99
  }
97
100
  createCollectionBackend(collection, indices = []) {
98
101
  this.queries[collection.name] = /* @__PURE__ */ new Map();
99
102
  void this.exec("registerCollection", collection.name, indices);
100
103
  this.collectionReady.set(collection.name, this.exec("isReady", collection.name));
104
+ this.batchExecutionHelpers.set(collection.name, batchOnNextTick(async (method, args) => this.exec(method, collection.name, args)));
101
105
  return {
102
106
  insert: async (item) => {
103
- return this.exec("insert", collection.name, item);
107
+ return this.enqueueBatched(collection.name, "insert", [item]);
104
108
  },
105
109
  updateOne: async (selector, modifier) => {
106
- return this.exec("updateOne", collection.name, selector, modifier);
110
+ return this.enqueueBatched(collection.name, "updateOne", [selector, modifier]);
107
111
  },
108
112
  updateMany: async (selector, modifier) => {
109
- return this.exec("updateMany", collection.name, selector, modifier);
113
+ return this.enqueueBatched(collection.name, "updateMany", [selector, modifier]);
110
114
  },
111
115
  replaceOne: async (selector, replacement) => {
112
- return this.exec("replaceOne", collection.name, selector, replacement);
116
+ return this.enqueueBatched(collection.name, "replaceOne", [selector, replacement]);
113
117
  },
114
118
  removeOne: async (selector) => {
115
- return this.exec("removeOne", collection.name, selector);
119
+ return this.enqueueBatched(collection.name, "removeOne", [selector]);
116
120
  },
117
121
  removeMany: async (selector) => {
118
- return this.exec("removeMany", collection.name, selector);
122
+ return this.enqueueBatched(collection.name, "removeMany", [selector]);
119
123
  },
120
124
  // methods for registering and unregistering queries that will be called from the collection during find/findOne
121
125
  registerQuery: (selector, options) => {
122
- const listeners = this.queryListeners(collection.name, { selector, options });
123
- if (listeners === 0) {
124
- this.updateQuery(collection.name, { selector, options }, { state: "active", error: null, items: [] });
125
- void this.exec("registerQuery", collection.name, selector, options);
126
- }
127
- this.queryListeners(collection.name, { selector, options }, listeners + 1);
126
+ this.updateQuery(collection.name, { selector, options }, { state: "active", error: null, items: [] });
127
+ void this.exec("registerQuery", collection.name, selector, options);
128
128
  },
129
129
  unregisterQuery: (selector, options) => {
130
- setTimeout(() => {
131
- const listeners = this.queryListeners(collection.name, { selector, options });
132
- const newListeners = Math.max(0, listeners - 1);
133
- if (newListeners === 0) {
134
- this.queries[collection.name]?.delete(queryId(selector, options));
135
- void this.exec("unregisterQuery", collection.name, selector, options);
136
- }
137
- }, 0);
130
+ this.queries[collection.name]?.delete(queryId(selector, options));
131
+ void this.exec("unregisterQuery", collection.name, selector, options);
138
132
  },
139
133
  getQueryState: (selector, options) => {
140
134
  const query = this.queries[collection.name]?.get(queryId(selector, options));
@@ -160,10 +154,9 @@ class WorkerDataAdapter {
160
154
  return;
161
155
  if (collectionName !== collection.name)
162
156
  return;
163
- if (JSON.stringify(responseSelector) !== JSON.stringify(selector))
164
- return;
165
- if (JSON.stringify(responseOptions) !== JSON.stringify(options))
157
+ if (queryId(responseSelector, responseOptions) !== queryId(selector, options))
166
158
  return;
159
+ this.log("queryUpdate", responseSelector, responseOptions, state, data ?? error);
167
160
  this.updateQuery(collection.name, {
168
161
  selector: responseSelector,
169
162
  options: responseOptions
@@ -175,6 +168,7 @@ class WorkerDataAdapter {
175
168
  this.worker.removeEventListener("message", handler);
176
169
  };
177
170
  },
171
+ executeQuery: (selector, options) => this.exec("executeQuery", collection.name, selector, options),
178
172
  // lifecycle methods
179
173
  dispose: async () => {
180
174
  await this.exec("unregisterCollection", collection.name);