@signaldb/core 2.0.0-beta.13 → 2.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +72 -46
- package/dist/AsyncDataAdapter.d.ts +11 -2
- package/dist/Collection/Cursor.d.ts +11 -1
- package/dist/Collection/Observer.d.ts +31 -0
- package/dist/DataAdapter.d.ts +15 -2
- package/dist/WorkerDataAdapter.d.ts +25 -2
- package/dist/WorkerDataAdapterHost.d.ts +2 -0
- package/dist/index.cjs.js +15 -15
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +15 -15
- package/dist/index10.cjs.js +7 -19
- package/dist/index10.mjs +7 -19
- package/dist/index11.cjs.js +19 -41
- package/dist/index11.mjs +19 -41
- package/dist/index12.cjs.js +41 -18
- package/dist/index12.mjs +41 -18
- package/dist/index13.cjs.js +18 -41
- package/dist/index13.mjs +18 -41
- package/dist/index14.cjs.js +39 -80
- package/dist/index14.mjs +39 -80
- package/dist/index15.cjs.js +82 -11
- package/dist/index15.mjs +82 -11
- package/dist/index16.cjs.js +11 -132
- package/dist/index16.mjs +11 -132
- package/dist/index17.cjs.js +127 -38
- package/dist/index17.mjs +127 -38
- package/dist/index18.cjs.js +43 -11
- package/dist/index18.mjs +43 -11
- package/dist/index19.cjs.js +10 -18
- package/dist/index19.mjs +11 -19
- package/dist/index20.cjs.js +19 -33
- package/dist/index20.mjs +19 -33
- package/dist/index21.cjs.js +33 -31
- package/dist/index21.mjs +33 -31
- package/dist/index22.cjs.js +31 -21
- package/dist/index22.mjs +31 -21
- package/dist/index23.cjs.js +16 -14
- package/dist/index23.mjs +16 -14
- package/dist/index24.cjs.js +15 -338
- package/dist/index24.mjs +15 -338
- package/dist/index25.cjs.js +122 -554
- package/dist/index25.mjs +121 -554
- package/dist/index26.cjs.js +34 -7
- package/dist/index26.mjs +34 -7
- package/dist/index27.cjs.js +339 -7
- package/dist/index27.mjs +339 -7
- package/dist/index28.cjs.js +550 -83
- package/dist/index28.mjs +550 -82
- package/dist/index29.cjs.js +8 -422
- package/dist/index29.mjs +8 -422
- package/dist/index30.cjs.js +7 -68
- package/dist/index30.mjs +7 -68
- package/dist/index31.cjs.js +86 -28
- package/dist/index31.mjs +85 -28
- package/dist/index32.cjs.js +446 -278
- package/dist/index32.mjs +446 -278
- package/dist/index33.cjs.js +68 -17
- package/dist/index33.mjs +68 -17
- package/dist/index34.cjs.js +460 -304
- package/dist/index34.mjs +460 -304
- package/dist/index35.cjs.js +14 -532
- package/dist/index35.mjs +14 -532
- package/dist/index36.cjs.js +389 -0
- package/dist/index36.mjs +389 -0
- package/dist/index37.cjs.js +539 -0
- package/dist/index37.mjs +539 -0
- package/dist/index4.cjs.js +199 -140
- package/dist/index4.mjs +195 -140
- package/dist/index5.cjs.js +152 -253
- package/dist/index5.mjs +152 -253
- package/dist/index6.cjs.js +267 -89
- package/dist/index6.mjs +267 -89
- package/dist/index7.cjs.js +121 -29
- package/dist/index7.mjs +121 -29
- package/dist/index8.cjs.js +29 -8
- package/dist/index8.mjs +29 -8
- package/dist/index9.cjs.js +8 -7
- package/dist/index9.mjs +8 -7
- package/dist/utils/incrementalQueryUpdate.d.ts +60 -0
- package/dist/utils/projectItems.d.ts +12 -0
- package/dist/utils/queryDelta.d.ts +83 -0
- package/package.json +1 -1
package/dist/index34.cjs.js
CHANGED
|
@@ -1,335 +1,491 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const require_queryDelta = require("./index4.cjs.js");
|
|
2
|
+
const require_randomId = require("./index9.cjs.js");
|
|
3
|
+
const require_deepClone = require("./index18.cjs.js");
|
|
4
|
+
const require_match = require("./index19.cjs.js");
|
|
5
|
+
const require_modify = require("./index20.cjs.js");
|
|
6
|
+
const require_incrementalQueryUpdate = require("./index25.cjs.js");
|
|
7
|
+
const require_queryId = require("./index26.cjs.js");
|
|
8
|
+
const require_batchOnNextTick = require("./index33.cjs.js");
|
|
9
|
+
//#region src/WorkerDataAdapter.ts
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the ids a selector names outright.
|
|
12
|
+
* @param selector - The selector to inspect.
|
|
13
|
+
* @returns The named ids, or `null` when the selector asks more than ids can answer.
|
|
14
|
+
*/
|
|
15
|
+
function selectorIds(selector) {
|
|
16
|
+
if (selector == null || typeof selector !== "object") return null;
|
|
17
|
+
const keys = Object.keys(selector);
|
|
18
|
+
if (keys.length !== 1 || keys[0] !== "id") return null;
|
|
19
|
+
const value = selector.id;
|
|
20
|
+
if (value == null) return null;
|
|
21
|
+
if (typeof value !== "object") return [value];
|
|
22
|
+
const valueKeys = Object.keys(value);
|
|
23
|
+
if (valueKeys.length !== 1 || valueKeys[0] !== "$in") return null;
|
|
24
|
+
const inValues = value.$in;
|
|
25
|
+
return Array.isArray(inValues) ? inValues : null;
|
|
26
|
+
}
|
|
27
|
+
var WorkerDataAdapter = class WorkerDataAdapter {
|
|
28
|
+
worker;
|
|
14
29
|
options;
|
|
15
30
|
id;
|
|
31
|
+
isDisposed = false;
|
|
32
|
+
workerReady;
|
|
16
33
|
log = () => {};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.
|
|
34
|
+
collectionReady = /* @__PURE__ */ new Map();
|
|
35
|
+
batchExecutionHelpers = /* @__PURE__ */ new Map();
|
|
36
|
+
queries = {};
|
|
37
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
38
|
+
pendingWrites = /* @__PURE__ */ new Map();
|
|
39
|
+
pendingWriteSeq = 0;
|
|
40
|
+
pendingWriteVersions = /* @__PURE__ */ new Map();
|
|
41
|
+
bumpPendingWriteVersion(collectionName) {
|
|
42
|
+
const current = this.pendingWriteVersions.get(collectionName) ?? 0;
|
|
43
|
+
this.pendingWriteVersions.set(collectionName, current + 1);
|
|
44
|
+
}
|
|
45
|
+
constructor(worker, options) {
|
|
46
|
+
this.worker = worker;
|
|
26
47
|
this.options = options;
|
|
27
48
|
this.id = this.options.id || "default-worker-data-adapter";
|
|
28
|
-
if (this.options.onError) this.onError = this.options.onError;
|
|
29
49
|
if (this.options.log) this.log = this.options.log;
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.respond("ready", null, null, "ready");
|
|
39
|
-
}
|
|
40
|
-
respond(id, data, error = null, type = "response") {
|
|
41
|
-
this.workerContext.postMessage({
|
|
42
|
-
id,
|
|
43
|
-
workerId: this.id,
|
|
44
|
-
type,
|
|
45
|
-
data,
|
|
46
|
-
error
|
|
50
|
+
this.workerReady = new Promise((resolve, reject) => {
|
|
51
|
+
const timeoutId = setTimeout(() => {
|
|
52
|
+
reject(/* @__PURE__ */ new Error("WorkerDataAdapter initialization timed out"));
|
|
53
|
+
}, 5e3);
|
|
54
|
+
this.resolveWorkerReady = () => {
|
|
55
|
+
clearTimeout(timeoutId);
|
|
56
|
+
resolve();
|
|
57
|
+
};
|
|
47
58
|
});
|
|
59
|
+
this.worker.addEventListener("message", this.handleWorkerMessage);
|
|
48
60
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
61
|
+
resolveWorkerReady = () => {};
|
|
62
|
+
handleWorkerMessage = (event) => {
|
|
63
|
+
const message = event.data;
|
|
64
|
+
if (message == null) return;
|
|
65
|
+
if (message.workerId !== this.id) return;
|
|
66
|
+
if (message.type === "ready") {
|
|
67
|
+
this.resolveWorkerReady();
|
|
54
68
|
return;
|
|
55
69
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
if (message.type === "response") {
|
|
71
|
+
if (message.id == null) return;
|
|
72
|
+
const pending = this.pendingRequests.get(message.id);
|
|
73
|
+
if (!pending) return;
|
|
74
|
+
this.pendingRequests.delete(message.id);
|
|
75
|
+
this.log("response", message.data ?? message.error);
|
|
76
|
+
if (message.error) pending.reject(message.error);
|
|
77
|
+
else pending.resolve(message.data);
|
|
78
|
+
return;
|
|
63
79
|
}
|
|
80
|
+
if (message.type === "queryUpdate") this.handleQueryUpdate(message.data, message.error ?? null);
|
|
81
|
+
};
|
|
82
|
+
handleQueryUpdate(data, error) {
|
|
83
|
+
if (data == null) return;
|
|
84
|
+
const { collectionName, qid, selector, options, state, items, delta } = data;
|
|
85
|
+
if (collectionName == null) return;
|
|
86
|
+
const collectionQueries = this.queries[collectionName];
|
|
87
|
+
if (!collectionQueries) return;
|
|
88
|
+
const id = qid ?? (selector === void 0 ? void 0 : require_queryId.default(selector, options));
|
|
89
|
+
if (id == null) return;
|
|
90
|
+
const query = collectionQueries.get(id);
|
|
91
|
+
if (!query) return;
|
|
92
|
+
this.log("queryUpdate", query.selector, query.options, state, data ?? error);
|
|
93
|
+
let nextItems = items;
|
|
94
|
+
let deltaToPublish;
|
|
95
|
+
if (delta != null) {
|
|
96
|
+
if (!require_queryDelta.canApplyQueryDelta(query.items, delta)) return;
|
|
97
|
+
if (require_queryDelta.isEmptyQueryDelta(delta)) return;
|
|
98
|
+
const servedBefore = this.flattenPendingWrites(collectionName) == null ? null : this.servedResult(collectionName, query);
|
|
99
|
+
nextItems = require_queryDelta.applyQueryDelta(query.items, delta);
|
|
100
|
+
if (servedBefore == null) deltaToPublish = delta;
|
|
101
|
+
else {
|
|
102
|
+
this.updateQuery(collectionName, {
|
|
103
|
+
selector: query.selector,
|
|
104
|
+
options: query.options
|
|
105
|
+
}, {
|
|
106
|
+
state,
|
|
107
|
+
error,
|
|
108
|
+
items: nextItems
|
|
109
|
+
});
|
|
110
|
+
const stored = collectionQueries.get(id);
|
|
111
|
+
if (!stored) return;
|
|
112
|
+
const servedDelta = require_queryDelta.diffQueryResults(servedBefore, this.servedResult(collectionName, stored));
|
|
113
|
+
if (require_queryDelta.isEmptyQueryDelta(servedDelta) && state === query.state) return;
|
|
114
|
+
stored.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, state, servedDelta));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this.updateQuery(collectionName, {
|
|
119
|
+
selector: query.selector,
|
|
120
|
+
options: query.options
|
|
121
|
+
}, {
|
|
122
|
+
state,
|
|
123
|
+
error,
|
|
124
|
+
items: nextItems
|
|
125
|
+
});
|
|
126
|
+
const updated = collectionQueries.get(id);
|
|
127
|
+
if (!updated) return;
|
|
128
|
+
updated.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, state, deltaToPublish));
|
|
64
129
|
}
|
|
65
|
-
async
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
130
|
+
async exec(method, collectionName, ...args) {
|
|
131
|
+
await this.workerReady;
|
|
132
|
+
if (method !== "isReady") {
|
|
133
|
+
const collectionReady = this.collectionReady.get(collectionName);
|
|
134
|
+
if (!collectionReady) throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
|
|
135
|
+
await collectionReady;
|
|
136
|
+
}
|
|
137
|
+
if (this.isDisposed) throw new Error("WorkerDataAdapter is disposed");
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const messageId = require_randomId.default();
|
|
140
|
+
this.pendingRequests.set(messageId, {
|
|
141
|
+
resolve,
|
|
142
|
+
reject
|
|
143
|
+
});
|
|
144
|
+
this.worker.postMessage({
|
|
145
|
+
id: messageId,
|
|
146
|
+
workerId: this.id,
|
|
147
|
+
method,
|
|
148
|
+
args: [collectionName, ...args]
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Issues a call whose result nobody is waiting for, and makes sure a failure has somewhere to
|
|
154
|
+
* go. A bare rejection here would surface as an uncaught error — which is what a disposed
|
|
155
|
+
* collection produced every time a cursor was cleaned up after it.
|
|
156
|
+
* @param method - The method to call on the worker.
|
|
157
|
+
* @param collectionName - The collection it applies to.
|
|
158
|
+
* @param args - The remaining arguments.
|
|
159
|
+
* @param onError - Called when the call fails, in place of merely logging it.
|
|
160
|
+
*/
|
|
161
|
+
execInBackground(method, collectionName, args = [], onError) {
|
|
162
|
+
this.exec(method, collectionName, ...args).catch((error) => {
|
|
163
|
+
if (onError) {
|
|
164
|
+
onError(error);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this.log(method, "failed", error);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
queryItemsById(query) {
|
|
171
|
+
if (!query.itemsById) query.itemsById = new Map(query.items.map((item) => [item.id, item]));
|
|
172
|
+
return query.itemsById;
|
|
173
|
+
}
|
|
174
|
+
flattenPendingWrites(collectionName) {
|
|
175
|
+
const pending = this.pendingWrites.get(collectionName);
|
|
176
|
+
if (!pending || pending.size === 0) return null;
|
|
177
|
+
const upserts = /* @__PURE__ */ new Map();
|
|
178
|
+
const deletes = /* @__PURE__ */ new Set();
|
|
179
|
+
[...pending.entries()].sort(([a], [b]) => a - b).forEach(([, write]) => {
|
|
180
|
+
write.upserts.forEach((item, id) => {
|
|
181
|
+
upserts.set(id, item);
|
|
182
|
+
deletes.delete(id);
|
|
183
|
+
});
|
|
184
|
+
write.deletes.forEach((id) => {
|
|
185
|
+
deletes.add(id);
|
|
186
|
+
upserts.delete(id);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
upserts,
|
|
191
|
+
deletes
|
|
77
192
|
};
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
193
|
+
}
|
|
194
|
+
static providesFullItems(query) {
|
|
195
|
+
return query.options?.fields == null;
|
|
196
|
+
}
|
|
197
|
+
observableItems(collectionName) {
|
|
198
|
+
const byId = /* @__PURE__ */ new Map();
|
|
199
|
+
this.queries[collectionName]?.forEach((query) => {
|
|
200
|
+
if (!WorkerDataAdapter.providesFullItems(query)) return;
|
|
201
|
+
query.items.forEach((item) => byId.set(item.id, item));
|
|
202
|
+
});
|
|
203
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
204
|
+
if (pending) {
|
|
205
|
+
pending.upserts.forEach((item, id) => byId.set(id, item));
|
|
206
|
+
pending.deletes.forEach((id) => byId.delete(id));
|
|
207
|
+
}
|
|
208
|
+
return [...byId.values()];
|
|
209
|
+
}
|
|
210
|
+
observableItemsByIds(collectionName, ids) {
|
|
211
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
212
|
+
const found = /* @__PURE__ */ new Map();
|
|
213
|
+
ids.forEach((id) => {
|
|
214
|
+
if (pending?.deletes.has(id)) return;
|
|
215
|
+
const pendingItem = pending?.upserts.get(id);
|
|
216
|
+
if (pendingItem) {
|
|
217
|
+
found.set(id, pendingItem);
|
|
218
|
+
return;
|
|
93
219
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
220
|
+
const queries = this.queries[collectionName];
|
|
221
|
+
if (!queries) return;
|
|
222
|
+
for (const query of queries.values()) {
|
|
223
|
+
if (!WorkerDataAdapter.providesFullItems(query)) continue;
|
|
224
|
+
const item = this.queryItemsById(query).get(id);
|
|
225
|
+
if (item) {
|
|
226
|
+
found.set(id, item);
|
|
227
|
+
return;
|
|
99
228
|
}
|
|
100
|
-
includedIds = includedIds.filter((pos) => !excludeIds.has(pos));
|
|
101
229
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
ids: includedIds,
|
|
105
|
-
fields: [field],
|
|
106
|
-
keepSelector: filteresForNull
|
|
107
|
-
};
|
|
108
|
-
}), selector);
|
|
230
|
+
});
|
|
231
|
+
return [...found.values()];
|
|
109
232
|
}
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
233
|
+
servedResult(collectionName, query) {
|
|
234
|
+
const pendingVersion = this.pendingWriteVersions.get(collectionName) ?? 0;
|
|
235
|
+
if (query.served && query.served.fromItems === query.items && query.served.pendingVersion === pendingVersion) return query.served.items;
|
|
236
|
+
const items = this.computeServedResult(collectionName, query);
|
|
237
|
+
query.served = {
|
|
238
|
+
items,
|
|
239
|
+
fromItems: query.items,
|
|
240
|
+
pendingVersion
|
|
118
241
|
};
|
|
119
|
-
|
|
120
|
-
const items = await storageAdapter.readIds(indexInfo.ids);
|
|
121
|
-
if (require_isEqual.default(indexInfo.optimizedSelector, {})) return items;
|
|
122
|
-
return items.filter(matchItems);
|
|
123
|
-
} else {
|
|
124
|
-
const allItems = await storageAdapter.readAll();
|
|
125
|
-
if (require_isEqual.default(selector, {})) return allItems;
|
|
126
|
-
return allItems.filter(matchItems);
|
|
127
|
-
}
|
|
242
|
+
return items;
|
|
128
243
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
244
|
+
computeServedResult(collectionName, query) {
|
|
245
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
246
|
+
if (!pending) return query.items;
|
|
247
|
+
const byId = this.queryItemsById(query);
|
|
248
|
+
let affected = false;
|
|
249
|
+
pending.deletes.forEach((id) => {
|
|
250
|
+
if (byId.has(id)) affected = true;
|
|
251
|
+
});
|
|
252
|
+
if (!affected) pending.upserts.forEach((item, id) => {
|
|
253
|
+
if (affected) return;
|
|
254
|
+
if (byId.has(id)) affected = true;
|
|
255
|
+
else if (query.selector != null && require_match.default(item, query.selector)) affected = true;
|
|
256
|
+
});
|
|
257
|
+
if (!affected) return query.items;
|
|
258
|
+
return require_incrementalQueryUpdate.mergeChangesetIntoResult(query.items, query.selector, query.options, {
|
|
259
|
+
upserts: [...pending.upserts.values()],
|
|
260
|
+
deletes: [...pending.deletes]
|
|
143
261
|
});
|
|
144
262
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Registers a write's effect locally and notifies every active query it
|
|
265
|
+
* touches, then returns a function that drops it again once the write
|
|
266
|
+
* settles.
|
|
267
|
+
* @param collectionName - The collection the write targets.
|
|
268
|
+
* @param upserts - Items inserted or updated by the write.
|
|
269
|
+
* @param deletes - Ids removed by the write.
|
|
270
|
+
* @returns A function that drops the pending write and re-notifies.
|
|
271
|
+
*/
|
|
272
|
+
applyPendingWrite(collectionName, upserts, deletes) {
|
|
273
|
+
if (upserts.length === 0 && deletes.length === 0) return () => {};
|
|
274
|
+
const affectedIds = new Set([...upserts.map((item) => item.id), ...deletes]);
|
|
275
|
+
const affected = this.affectedQueries(collectionName, upserts, affectedIds);
|
|
276
|
+
const servedBefore = this.servedResults(collectionName, affected);
|
|
277
|
+
const seq = this.pendingWriteSeq += 1;
|
|
278
|
+
const pending = this.pendingWrites.get(collectionName) ?? /* @__PURE__ */ new Map();
|
|
279
|
+
pending.set(seq, {
|
|
280
|
+
upserts: new Map(upserts.map((item) => [item.id, item])),
|
|
281
|
+
deletes: new Set(deletes)
|
|
282
|
+
});
|
|
283
|
+
this.pendingWrites.set(collectionName, pending);
|
|
284
|
+
this.bumpPendingWriteVersion(collectionName);
|
|
285
|
+
this.notifyWithDeltas(collectionName, affected, servedBefore);
|
|
286
|
+
return () => {
|
|
287
|
+
const current = this.pendingWrites.get(collectionName);
|
|
288
|
+
if (!current) return;
|
|
289
|
+
const affectedOnDrop = this.affectedQueries(collectionName, upserts, affectedIds);
|
|
290
|
+
const beforeDrop = this.servedResults(collectionName, affectedOnDrop);
|
|
291
|
+
current.delete(seq);
|
|
292
|
+
if (current.size === 0) this.pendingWrites.delete(collectionName);
|
|
293
|
+
this.bumpPendingWriteVersion(collectionName);
|
|
294
|
+
this.notifyWithDeltas(collectionName, affectedOnDrop, beforeDrop);
|
|
295
|
+
};
|
|
157
296
|
}
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
297
|
+
affectedQueries(collectionName, upserts, affectedIds) {
|
|
298
|
+
const affected = [];
|
|
299
|
+
this.queries[collectionName]?.forEach((query) => {
|
|
300
|
+
const byId = this.queryItemsById(query);
|
|
301
|
+
let wasHolding = false;
|
|
302
|
+
affectedIds.forEach((id) => {
|
|
303
|
+
if (byId.has(id)) wasHolding = true;
|
|
304
|
+
});
|
|
305
|
+
const nowMatches = upserts.some((item) => query.selector != null && require_match.default(item, query.selector));
|
|
306
|
+
if (!wasHolding && !nowMatches) return;
|
|
307
|
+
affected.push(query);
|
|
308
|
+
});
|
|
309
|
+
return affected;
|
|
169
310
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const adapter = this.options.storage(name);
|
|
173
|
-
if (!adapter) return;
|
|
174
|
-
this.storageAdapters.set(name, adapter);
|
|
311
|
+
servedResults(collectionName, queries) {
|
|
312
|
+
return new Map(queries.map((query) => [query, this.servedResult(collectionName, query)]));
|
|
175
313
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
314
|
+
notifyWithDeltas(collectionName, queries, servedBefore) {
|
|
315
|
+
queries.forEach((query) => {
|
|
316
|
+
const before = servedBefore.get(query);
|
|
317
|
+
if (before == null) return;
|
|
318
|
+
const delta = require_queryDelta.diffQueryResults(before, this.servedResult(collectionName, query));
|
|
319
|
+
if (require_queryDelta.isEmptyQueryDelta(delta)) return;
|
|
320
|
+
query.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, query.state, delta));
|
|
183
321
|
});
|
|
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
322
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this.
|
|
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);
|
|
323
|
+
matchObservableItems(collectionName, selector, onlyFirst) {
|
|
324
|
+
if (selector == null) return [];
|
|
325
|
+
const ids = selectorIds(selector);
|
|
326
|
+
const matches = ids == null ? this.observableItems(collectionName).filter((item) => require_match.default(item, selector)) : this.observableItemsByIds(collectionName, ids);
|
|
327
|
+
return onlyFirst ? matches.slice(0, 1) : matches;
|
|
328
|
+
}
|
|
329
|
+
resolveUpdate(collectionName, selector, modifier, onlyFirst) {
|
|
330
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
331
|
+
const upserts = [];
|
|
332
|
+
const deletes = [];
|
|
333
|
+
this.matchObservableItems(collectionName, selector, onlyFirst).forEach((item) => {
|
|
239
334
|
const modifiedItem = require_modify.default(require_deepClone.default(item), restModifier);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return modifiedItem;
|
|
268
|
-
}));
|
|
269
|
-
} catch (error) {
|
|
270
|
-
return error;
|
|
271
|
-
}
|
|
272
|
-
}));
|
|
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]);
|
|
335
|
+
upserts.push(modifiedItem);
|
|
336
|
+
if (modifiedItem.id !== item.id) deletes.push(item.id);
|
|
337
|
+
});
|
|
338
|
+
return {
|
|
339
|
+
upserts,
|
|
340
|
+
deletes
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
resolveRemoval(collectionName, selector, onlyFirst) {
|
|
344
|
+
return this.matchObservableItems(collectionName, selector, onlyFirst).map((item) => item.id);
|
|
345
|
+
}
|
|
346
|
+
resolveReplacement(item, replacement) {
|
|
347
|
+
const modifiedItem = {
|
|
348
|
+
...replacement,
|
|
349
|
+
id: replacement.id ?? item.id
|
|
350
|
+
};
|
|
351
|
+
return {
|
|
352
|
+
upserts: [modifiedItem],
|
|
353
|
+
deletes: modifiedItem.id === item.id ? [] : [item.id]
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async withPendingWrite(collectionName, delta, run) {
|
|
357
|
+
const dropPendingWrite = this.applyPendingWrite(collectionName, delta.upserts, delta.deletes);
|
|
358
|
+
try {
|
|
359
|
+
return await run();
|
|
360
|
+
} finally {
|
|
361
|
+
dropPendingWrite();
|
|
277
362
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
363
|
+
}
|
|
364
|
+
enqueueBatched(collectionName, method, args) {
|
|
365
|
+
const helper = this.batchExecutionHelpers.get(collectionName);
|
|
366
|
+
if (!helper) throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
|
|
367
|
+
return helper.enqueue(method, args);
|
|
368
|
+
}
|
|
369
|
+
updateQuery(collectionName, query, update) {
|
|
370
|
+
const id = require_queryId.default(query.selector, query.options);
|
|
371
|
+
const collectionQueries = this.queries[collectionName];
|
|
372
|
+
if (!collectionQueries) return;
|
|
373
|
+
const existing = collectionQueries.get(id);
|
|
374
|
+
const newState = {
|
|
375
|
+
selector: query.selector,
|
|
376
|
+
options: query.options,
|
|
377
|
+
state: "active",
|
|
378
|
+
error: null,
|
|
379
|
+
stateChangeCallbacks: [],
|
|
380
|
+
...existing,
|
|
381
|
+
...update,
|
|
382
|
+
...update.items ? {
|
|
383
|
+
items: update.items,
|
|
384
|
+
itemsById: void 0
|
|
385
|
+
} : { items: existing?.items ?? [] }
|
|
386
|
+
};
|
|
387
|
+
collectionQueries.set(id, newState);
|
|
388
|
+
this.queries[collectionName] = collectionQueries;
|
|
389
|
+
}
|
|
390
|
+
createCollectionBackend(collection, indices) {
|
|
391
|
+
this.queries[collection.name] = /* @__PURE__ */ new Map();
|
|
392
|
+
this.execInBackground("registerCollection", collection.name, [indices]);
|
|
393
|
+
this.collectionReady.set(collection.name, this.exec("isReady", collection.name));
|
|
394
|
+
this.batchExecutionHelpers.set(collection.name, require_batchOnNextTick.default(async (method, args) => this.exec(method, collection.name, args)));
|
|
395
|
+
return {
|
|
396
|
+
insert: async (item) => {
|
|
397
|
+
return this.withPendingWrite(collection.name, {
|
|
398
|
+
upserts: [item],
|
|
399
|
+
deletes: []
|
|
400
|
+
}, () => this.enqueueBatched(collection.name, "insert", [item]));
|
|
401
|
+
},
|
|
402
|
+
updateOne: async (selector, modifier) => {
|
|
403
|
+
return this.withPendingWrite(collection.name, this.resolveUpdate(collection.name, selector, modifier, true), () => this.enqueueBatched(collection.name, "updateOne", [selector, modifier]));
|
|
404
|
+
},
|
|
405
|
+
updateMany: async (selector, modifier) => {
|
|
406
|
+
return this.withPendingWrite(collection.name, this.resolveUpdate(collection.name, selector, modifier, false), () => this.enqueueBatched(collection.name, "updateMany", [selector, modifier]));
|
|
407
|
+
},
|
|
408
|
+
replaceOne: async (selector, replacement) => {
|
|
409
|
+
const [item] = this.matchObservableItems(collection.name, selector, true);
|
|
410
|
+
return this.withPendingWrite(collection.name, item == null ? {
|
|
411
|
+
upserts: [],
|
|
412
|
+
deletes: []
|
|
413
|
+
} : this.resolveReplacement(item, replacement), () => this.enqueueBatched(collection.name, "replaceOne", [selector, replacement]));
|
|
414
|
+
},
|
|
415
|
+
removeOne: async (selector) => {
|
|
416
|
+
return this.withPendingWrite(collection.name, {
|
|
417
|
+
upserts: [],
|
|
418
|
+
deletes: this.resolveRemoval(collection.name, selector, true)
|
|
419
|
+
}, () => this.enqueueBatched(collection.name, "removeOne", [selector]));
|
|
420
|
+
},
|
|
421
|
+
removeMany: async (selector) => {
|
|
422
|
+
return this.withPendingWrite(collection.name, {
|
|
423
|
+
upserts: [],
|
|
424
|
+
deletes: this.resolveRemoval(collection.name, selector, false)
|
|
425
|
+
}, () => this.enqueueBatched(collection.name, "removeMany", [selector]));
|
|
426
|
+
},
|
|
427
|
+
registerQuery: (selector, options) => {
|
|
428
|
+
this.updateQuery(collection.name, {
|
|
429
|
+
selector,
|
|
430
|
+
options
|
|
431
|
+
}, {
|
|
432
|
+
state: "active",
|
|
433
|
+
error: null,
|
|
434
|
+
items: []
|
|
435
|
+
});
|
|
436
|
+
this.execInBackground("registerQuery", collection.name, [selector, options], (error) => {
|
|
437
|
+
const query = this.queries[collection.name]?.get(require_queryId.default(selector, options));
|
|
438
|
+
if (!query) return;
|
|
439
|
+
this.updateQuery(collection.name, {
|
|
440
|
+
selector,
|
|
441
|
+
options
|
|
442
|
+
}, {
|
|
443
|
+
state: "error",
|
|
444
|
+
error
|
|
445
|
+
});
|
|
446
|
+
query.stateChangeCallbacks.forEach((callback) => callback("error"));
|
|
447
|
+
});
|
|
448
|
+
},
|
|
449
|
+
unregisterQuery: (selector, options) => {
|
|
450
|
+
this.queries[collection.name]?.delete(require_queryId.default(selector, options));
|
|
451
|
+
this.execInBackground("unregisterQuery", collection.name, [selector, options]);
|
|
452
|
+
},
|
|
453
|
+
getQueryState: (selector, options) => {
|
|
454
|
+
return (this.queries[collection.name]?.get(require_queryId.default(selector, options)))?.state || "active";
|
|
455
|
+
},
|
|
456
|
+
getQueryError: (selector, options) => {
|
|
457
|
+
return (this.queries[collection.name]?.get(require_queryId.default(selector, options)))?.error || null;
|
|
458
|
+
},
|
|
459
|
+
getQueryResult: (selector, options) => {
|
|
460
|
+
const query = this.queries[collection.name]?.get(require_queryId.default(selector, options));
|
|
461
|
+
if (!query) return [];
|
|
462
|
+
return this.servedResult(collection.name, query);
|
|
463
|
+
},
|
|
464
|
+
onQueryStateChange: (selector, options, callback) => {
|
|
465
|
+
this.updateQuery(collection.name, {
|
|
466
|
+
selector,
|
|
467
|
+
options
|
|
468
|
+
}, { stateChangeCallbacks: [...this.queries[collection.name]?.get(require_queryId.default(selector, options))?.stateChangeCallbacks || [], callback] });
|
|
469
|
+
return () => {
|
|
470
|
+
const currentCallbacks = this.queries[collection.name]?.get(require_queryId.default(selector, options))?.stateChangeCallbacks;
|
|
471
|
+
if (!currentCallbacks) return;
|
|
472
|
+
this.updateQuery(collection.name, {
|
|
473
|
+
selector,
|
|
474
|
+
options
|
|
475
|
+
}, { stateChangeCallbacks: currentCallbacks.filter((existingCallback) => existingCallback !== callback) });
|
|
476
|
+
};
|
|
477
|
+
},
|
|
478
|
+
executeQuery: (selector, options) => this.exec("executeQuery", collection.name, selector, options),
|
|
479
|
+
dispose: async () => {
|
|
480
|
+
await this.exec("unregisterCollection", collection.name);
|
|
481
|
+
this.isDisposed = true;
|
|
482
|
+
this.worker.terminate?.();
|
|
483
|
+
},
|
|
484
|
+
isReady: async () => {
|
|
485
|
+
await this.exec("isReady", collection.name);
|
|
294
486
|
}
|
|
295
|
-
|
|
296
|
-
|
|
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]);
|
|
301
|
-
}
|
|
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);
|
|
316
|
-
}
|
|
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
|
-
};
|
|
487
|
+
};
|
|
488
|
+
}
|
|
333
489
|
};
|
|
334
490
|
//#endregion
|
|
335
|
-
exports.default =
|
|
491
|
+
exports.default = WorkerDataAdapter;
|