@signaldb/svelte 2.0.0-beta.1 → 2.0.0-beta.10

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 (93) hide show
  1. package/dist/base/core/src/AsyncDataAdapter.d.ts +103 -0
  2. package/dist/base/core/src/AsyncDataAdapter.js +518 -0
  3. package/dist/base/core/src/AutoFetchDataAdapter.d.ts +112 -0
  4. package/dist/base/core/src/AutoFetchDataAdapter.js +652 -0
  5. package/dist/base/core/src/Collection/Cursor.d.ts +113 -0
  6. package/dist/base/core/src/Collection/Cursor.js +293 -0
  7. package/dist/base/core/src/Collection/Observer.d.ts +64 -0
  8. package/dist/base/core/src/Collection/Observer.js +181 -0
  9. package/dist/base/core/src/Collection/index.d.ts +300 -0
  10. package/dist/base/core/src/Collection/index.js +625 -0
  11. package/dist/base/core/src/Collection/types.d.ts +28 -0
  12. package/dist/base/core/src/Collection/types.js +2 -0
  13. package/dist/base/core/src/DataAdapter.d.ts +41 -0
  14. package/dist/base/core/src/DataAdapter.js +2 -0
  15. package/dist/base/core/src/DefaultDataAdapter.d.ts +35 -0
  16. package/dist/base/core/src/DefaultDataAdapter.js +398 -0
  17. package/dist/base/core/src/WorkerDataAdapter.d.ts +25 -0
  18. package/dist/base/core/src/WorkerDataAdapter.js +211 -0
  19. package/dist/base/core/src/WorkerDataAdapterHost.d.ts +62 -0
  20. package/dist/base/core/src/WorkerDataAdapterHost.js +409 -0
  21. package/dist/base/core/src/createIndex.d.ts +7 -0
  22. package/dist/base/core/src/createIndex.js +120 -0
  23. package/dist/base/core/src/createIndexProvider.d.ts +8 -0
  24. package/dist/base/core/src/createIndexProvider.js +11 -0
  25. package/dist/base/core/src/createReactivityAdapter.d.ts +8 -0
  26. package/dist/base/core/src/createReactivityAdapter.js +11 -0
  27. package/dist/base/core/src/createStorageAdapter.d.ts +9 -0
  28. package/dist/base/core/src/createStorageAdapter.js +11 -0
  29. package/dist/base/core/src/getIndexInfo.d.ts +39 -0
  30. package/dist/base/core/src/getIndexInfo.js +176 -0
  31. package/dist/base/core/src/index.d.ts +22 -0
  32. package/dist/base/core/src/index.js +39 -0
  33. package/dist/base/core/src/types/Dependency.d.ts +4 -0
  34. package/dist/base/core/src/types/Dependency.js +2 -0
  35. package/dist/base/core/src/types/IndexProvider.d.ts +26 -0
  36. package/dist/base/core/src/types/IndexProvider.js +2 -0
  37. package/dist/base/core/src/types/Modifier.d.ts +46 -0
  38. package/dist/base/core/src/types/Modifier.js +3 -0
  39. package/dist/base/core/src/types/ReactivityAdapter.d.ts +6 -0
  40. package/dist/base/core/src/types/ReactivityAdapter.js +2 -0
  41. package/dist/base/core/src/types/Selector.d.ts +46 -0
  42. package/dist/base/core/src/types/Selector.js +3 -0
  43. package/dist/base/core/src/types/Signal.d.ts +4 -0
  44. package/dist/base/core/src/types/Signal.js +2 -0
  45. package/dist/base/core/src/types/StorageAdapter.d.ts +20 -0
  46. package/dist/base/core/src/types/StorageAdapter.js +2 -0
  47. package/dist/base/core/src/utils/EventEmitter.d.ts +71 -0
  48. package/dist/base/core/src/utils/EventEmitter.js +144 -0
  49. package/dist/base/core/src/utils/batchOnNextTick.d.ts +16 -0
  50. package/dist/base/core/src/utils/batchOnNextTick.js +72 -0
  51. package/dist/base/core/src/utils/compact.d.ts +9 -0
  52. package/dist/base/core/src/utils/compact.js +21 -0
  53. package/dist/base/core/src/utils/createSignal.d.ts +14 -0
  54. package/dist/base/core/src/utils/createSignal.js +38 -0
  55. package/dist/base/core/src/utils/deepClone.d.ts +17 -0
  56. package/dist/base/core/src/utils/deepClone.js +68 -0
  57. package/dist/base/core/src/utils/get.d.ts +9 -0
  58. package/dist/base/core/src/utils/get.js +27 -0
  59. package/dist/base/core/src/utils/getMatchingKeys.d.ts +19 -0
  60. package/dist/base/core/src/utils/getMatchingKeys.js +49 -0
  61. package/dist/base/core/src/utils/intersection.d.ts +9 -0
  62. package/dist/base/core/src/utils/intersection.js +16 -0
  63. package/dist/base/core/src/utils/isEqual.d.ts +14 -0
  64. package/dist/base/core/src/utils/isEqual.js +43 -0
  65. package/dist/base/core/src/utils/isFieldExpression.d.ts +11 -0
  66. package/dist/base/core/src/utils/isFieldExpression.js +52 -0
  67. package/dist/base/core/src/utils/match.d.ts +12 -0
  68. package/dist/base/core/src/utils/match.js +16 -0
  69. package/dist/base/core/src/utils/modify.d.ts +14 -0
  70. package/dist/base/core/src/utils/modify.js +29 -0
  71. package/dist/base/core/src/utils/project.d.ts +15 -0
  72. package/dist/base/core/src/utils/project.js +43 -0
  73. package/dist/base/core/src/utils/queryId.d.ts +9 -0
  74. package/dist/base/core/src/utils/queryId.js +30 -0
  75. package/dist/base/core/src/utils/randomId.d.ts +7 -0
  76. package/dist/base/core/src/utils/randomId.js +12 -0
  77. package/dist/base/core/src/utils/reactiveOrAsync.d.ts +59 -0
  78. package/dist/base/core/src/utils/reactiveOrAsync.js +93 -0
  79. package/dist/base/core/src/utils/serializeValue.d.ts +12 -0
  80. package/dist/base/core/src/utils/serializeValue.js +27 -0
  81. package/dist/base/core/src/utils/set.d.ts +13 -0
  82. package/dist/base/core/src/utils/set.js +43 -0
  83. package/dist/base/core/src/utils/sortItems.d.ts +12 -0
  84. package/dist/base/core/src/utils/sortItems.js +23 -0
  85. package/dist/base/core/src/utils/uniqueBy.d.ts +10 -0
  86. package/dist/base/core/src/utils/uniqueBy.js +19 -0
  87. package/dist/index.cjs +38 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.mjs +38 -0
  90. package/dist/index.mjs.map +1 -0
  91. package/dist/reactivity-adapters/svelte/index.svelte.d.ts +10 -0
  92. package/dist/reactivity-adapters/svelte/index.svelte.js +36 -0
  93. package/package.json +3 -3
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const queryId_1 = __importDefault(require("./utils/queryId"));
7
+ const randomId_1 = __importDefault(require("./utils/randomId"));
8
+ const batchOnNextTick_1 = __importDefault(require("./utils/batchOnNextTick"));
9
+ class WorkerDataAdapter {
10
+ worker;
11
+ options;
12
+ id;
13
+ isDisposed = false;
14
+ workerReady;
15
+ log = () => { };
16
+ collectionReady = new Map();
17
+ batchExecutionHelpers = new Map();
18
+ queries = {};
19
+ constructor(worker, options) {
20
+ this.worker = worker;
21
+ this.options = options;
22
+ this.id = this.options.id || 'default-worker-data-adapter';
23
+ if (this.options.log)
24
+ this.log = this.options.log;
25
+ this.workerReady = new Promise((resolve, reject) => {
26
+ const timeoutId = setTimeout(() => {
27
+ reject(new Error('WorkerDataAdapter initialization timed out'));
28
+ }, 5000);
29
+ const handleMessage = (event) => {
30
+ const { type, workerId } = event.data;
31
+ if (workerId !== this.id)
32
+ return;
33
+ if (type === 'ready') {
34
+ resolve();
35
+ clearTimeout(timeoutId);
36
+ this.worker.removeEventListener('message', handleMessage);
37
+ }
38
+ };
39
+ this.worker.addEventListener('message', handleMessage);
40
+ });
41
+ }
42
+ async exec(method, collectionName, ...args) {
43
+ await this.workerReady;
44
+ if (method !== 'isReady') {
45
+ const collectionReady = this.collectionReady.get(collectionName);
46
+ if (!collectionReady)
47
+ throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
48
+ await collectionReady;
49
+ }
50
+ if (this.isDisposed) {
51
+ throw new Error('WorkerDataAdapter is disposed');
52
+ }
53
+ return new Promise((resolve, reject) => {
54
+ const messageId = (0, randomId_1.default)();
55
+ const handleMessage = (event) => {
56
+ const { id, workerId, type, data, error } = event.data;
57
+ if (workerId !== this.id)
58
+ return;
59
+ if (type !== 'response')
60
+ return;
61
+ if (id !== messageId)
62
+ return;
63
+ this.log(method, 'result', data ?? error);
64
+ if (error) {
65
+ reject(error);
66
+ }
67
+ else {
68
+ resolve(data);
69
+ }
70
+ this.worker.removeEventListener('message', handleMessage);
71
+ };
72
+ this.worker.addEventListener('message', handleMessage);
73
+ this.worker.postMessage({
74
+ id: messageId,
75
+ workerId: this.id,
76
+ method,
77
+ args: [collectionName, ...args],
78
+ });
79
+ });
80
+ }
81
+ enqueueBatched(collectionName, method, args) {
82
+ const helper = this.batchExecutionHelpers.get(collectionName);
83
+ if (!helper)
84
+ throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
85
+ return helper.enqueue(method, args);
86
+ }
87
+ // ---------- end batching integration ----------
88
+ updateQuery(collectionName, query, update) {
89
+ const id = (0, queryId_1.default)(query.selector, query.options);
90
+ const collectionQueries = this.queries[collectionName];
91
+ if (!collectionQueries)
92
+ return;
93
+ const existing = collectionQueries.get(id);
94
+ const newState = {
95
+ state: 'active',
96
+ error: null,
97
+ items: [],
98
+ stateChangeCallbacks: [],
99
+ eventHandler: existing?.eventHandler,
100
+ ...existing,
101
+ ...update,
102
+ };
103
+ collectionQueries.set(id, newState);
104
+ this.queries[collectionName] = collectionQueries;
105
+ }
106
+ createCollectionBackend(collection, indices) {
107
+ this.queries[collection.name] = new Map();
108
+ void this.exec('registerCollection', collection.name, indices);
109
+ this.collectionReady.set(collection.name, this.exec('isReady', collection.name));
110
+ this.batchExecutionHelpers.set(collection.name, (0, batchOnNextTick_1.default)(async (method, args) => this.exec(method, collection.name, args)));
111
+ return {
112
+ insert: async (item) => {
113
+ return this.enqueueBatched(collection.name, 'insert', [item]);
114
+ },
115
+ updateOne: async (selector, modifier) => {
116
+ return this.enqueueBatched(collection.name, 'updateOne', [selector, modifier]);
117
+ },
118
+ updateMany: async (selector, modifier) => {
119
+ return this.enqueueBatched(collection.name, 'updateMany', [selector, modifier]);
120
+ },
121
+ replaceOne: async (selector, replacement) => {
122
+ return this.enqueueBatched(collection.name, 'replaceOne', [selector, replacement]);
123
+ },
124
+ removeOne: async (selector) => {
125
+ return this.enqueueBatched(collection.name, 'removeOne', [selector]);
126
+ },
127
+ removeMany: async (selector) => {
128
+ return this.enqueueBatched(collection.name, 'removeMany', [selector]);
129
+ },
130
+ // methods for registering and unregistering queries that will be called from the collection during find/findOne
131
+ registerQuery: (selector, options) => {
132
+ this.updateQuery(collection.name, { selector, options }, { state: 'active', error: null, items: [] });
133
+ void this.exec('registerQuery', collection.name, selector, options);
134
+ const handler = (event) => {
135
+ const { type, data, workerId, error } = event.data;
136
+ if (type !== 'queryUpdate')
137
+ return;
138
+ if (data == null)
139
+ return;
140
+ const { collectionName, selector: responseSelector, options: responseOptions, state, items, } = data;
141
+ if (workerId !== this.id)
142
+ return;
143
+ if (collectionName !== collection.name)
144
+ return;
145
+ if ((0, queryId_1.default)(responseSelector, responseOptions) !== (0, queryId_1.default)(selector, options))
146
+ return;
147
+ this.log('queryUpdate', responseSelector, responseOptions, state, data ?? error);
148
+ this.updateQuery(collection.name, {
149
+ selector: responseSelector,
150
+ options: responseOptions,
151
+ }, { state, error, items });
152
+ const query = this.queries[collection.name]?.get((0, queryId_1.default)(selector, options));
153
+ if (!query)
154
+ return;
155
+ query.stateChangeCallbacks.forEach(callback => callback(state));
156
+ };
157
+ this.worker.addEventListener('message', handler);
158
+ this.updateQuery(collection.name, { selector, options }, { eventHandler: handler });
159
+ },
160
+ unregisterQuery: (selector, options) => {
161
+ const qid = (0, queryId_1.default)(selector, options);
162
+ const query = this.queries[collection.name]?.get(qid);
163
+ if (query?.eventHandler) {
164
+ this.worker.removeEventListener('message', query.eventHandler);
165
+ }
166
+ this.queries[collection.name]?.delete(qid);
167
+ void this.exec('unregisterQuery', collection.name, selector, options);
168
+ },
169
+ getQueryState: (selector, options) => {
170
+ const query = this.queries[collection.name]?.get((0, queryId_1.default)(selector, options));
171
+ return query?.state || 'active';
172
+ },
173
+ getQueryError: (selector, options) => {
174
+ const query = this.queries[collection.name]?.get((0, queryId_1.default)(selector, options));
175
+ return query?.error || null;
176
+ },
177
+ getQueryResult: (selector, options) => {
178
+ const query = this.queries[collection.name]?.get((0, queryId_1.default)(selector, options));
179
+ return query?.items || [];
180
+ },
181
+ onQueryStateChange: (selector, options, callback) => {
182
+ this.updateQuery(collection.name, { selector, options }, {
183
+ stateChangeCallbacks: [
184
+ ...this.queries[collection.name]?.get((0, queryId_1.default)(selector, options))?.stateChangeCallbacks || [],
185
+ callback,
186
+ ],
187
+ });
188
+ return () => {
189
+ const currentCallbacks = this.queries[collection.name]?.get((0, queryId_1.default)(selector, options))?.stateChangeCallbacks;
190
+ if (!currentCallbacks)
191
+ throw new Error('State change callbacks are not defined!');
192
+ this.updateQuery(collection.name, { selector, options }, {
193
+ stateChangeCallbacks: currentCallbacks
194
+ .filter(existingCallback => existingCallback !== callback),
195
+ });
196
+ };
197
+ },
198
+ executeQuery: (selector, options) => this.exec('executeQuery', collection.name, selector, options),
199
+ // lifecycle methods
200
+ dispose: async () => {
201
+ await this.exec('unregisterCollection', collection.name);
202
+ this.isDisposed = true;
203
+ this.worker.terminate();
204
+ },
205
+ isReady: async () => {
206
+ await this.exec('isReady', collection.name);
207
+ },
208
+ };
209
+ }
210
+ }
211
+ exports.default = WorkerDataAdapter;
@@ -0,0 +1,62 @@
1
+ import type { BaseItem } from './Collection';
2
+ import type { QueryOptions } from './DataAdapter';
3
+ import type Modifier from './types/Modifier';
4
+ import type StorageAdapter from './types/StorageAdapter';
5
+ import type Selector from './types/Selector';
6
+ interface WorkerContext {
7
+ addEventListener: (type: 'message', listener: (event: MessageEvent) => any) => void;
8
+ postMessage: (message: any) => void;
9
+ }
10
+ interface WorkerDataAdapterHostOptions {
11
+ id?: string;
12
+ storage: (name: string) => StorageAdapter<any, any>;
13
+ onError?: (error: Error) => void;
14
+ log?: (message: string, ...args: any[]) => void;
15
+ }
16
+ type CollectionMethods<T extends BaseItem<I>, I = any> = {
17
+ registerCollection: (collectionName: string, indices: string[]) => Promise<void>;
18
+ unregisterCollection: (collectionName: string) => Promise<void>;
19
+ registerQuery: <O extends QueryOptions<T>>(collectionName: string, selector: Selector<T>, options?: O) => Promise<void>;
20
+ unregisterQuery: <O extends QueryOptions<T>>(collectionName: string, selector: Selector<T>, options?: O) => Promise<void>;
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)[]>;
28
+ isReady: (collectionName: string) => Promise<void>;
29
+ };
30
+ export default class WorkerDataAdapterHost<T extends BaseItem<I>, I = any> {
31
+ private workerContext;
32
+ private options;
33
+ private id;
34
+ private log;
35
+ private storageAdapters;
36
+ private storageAdapterReady;
37
+ private collectionIndices;
38
+ private queries;
39
+ private onError;
40
+ constructor(workerContext: WorkerContext, options: WorkerDataAdapterHostOptions);
41
+ private respond;
42
+ private handleMessage;
43
+ private getIndexInfo;
44
+ private queryItems;
45
+ private executeQuery;
46
+ private ensureQuery;
47
+ private emitQueryUpdate;
48
+ private ensureStorageAdapter;
49
+ private checkQueryUpdates;
50
+ protected registerCollection: CollectionMethods<T, I>['registerCollection'];
51
+ protected unregisterCollection: CollectionMethods<T, I>['unregisterCollection'];
52
+ protected registerQuery: CollectionMethods<T, I>['registerQuery'];
53
+ protected unregisterQuery: CollectionMethods<T, I>['unregisterQuery'];
54
+ protected insert: CollectionMethods<T, I>['insert'];
55
+ protected updateOne: CollectionMethods<T, I>['updateOne'];
56
+ protected updateMany: CollectionMethods<T, I>['updateMany'];
57
+ protected replaceOne: CollectionMethods<T, I>['replaceOne'];
58
+ protected removeOne: CollectionMethods<T, I>['removeOne'];
59
+ protected removeMany: CollectionMethods<T, I>['removeMany'];
60
+ protected isReady: CollectionMethods<T, I>['isReady'];
61
+ }
62
+ export {};
@@ -0,0 +1,409 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const deepClone_1 = __importDefault(require("./utils/deepClone"));
7
+ const match_1 = __importDefault(require("./utils/match"));
8
+ const modify_1 = __importDefault(require("./utils/modify"));
9
+ const queryId_1 = __importDefault(require("./utils/queryId"));
10
+ const isEqual_1 = __importDefault(require("./utils/isEqual"));
11
+ const getIndexInfo_1 = __importDefault(require("./getIndexInfo"));
12
+ const getMatchingKeys_1 = __importDefault(require("./utils/getMatchingKeys"));
13
+ const sortItems_1 = __importDefault(require("./utils/sortItems"));
14
+ const project_1 = __importDefault(require("./utils/project"));
15
+ const compact_1 = __importDefault(require("./utils/compact"));
16
+ class WorkerDataAdapterHost {
17
+ workerContext;
18
+ options;
19
+ id;
20
+ log = () => { };
21
+ storageAdapters = new Map();
22
+ storageAdapterReady = new Map();
23
+ collectionIndices = new Map();
24
+ queries = new Map();
25
+ onError = (error) => {
26
+ // eslint-disable-next-line no-console
27
+ console.error(error);
28
+ };
29
+ constructor(workerContext, options) {
30
+ this.workerContext = workerContext;
31
+ this.options = options;
32
+ this.id = this.options.id || 'default-worker-data-adapter';
33
+ if (this.options.onError) {
34
+ this.onError = this.options.onError;
35
+ }
36
+ if (this.options.log)
37
+ this.log = this.options.log;
38
+ if (typeof addEventListener === 'undefined' || typeof postMessage === 'undefined') {
39
+ throw new TypeError('WorkerDataAdapterHost can only be used in a Web Worker context');
40
+ }
41
+ this.workerContext.addEventListener('message', async (event) => {
42
+ try {
43
+ const { workerId, id, method, args } = event.data;
44
+ await this.handleMessage(workerId, id, method, args);
45
+ }
46
+ catch (error) {
47
+ this.onError(error);
48
+ }
49
+ });
50
+ this.respond('ready', null, null, 'ready');
51
+ }
52
+ respond(id, data, error = null, type = 'response') {
53
+ this.workerContext.postMessage({ id, workerId: this.id, type, data, error });
54
+ }
55
+ async handleMessage(workerId, id, method, args) {
56
+ if (workerId !== this.id)
57
+ return;
58
+ const fn = this[method];
59
+ if (typeof fn !== 'function') {
60
+ this.respond(id, null, new Error(`Method ${method} not found`));
61
+ return;
62
+ }
63
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
64
+ this.log(method, ...args);
65
+ // wait for the storage adapter to be ready
66
+ await this.isReady(args[0]);
67
+ try {
68
+ const result = await fn.apply(this, args);
69
+ this.respond(id, result);
70
+ }
71
+ catch (error) {
72
+ this.respond(id, null, error);
73
+ }
74
+ }
75
+ async getIndexInfo(collectionName, selector) {
76
+ const storageAdapter = this.storageAdapters.get(collectionName);
77
+ if (!storageAdapter)
78
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
79
+ if (selector != null
80
+ && Object.keys(selector).length === 1
81
+ && 'id' in selector
82
+ && typeof selector.id !== 'object') {
83
+ return {
84
+ matched: true,
85
+ ids: (0, compact_1.default)([selector.id]),
86
+ optimizedSelector: {},
87
+ };
88
+ }
89
+ if (selector == null) {
90
+ return {
91
+ matched: false,
92
+ ids: [],
93
+ optimizedSelector: {},
94
+ };
95
+ }
96
+ const indices = this.collectionIndices.get(collectionName) ?? [];
97
+ return (0, getIndexInfo_1.default)(indices.map(field => async (flatSelector) => {
98
+ if (!Object.hasOwnProperty.call(flatSelector, field)) {
99
+ // If the field is not present in the selector, we can't optimize
100
+ return { matched: false };
101
+ }
102
+ const index = await storageAdapter.readIndex(field);
103
+ const fieldSelector = flatSelector[field];
104
+ const filteresForNull = fieldSelector == null || fieldSelector.$exists === false;
105
+ const keys = filteresForNull
106
+ ? { include: null, exclude: [...index.keys()].filter(key => key != null) }
107
+ : (0, getMatchingKeys_1.default)(field, flatSelector);
108
+ if (keys.include == null && keys.exclude == null)
109
+ return { matched: false };
110
+ // Accumulate included positions
111
+ let includedIds = [];
112
+ if (keys.include == null) {
113
+ for (const set of index.values()) {
114
+ for (const pos of set) {
115
+ includedIds.push(pos);
116
+ }
117
+ }
118
+ }
119
+ else {
120
+ for (const key of keys.include) {
121
+ const idSet = index.get(key);
122
+ if (idSet) {
123
+ for (const id of idSet) {
124
+ includedIds.push(id);
125
+ }
126
+ }
127
+ }
128
+ }
129
+ // If exclusion is specified, build a single set of all positions to exclude.
130
+ if (keys.exclude != null) {
131
+ const excludeIds = new Set();
132
+ for (const key of keys.exclude) {
133
+ const idSet = index.get(key);
134
+ if (idSet) {
135
+ for (const id of idSet) {
136
+ excludeIds.add(id);
137
+ }
138
+ }
139
+ }
140
+ // Filter out any position that exists in the exclude set.
141
+ includedIds = includedIds.filter(pos => !excludeIds.has(pos));
142
+ }
143
+ return {
144
+ matched: true,
145
+ ids: includedIds,
146
+ fields: [field],
147
+ keepSelector: filteresForNull,
148
+ };
149
+ }), selector);
150
+ }
151
+ async queryItems(collectionName, selector) {
152
+ const storageAdapter = this.storageAdapters.get(collectionName);
153
+ if (!storageAdapter)
154
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
155
+ const indexInfo = await this.getIndexInfo(collectionName, selector);
156
+ const matchItems = (item) => {
157
+ if (indexInfo.optimizedSelector == null)
158
+ return true; // if no selector is given, return all items
159
+ if (Object.keys(indexInfo.optimizedSelector).length <= 0)
160
+ return true; // if selector is empty, return all items
161
+ const matches = (0, match_1.default)(item, indexInfo.optimizedSelector);
162
+ return matches;
163
+ };
164
+ if (indexInfo.matched) {
165
+ const items = await storageAdapter.readIds(indexInfo.ids);
166
+ if ((0, isEqual_1.default)(indexInfo.optimizedSelector, {}))
167
+ return items;
168
+ return items.filter(matchItems);
169
+ }
170
+ else {
171
+ const allItems = await storageAdapter.readAll();
172
+ if ((0, isEqual_1.default)(selector, {}))
173
+ return allItems;
174
+ return allItems.filter(matchItems);
175
+ }
176
+ }
177
+ async executeQuery(collectionName, selector, options) {
178
+ // null selector means no matches
179
+ if (selector === null)
180
+ return [];
181
+ const items = await this.queryItems(collectionName, selector || {});
182
+ const { sort, skip, limit, fields } = options || {};
183
+ const sorted = sort ? (0, sortItems_1.default)(items, sort) : items;
184
+ const skipped = skip ? sorted.slice(skip) : sorted;
185
+ const limited = limit ? skipped.slice(0, limit) : skipped;
186
+ const idExcluded = fields && fields.id === 0;
187
+ return limited.map((item) => {
188
+ if (!fields)
189
+ return item;
190
+ return {
191
+ ...idExcluded ? {} : { id: item.id },
192
+ ...(0, project_1.default)(item, fields),
193
+ };
194
+ });
195
+ }
196
+ ensureQuery(collectionName, selector, options) {
197
+ const id = (0, queryId_1.default)(selector, options);
198
+ if (!this.queries.get(collectionName)) {
199
+ throw new Error(`Collection ${collectionName} not initialized!`);
200
+ }
201
+ let query = this.queries.get(collectionName)?.get(id);
202
+ if (!query) {
203
+ query = { selector, options };
204
+ this.queries.get(collectionName)?.set(id, query);
205
+ }
206
+ return query;
207
+ }
208
+ emitQueryUpdate(collectionName, selector, options, state, error, items) {
209
+ const id = (0, queryId_1.default)(selector, options);
210
+ const collectionQueries = this.queries.get(collectionName);
211
+ if (!collectionQueries)
212
+ throw new Error(`Collection ${collectionName} not initialized!`);
213
+ this.respond(id, { collectionName, selector, options, state, error, items }, null, 'queryUpdate');
214
+ }
215
+ ensureStorageAdapter(name) {
216
+ if (this.storageAdapters.has(name))
217
+ return; // already created
218
+ const adapter = this.options.storage(name);
219
+ if (!adapter)
220
+ return; // no adapter returned
221
+ this.storageAdapters.set(name, adapter);
222
+ }
223
+ async checkQueryUpdates(collectionName, items) {
224
+ const queries = this.queries.get(collectionName);
225
+ if (!queries)
226
+ throw new Error(`Collection ${collectionName} not initialized!`);
227
+ const affectedQueries = [...queries.values()].filter(({ selector }) => items.some(item => (0, match_1.default)(item, selector))) ?? [];
228
+ if (affectedQueries.length === 0)
229
+ return; // no active queries affected
230
+ affectedQueries.forEach(({ selector, options }) => {
231
+ this.emitQueryUpdate(collectionName, selector, options, 'active', null);
232
+ });
233
+ await Promise.all(affectedQueries.map(async ({ selector, options }) => {
234
+ const queryItems = await this.executeQuery(collectionName, selector, options);
235
+ this.emitQueryUpdate(collectionName, selector, options, 'complete', null, queryItems);
236
+ }));
237
+ }
238
+ registerCollection = async (collectionName, indices) => {
239
+ this.collectionIndices.set(collectionName, indices);
240
+ this.queries.set(collectionName, new Map());
241
+ this.ensureStorageAdapter(collectionName);
242
+ const storageAdapter = this.storageAdapters.get(collectionName);
243
+ if (!storageAdapter)
244
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
245
+ const setupPromise = (async () => {
246
+ await storageAdapter.setup();
247
+ await Promise.all(indices.map(index => storageAdapter.createIndex(index)));
248
+ })();
249
+ this.storageAdapterReady.set(collectionName, setupPromise);
250
+ await setupPromise;
251
+ };
252
+ unregisterCollection = async (collectionName) => {
253
+ this.storageAdapters.delete(collectionName);
254
+ this.queries.delete(collectionName);
255
+ };
256
+ registerQuery = async (collectionName, selector, options) => {
257
+ this.ensureQuery(collectionName, selector, options);
258
+ const queryItems = await this.executeQuery(collectionName, selector, options);
259
+ this.emitQueryUpdate(collectionName, selector, options, 'complete', null, queryItems);
260
+ };
261
+ unregisterQuery = async (collectionName, selector, options) => {
262
+ const id = (0, queryId_1.default)(selector, options);
263
+ if (!this.queries.get(collectionName))
264
+ throw new Error(`Collection ${collectionName} not initialized!`);
265
+ this.queries.get(collectionName)?.delete(id);
266
+ };
267
+ insert = async (collectionName, input) => {
268
+ const storageAdapter = this.storageAdapters.get(collectionName);
269
+ if (!storageAdapter)
270
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
271
+ const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map(i => i[0].id) } });
272
+ const result = input.map(([item]) => {
273
+ if (item.id == null)
274
+ return new Error('Item must have an id');
275
+ if (existingItems.some(existing => existing.id === item.id)) {
276
+ return new Error(`Item with id ${item.id} already exists`);
277
+ }
278
+ return item;
279
+ });
280
+ const newItems = result.filter(item => !(item instanceof Error));
281
+ await storageAdapter.insert(newItems);
282
+ await this.checkQueryUpdates(collectionName, newItems);
283
+ return result;
284
+ };
285
+ updateOne = async (collectionName, parameters) => {
286
+ const storageAdapter = this.storageAdapters.get(collectionName);
287
+ if (!storageAdapter)
288
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
289
+ const previousItems = [];
290
+ const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
291
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then(items => items[0] ?? null);
292
+ const { $setOnInsert, ...restModifier } = modifier;
293
+ if (item == null)
294
+ return [];
295
+ previousItems.push(item);
296
+ const modifiedItem = (0, modify_1.default)((0, deepClone_1.default)(item), restModifier);
297
+ if (item.id !== modifiedItem.id) {
298
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
299
+ if (existingItems.length > 0) {
300
+ return new Error(`Item with id ${modifiedItem.id} already exists`);
301
+ }
302
+ }
303
+ return [modifiedItem];
304
+ }));
305
+ const modifiedItems = (0, compact_1.default)(result.filter(item => !(item instanceof Error)).flat());
306
+ if (modifiedItems.length > 0) {
307
+ await storageAdapter.replace(modifiedItems);
308
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
309
+ }
310
+ return result;
311
+ };
312
+ updateMany = async (collectionName, parameters) => {
313
+ const storageAdapter = this.storageAdapters.get(collectionName);
314
+ if (!storageAdapter)
315
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
316
+ const previousItems = [];
317
+ const result = await Promise.all(parameters.map(async ([selector, modifier]) => {
318
+ const items = await this.executeQuery(collectionName, selector);
319
+ if (items.length === 0)
320
+ return []; // no items found, nothing to update
321
+ const { $setOnInsert, ...restModifier } = modifier;
322
+ try {
323
+ const changedItems = await Promise.all(items.map(async (item) => {
324
+ const modifiedItem = (0, modify_1.default)((0, deepClone_1.default)(item), restModifier);
325
+ if (item.id !== modifiedItem.id) {
326
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
327
+ if (existingItems.length > 0) {
328
+ throw new Error(`Item with id ${modifiedItem.id} already exists`);
329
+ }
330
+ }
331
+ previousItems.push(item);
332
+ return modifiedItem;
333
+ }));
334
+ return changedItems;
335
+ }
336
+ catch (error) {
337
+ return error;
338
+ }
339
+ }));
340
+ const modifiedItems = (0, compact_1.default)(result.filter(item => !(item instanceof Error)).flat());
341
+ if (modifiedItems.length > 0) {
342
+ await storageAdapter.replace(modifiedItems);
343
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
344
+ }
345
+ return result;
346
+ };
347
+ replaceOne = async (collectionName, parameters) => {
348
+ const storageAdapter = this.storageAdapters.get(collectionName);
349
+ if (!storageAdapter)
350
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
351
+ const previousItems = [];
352
+ const result = await Promise.all(parameters.map(async ([selector, replacement,]) => {
353
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then(items => items[0] ?? null);
354
+ if (item == null)
355
+ return []; // no item found, nothing to update
356
+ previousItems.push(item);
357
+ const modifiedItem = {
358
+ ...replacement,
359
+ id: replacement.id ?? item.id,
360
+ };
361
+ if (item.id !== modifiedItem.id) {
362
+ const existingItems = await this.executeQuery(collectionName, { id: modifiedItem.id }, { limit: 1 });
363
+ if (existingItems.length > 0) {
364
+ return new Error(`Item with id ${modifiedItem.id} already exists`);
365
+ }
366
+ }
367
+ return [modifiedItem];
368
+ }));
369
+ const modifiedItems = (0, compact_1.default)(result.filter(item => !(item instanceof Error)).flat());
370
+ if (modifiedItems.length > 0) {
371
+ await storageAdapter.replace(modifiedItems);
372
+ await this.checkQueryUpdates(collectionName, [...modifiedItems, ...previousItems]);
373
+ }
374
+ return result;
375
+ };
376
+ removeOne = async (collectionName, selectors) => {
377
+ const storageAdapter = this.storageAdapters.get(collectionName);
378
+ if (!storageAdapter)
379
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
380
+ const result = await Promise.all(selectors.map(async ([selector]) => {
381
+ const item = await this.executeQuery(collectionName, selector, { limit: 1 }).then(items => items[0] ?? null);
382
+ if (item == null)
383
+ return []; // no item found, nothing to remove
384
+ return [item];
385
+ }));
386
+ const items = result.flat();
387
+ if (items.length > 0) {
388
+ await storageAdapter.remove(items);
389
+ await this.checkQueryUpdates(collectionName, items);
390
+ }
391
+ return result;
392
+ };
393
+ removeMany = async (collectionName, selectors) => {
394
+ const storageAdapter = this.storageAdapters.get(collectionName);
395
+ if (!storageAdapter)
396
+ throw new Error(`No persistence adapter for collection ${collectionName}`);
397
+ const result = await Promise.all(selectors.map(async ([selector]) => this.executeQuery(collectionName, selector)));
398
+ const items = result.flat();
399
+ if (items.length > 0) {
400
+ await storageAdapter.remove(items);
401
+ await this.checkQueryUpdates(collectionName, items);
402
+ }
403
+ return result;
404
+ };
405
+ isReady = async (collectionName) => {
406
+ return this.storageAdapterReady.get(collectionName);
407
+ };
408
+ }
409
+ exports.default = WorkerDataAdapterHost;