@signaldb/core 2.0.0-beta.2 → 2.0.0-beta.21
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 +183 -80
- package/dist/AsyncDataAdapter.d.ts +53 -7
- package/dist/AutoFetchDataAdapter.d.ts +4 -6
- package/dist/Collection/Cursor.d.ts +56 -4
- package/dist/Collection/Observer.d.ts +33 -0
- package/dist/Collection/index.d.ts +66 -19
- package/dist/Collection/types.d.ts +9 -2
- package/dist/DataAdapter.d.ts +24 -6
- package/dist/DefaultDataAdapter.d.ts +5 -5
- package/dist/WorkerDataAdapter.d.ts +57 -5
- package/dist/WorkerDataAdapterHost.d.ts +9 -7
- package/dist/createIndex.d.ts +2 -2
- package/dist/createIndexProvider.d.ts +2 -2
- package/dist/createReactivityAdapter.d.ts +2 -2
- package/dist/createStorageAdapter.d.ts +1 -1
- package/dist/getIndexInfo.d.ts +3 -4
- package/dist/index.cjs.js +33 -31
- package/dist/index.d.ts +6 -1
- package/dist/index.mjs +17 -32
- package/dist/index10.cjs.js +11 -0
- package/dist/index10.mjs +10 -18
- package/dist/index11.cjs.js +23 -0
- package/dist/index11.mjs +22 -15
- package/dist/index12.cjs.js +45 -0
- package/dist/index12.mjs +44 -353
- package/dist/index13.cjs.js +22 -0
- package/dist/index13.mjs +21 -419
- package/dist/index14.cjs.js +45 -0
- package/dist/index14.mjs +44 -206
- package/dist/index15.cjs.js +86 -0
- package/dist/index15.mjs +85 -388
- package/dist/index16.cjs.js +15 -0
- package/dist/index16.mjs +14 -579
- package/dist/index17.cjs.js +141 -0
- package/dist/index17.mjs +140 -152
- package/dist/index18.cjs.js +47 -0
- package/dist/index18.mjs +46 -26
- package/dist/index19.cjs.js +15 -0
- package/dist/index19.mjs +14 -41
- package/dist/index2.cjs.js +33 -0
- package/dist/index2.mjs +32 -258
- package/dist/index20.cjs.js +23 -0
- package/dist/index20.mjs +22 -16
- package/dist/index21.cjs.js +37 -0
- package/dist/index21.mjs +36 -104
- package/dist/index22.cjs.js +35 -0
- package/dist/index22.mjs +34 -128
- package/dist/index23.cjs.js +21 -0
- package/dist/index23.mjs +20 -7
- package/dist/index24.cjs.js +19 -0
- package/dist/index24.mjs +18 -27
- package/dist/index25.cjs.js +157 -0
- package/dist/index25.mjs +156 -11
- package/dist/index26.cjs.js +38 -0
- package/dist/index26.mjs +37 -29
- package/dist/index27.cjs.js +42 -0
- package/dist/index27.mjs +41 -46
- package/dist/index28.cjs.js +347 -0
- package/dist/index28.mjs +345 -7
- package/dist/index29.cjs.js +602 -0
- package/dist/index29.mjs +601 -7
- package/dist/index3.cjs.js +19 -0
- package/dist/index3.mjs +18 -556
- package/dist/index30.cjs.js +11 -0
- package/dist/index30.mjs +10 -42
- package/dist/index31.cjs.js +11 -0
- package/dist/index31.mjs +10 -29
- package/dist/index32.cjs.js +90 -0
- package/dist/index32.mjs +89 -10
- package/dist/index33.cjs.js +53 -0
- package/dist/index33.mjs +52 -5
- package/dist/index34.cjs.js +453 -0
- package/dist/index34.mjs +453 -0
- package/dist/index35.cjs.js +72 -0
- package/dist/index35.mjs +72 -0
- package/dist/index36.cjs.js +580 -0
- package/dist/index36.mjs +580 -0
- package/dist/index37.cjs.js +21 -0
- package/dist/index37.mjs +21 -0
- package/dist/index38.cjs.js +363 -0
- package/dist/index38.mjs +363 -0
- package/dist/index39.cjs.js +513 -0
- package/dist/index39.mjs +513 -0
- package/dist/index4.cjs.js +209 -0
- package/dist/index4.mjs +204 -5
- package/dist/index5.cjs.js +187 -0
- package/dist/index5.mjs +186 -5
- package/dist/index6.cjs.js +303 -0
- package/dist/index6.mjs +301 -28
- package/dist/index7.cjs.js +125 -0
- package/dist/index7.mjs +124 -12
- package/dist/index8.cjs.js +33 -0
- package/dist/index8.mjs +32 -5
- package/dist/index9.cjs.js +12 -0
- package/dist/index9.mjs +11 -133
- package/dist/types/IndexProvider.d.ts +2 -2
- package/dist/types/Modifier.d.ts +1 -1
- package/dist/types/ReactivityAdapter.d.ts +1 -1
- package/dist/types/StorageAdapter.d.ts +9 -1
- package/dist/utils/applyQueryOptions.d.ts +15 -0
- package/dist/utils/createSignal.d.ts +2 -2
- package/dist/utils/getMatchingKeys.d.ts +2 -2
- package/dist/utils/idIndexQuery.d.ts +20 -0
- package/dist/utils/incrementalQueryUpdate.d.ts +62 -0
- package/dist/utils/isFieldExpression.d.ts +1 -1
- package/dist/utils/match.d.ts +1 -1
- package/dist/utils/modify.d.ts +1 -1
- package/dist/utils/projectItems.d.ts +12 -0
- package/dist/utils/queryDelta.d.ts +83 -0
- package/dist/utils/queryId.d.ts +2 -2
- package/dist/utils/reactiveOrAsync.d.ts +59 -0
- package/dist/utils/storageIndexQuery.d.ts +20 -0
- package/package.json +1 -1
- package/dist/index.cjs10.js +0 -18
- package/dist/index.cjs11.js +0 -15
- package/dist/index.cjs12.js +0 -353
- package/dist/index.cjs13.js +0 -419
- package/dist/index.cjs14.js +0 -206
- package/dist/index.cjs15.js +0 -388
- package/dist/index.cjs16.js +0 -579
- package/dist/index.cjs17.js +0 -152
- package/dist/index.cjs18.js +0 -26
- package/dist/index.cjs19.js +0 -42
- package/dist/index.cjs2.js +0 -259
- package/dist/index.cjs20.js +0 -16
- package/dist/index.cjs21.js +0 -104
- package/dist/index.cjs22.js +0 -129
- package/dist/index.cjs23.js +0 -7
- package/dist/index.cjs24.js +0 -27
- package/dist/index.cjs25.js +0 -10
- package/dist/index.cjs26.js +0 -29
- package/dist/index.cjs27.js +0 -46
- package/dist/index.cjs28.js +0 -8
- package/dist/index.cjs29.js +0 -7
- package/dist/index.cjs3.js +0 -557
- package/dist/index.cjs30.js +0 -42
- package/dist/index.cjs31.js +0 -29
- package/dist/index.cjs32.js +0 -9
- package/dist/index.cjs33.js +0 -5
- package/dist/index.cjs4.js +0 -5
- package/dist/index.cjs5.js +0 -5
- package/dist/index.cjs6.js +0 -29
- package/dist/index.cjs7.js +0 -12
- package/dist/index.cjs8.js +0 -5
- package/dist/index.cjs9.js +0 -133
|
@@ -0,0 +1,580 @@
|
|
|
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("./index35.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;
|
|
29
|
+
options;
|
|
30
|
+
id;
|
|
31
|
+
isDisposed = false;
|
|
32
|
+
workerReady;
|
|
33
|
+
log = () => {};
|
|
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;
|
|
47
|
+
this.options = options;
|
|
48
|
+
this.id = this.options.id || "default-worker-data-adapter";
|
|
49
|
+
if (this.options.log) this.log = this.options.log;
|
|
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
|
+
};
|
|
58
|
+
});
|
|
59
|
+
this.worker.addEventListener("message", this.handleWorkerMessage);
|
|
60
|
+
}
|
|
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();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
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;
|
|
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
|
+
const canApply = require_queryDelta.canApplyQueryDelta(query.items, delta);
|
|
97
|
+
if (!canApply || require_queryDelta.isEmptyQueryDelta(delta)) {
|
|
98
|
+
if (state === query.state) return;
|
|
99
|
+
this.updateQuery(collectionName, {
|
|
100
|
+
selector: query.selector,
|
|
101
|
+
options: query.options
|
|
102
|
+
}, {
|
|
103
|
+
state,
|
|
104
|
+
error
|
|
105
|
+
});
|
|
106
|
+
const settled = collectionQueries.get(id);
|
|
107
|
+
if (!settled) return;
|
|
108
|
+
settled.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, state, canApply ? delta : void 0));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const servedBefore = this.flattenPendingWrites(collectionName) == null ? null : this.servedResult(collectionName, query);
|
|
112
|
+
nextItems = require_queryDelta.applyQueryDelta(query.items, delta);
|
|
113
|
+
if (servedBefore == null) deltaToPublish = delta;
|
|
114
|
+
else {
|
|
115
|
+
this.updateQuery(collectionName, {
|
|
116
|
+
selector: query.selector,
|
|
117
|
+
options: query.options
|
|
118
|
+
}, {
|
|
119
|
+
state,
|
|
120
|
+
error,
|
|
121
|
+
items: nextItems
|
|
122
|
+
});
|
|
123
|
+
const stored = collectionQueries.get(id);
|
|
124
|
+
if (!stored) return;
|
|
125
|
+
const servedDelta = require_queryDelta.diffQueryResults(servedBefore, this.servedResult(collectionName, stored));
|
|
126
|
+
if (require_queryDelta.isEmptyQueryDelta(servedDelta) && state === query.state) return;
|
|
127
|
+
stored.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, state, servedDelta));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
this.updateQuery(collectionName, {
|
|
132
|
+
selector: query.selector,
|
|
133
|
+
options: query.options
|
|
134
|
+
}, {
|
|
135
|
+
state,
|
|
136
|
+
error,
|
|
137
|
+
items: nextItems
|
|
138
|
+
});
|
|
139
|
+
const updated = collectionQueries.get(id);
|
|
140
|
+
if (!updated) return;
|
|
141
|
+
updated.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, state, deltaToPublish));
|
|
142
|
+
}
|
|
143
|
+
async exec(method, collectionName, ...args) {
|
|
144
|
+
await this.workerReady;
|
|
145
|
+
if (method !== "isReady") {
|
|
146
|
+
const collectionReady = this.collectionReady.get(collectionName);
|
|
147
|
+
if (!collectionReady) throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
|
|
148
|
+
await collectionReady;
|
|
149
|
+
}
|
|
150
|
+
if (this.isDisposed) throw new Error("WorkerDataAdapter is disposed");
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const messageId = require_randomId.default();
|
|
153
|
+
this.pendingRequests.set(messageId, {
|
|
154
|
+
resolve,
|
|
155
|
+
reject
|
|
156
|
+
});
|
|
157
|
+
this.worker.postMessage({
|
|
158
|
+
id: messageId,
|
|
159
|
+
workerId: this.id,
|
|
160
|
+
method,
|
|
161
|
+
args: [collectionName, ...args]
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Issues a call whose result nobody is waiting for, and makes sure a failure has somewhere to
|
|
167
|
+
* go. A bare rejection here would surface as an uncaught error — which is what a disposed
|
|
168
|
+
* collection produced every time a cursor was cleaned up after it.
|
|
169
|
+
* @param method - The method to call on the worker.
|
|
170
|
+
* @param collectionName - The collection it applies to.
|
|
171
|
+
* @param args - The remaining arguments.
|
|
172
|
+
* @param onError - Called when the call fails, in place of merely logging it.
|
|
173
|
+
*/
|
|
174
|
+
execInBackground(method, collectionName, args = [], onError) {
|
|
175
|
+
this.exec(method, collectionName, ...args).catch((error) => {
|
|
176
|
+
if (onError) {
|
|
177
|
+
onError(error);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
this.log(method, "failed", error);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
queryItemsById(query) {
|
|
184
|
+
if (!query.itemsById) query.itemsById = new Map(query.items.map((item) => [item.id, item]));
|
|
185
|
+
return query.itemsById;
|
|
186
|
+
}
|
|
187
|
+
flattenPendingWrites(collectionName) {
|
|
188
|
+
const state = this.pendingWrites.get(collectionName);
|
|
189
|
+
if (!state || state.writes.size === 0) return null;
|
|
190
|
+
return state.flat;
|
|
191
|
+
}
|
|
192
|
+
static pushPendingEntry(state, id, item, seq) {
|
|
193
|
+
const stack = state.byId.get(id);
|
|
194
|
+
if (stack) stack.push({
|
|
195
|
+
seq,
|
|
196
|
+
item
|
|
197
|
+
});
|
|
198
|
+
else state.byId.set(id, [{
|
|
199
|
+
seq,
|
|
200
|
+
item
|
|
201
|
+
}]);
|
|
202
|
+
WorkerDataAdapter.writeFlatEntry(state, id, item);
|
|
203
|
+
}
|
|
204
|
+
static dropPendingEntry(state, id, seq) {
|
|
205
|
+
const stack = state.byId.get(id);
|
|
206
|
+
if (!stack) return;
|
|
207
|
+
const index = stack.findIndex((entry) => entry.seq === seq);
|
|
208
|
+
if (index !== -1) stack.splice(index, 1);
|
|
209
|
+
const top = stack.at(-1);
|
|
210
|
+
if (!top) {
|
|
211
|
+
state.byId.delete(id);
|
|
212
|
+
state.flat.upserts.delete(id);
|
|
213
|
+
state.flat.deletes.delete(id);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
WorkerDataAdapter.writeFlatEntry(state, id, top.item);
|
|
217
|
+
}
|
|
218
|
+
static writeFlatEntry(state, id, item) {
|
|
219
|
+
if (item === null) {
|
|
220
|
+
state.flat.deletes.add(id);
|
|
221
|
+
state.flat.upserts.delete(id);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
state.flat.upserts.set(id, item);
|
|
225
|
+
state.flat.deletes.delete(id);
|
|
226
|
+
}
|
|
227
|
+
static providesFullItems(query) {
|
|
228
|
+
return query.options?.fields == null;
|
|
229
|
+
}
|
|
230
|
+
observableItems(collectionName) {
|
|
231
|
+
const byId = /* @__PURE__ */ new Map();
|
|
232
|
+
this.queries[collectionName]?.forEach((query) => {
|
|
233
|
+
if (!WorkerDataAdapter.providesFullItems(query)) return;
|
|
234
|
+
query.items.forEach((item) => byId.set(item.id, item));
|
|
235
|
+
});
|
|
236
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
237
|
+
if (pending) {
|
|
238
|
+
pending.upserts.forEach((item, id) => byId.set(id, item));
|
|
239
|
+
pending.deletes.forEach((id) => byId.delete(id));
|
|
240
|
+
}
|
|
241
|
+
return [...byId.values()];
|
|
242
|
+
}
|
|
243
|
+
observableItemsByIds(collectionName, ids) {
|
|
244
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
245
|
+
const found = /* @__PURE__ */ new Map();
|
|
246
|
+
ids.forEach((id) => {
|
|
247
|
+
if (pending?.deletes.has(id)) return;
|
|
248
|
+
const pendingItem = pending?.upserts.get(id);
|
|
249
|
+
if (pendingItem) {
|
|
250
|
+
found.set(id, pendingItem);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const queries = this.queries[collectionName];
|
|
254
|
+
if (!queries) return;
|
|
255
|
+
for (const query of queries.values()) {
|
|
256
|
+
if (!WorkerDataAdapter.providesFullItems(query)) continue;
|
|
257
|
+
const item = this.queryItemsById(query).get(id);
|
|
258
|
+
if (item) {
|
|
259
|
+
found.set(id, item);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
return [...found.values()];
|
|
265
|
+
}
|
|
266
|
+
servedResult(collectionName, query) {
|
|
267
|
+
const pendingVersion = this.pendingWriteVersions.get(collectionName) ?? 0;
|
|
268
|
+
if (query.served && query.served.fromItems === query.items && query.served.pendingVersion === pendingVersion) return query.served.items;
|
|
269
|
+
const items = this.advanceServedResult(collectionName, query, pendingVersion) ?? this.computeServedResult(collectionName, query);
|
|
270
|
+
query.served = {
|
|
271
|
+
items,
|
|
272
|
+
fromItems: query.items,
|
|
273
|
+
pendingVersion
|
|
274
|
+
};
|
|
275
|
+
return items;
|
|
276
|
+
}
|
|
277
|
+
advanceServedResult(collectionName, query, pendingVersion) {
|
|
278
|
+
const served = query.served;
|
|
279
|
+
if (!served || served.fromItems !== query.items) return null;
|
|
280
|
+
if (served.pendingVersion !== pendingVersion - 1) return null;
|
|
281
|
+
if (!WorkerDataAdapter.providesFullItems(query) || query.options?.limit != null) return null;
|
|
282
|
+
const state = this.pendingWrites.get(collectionName);
|
|
283
|
+
if (state?.lastChange == null || state.lastChange.version !== pendingVersion) return null;
|
|
284
|
+
return require_incrementalQueryUpdate.mergeChangesetIntoResult(served.items, query.selector, query.options, this.changesetForIds(collectionName, query, state.lastChange.ids));
|
|
285
|
+
}
|
|
286
|
+
changesetForIds(collectionName, query, ids) {
|
|
287
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
288
|
+
const stored = this.queryItemsById(query);
|
|
289
|
+
const upserts = [];
|
|
290
|
+
const deletes = [];
|
|
291
|
+
ids.forEach((id) => {
|
|
292
|
+
const pendingItem = pending?.upserts.get(id);
|
|
293
|
+
if (pendingItem) {
|
|
294
|
+
upserts.push(pendingItem);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (pending?.deletes.has(id)) {
|
|
298
|
+
deletes.push(id);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const storedItem = stored.get(id);
|
|
302
|
+
if (storedItem) upserts.push(storedItem);
|
|
303
|
+
else deletes.push(id);
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
upserts,
|
|
307
|
+
deletes
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
computeServedResult(collectionName, query) {
|
|
311
|
+
const pending = this.flattenPendingWrites(collectionName);
|
|
312
|
+
if (!pending) return query.items;
|
|
313
|
+
const byId = this.queryItemsById(query);
|
|
314
|
+
let affected = false;
|
|
315
|
+
pending.deletes.forEach((id) => {
|
|
316
|
+
if (byId.has(id)) affected = true;
|
|
317
|
+
});
|
|
318
|
+
if (!affected) pending.upserts.forEach((item, id) => {
|
|
319
|
+
if (affected) return;
|
|
320
|
+
if (byId.has(id)) affected = true;
|
|
321
|
+
else if (query.selector != null && require_match.default(item, query.selector)) affected = true;
|
|
322
|
+
});
|
|
323
|
+
if (!affected) return query.items;
|
|
324
|
+
return require_incrementalQueryUpdate.mergeChangesetIntoResult(query.items, query.selector, query.options, {
|
|
325
|
+
upserts: [...pending.upserts.values()],
|
|
326
|
+
deletes: [...pending.deletes]
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Registers a write's effect locally and notifies every active query it
|
|
331
|
+
* touches, then returns a function that drops it again once the write
|
|
332
|
+
* settles.
|
|
333
|
+
* @param collectionName - The collection the write targets.
|
|
334
|
+
* @param upserts - Items inserted or updated by the write.
|
|
335
|
+
* @param deletes - Ids removed by the write.
|
|
336
|
+
* @returns A function that drops the pending write and re-notifies.
|
|
337
|
+
*/
|
|
338
|
+
applyPendingWrite(collectionName, upserts, deletes) {
|
|
339
|
+
if (upserts.length === 0 && deletes.length === 0) return () => {};
|
|
340
|
+
const affectedIds = new Set([...upserts.map((item) => item.id), ...deletes]);
|
|
341
|
+
const affected = this.affectedQueries(collectionName, upserts, affectedIds);
|
|
342
|
+
const servedBefore = this.servedResults(collectionName, affected);
|
|
343
|
+
const seq = this.pendingWriteSeq += 1;
|
|
344
|
+
const state = this.pendingWrites.get(collectionName) ?? {
|
|
345
|
+
writes: /* @__PURE__ */ new Map(),
|
|
346
|
+
byId: /* @__PURE__ */ new Map(),
|
|
347
|
+
flat: {
|
|
348
|
+
upserts: /* @__PURE__ */ new Map(),
|
|
349
|
+
deletes: /* @__PURE__ */ new Set()
|
|
350
|
+
},
|
|
351
|
+
lastChange: null
|
|
352
|
+
};
|
|
353
|
+
const write = {
|
|
354
|
+
upserts: new Map(upserts.map((item) => [item.id, item])),
|
|
355
|
+
deletes: new Set(deletes)
|
|
356
|
+
};
|
|
357
|
+
state.writes.set(seq, write);
|
|
358
|
+
write.upserts.forEach((item, id) => WorkerDataAdapter.pushPendingEntry(state, id, item, seq));
|
|
359
|
+
write.deletes.forEach((id) => WorkerDataAdapter.pushPendingEntry(state, id, null, seq));
|
|
360
|
+
this.pendingWrites.set(collectionName, state);
|
|
361
|
+
this.bumpPendingWriteVersion(collectionName);
|
|
362
|
+
state.lastChange = {
|
|
363
|
+
version: this.pendingWriteVersions.get(collectionName) ?? 0,
|
|
364
|
+
ids: [...affectedIds]
|
|
365
|
+
};
|
|
366
|
+
this.notifyWithDeltas(collectionName, affected, servedBefore);
|
|
367
|
+
return () => {
|
|
368
|
+
const current = this.pendingWrites.get(collectionName);
|
|
369
|
+
if (!current) return;
|
|
370
|
+
const settled = current.writes.get(seq);
|
|
371
|
+
if (!settled) return;
|
|
372
|
+
const affectedOnDrop = this.affectedQueries(collectionName, upserts, affectedIds);
|
|
373
|
+
const beforeDrop = this.servedResults(collectionName, affectedOnDrop);
|
|
374
|
+
current.writes.delete(seq);
|
|
375
|
+
settled.upserts.forEach((item, id) => WorkerDataAdapter.dropPendingEntry(current, id, seq));
|
|
376
|
+
settled.deletes.forEach((id) => WorkerDataAdapter.dropPendingEntry(current, id, seq));
|
|
377
|
+
if (current.writes.size === 0) this.pendingWrites.delete(collectionName);
|
|
378
|
+
this.bumpPendingWriteVersion(collectionName);
|
|
379
|
+
if (current.writes.size > 0) current.lastChange = {
|
|
380
|
+
version: this.pendingWriteVersions.get(collectionName) ?? 0,
|
|
381
|
+
ids: [...affectedIds]
|
|
382
|
+
};
|
|
383
|
+
this.notifyWithDeltas(collectionName, affectedOnDrop, beforeDrop);
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
affectedQueries(collectionName, upserts, affectedIds) {
|
|
387
|
+
const affected = [];
|
|
388
|
+
this.queries[collectionName]?.forEach((query) => {
|
|
389
|
+
const byId = this.queryItemsById(query);
|
|
390
|
+
let wasHolding = false;
|
|
391
|
+
affectedIds.forEach((id) => {
|
|
392
|
+
if (byId.has(id)) wasHolding = true;
|
|
393
|
+
});
|
|
394
|
+
const nowMatches = upserts.some((item) => query.selector != null && require_match.default(item, query.selector));
|
|
395
|
+
if (!wasHolding && !nowMatches) return;
|
|
396
|
+
affected.push(query);
|
|
397
|
+
});
|
|
398
|
+
return affected;
|
|
399
|
+
}
|
|
400
|
+
servedResults(collectionName, queries) {
|
|
401
|
+
return new Map(queries.map((query) => [query, this.servedResult(collectionName, query)]));
|
|
402
|
+
}
|
|
403
|
+
notifyWithDeltas(collectionName, queries, servedBefore) {
|
|
404
|
+
queries.forEach((query) => {
|
|
405
|
+
const before = servedBefore.get(query);
|
|
406
|
+
if (before == null) return;
|
|
407
|
+
const delta = require_queryDelta.diffQueryResults(before, this.servedResult(collectionName, query));
|
|
408
|
+
if (require_queryDelta.isEmptyQueryDelta(delta)) return;
|
|
409
|
+
query.stateChangeCallbacks.forEach((callback) => require_queryDelta.callWithDelta(callback, query.state, delta));
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
matchObservableItems(collectionName, selector, onlyFirst) {
|
|
413
|
+
if (selector == null) return [];
|
|
414
|
+
const ids = selectorIds(selector);
|
|
415
|
+
const matches = ids == null ? this.observableItems(collectionName).filter((item) => require_match.default(item, selector)) : this.observableItemsByIds(collectionName, ids);
|
|
416
|
+
return onlyFirst ? matches.slice(0, 1) : matches;
|
|
417
|
+
}
|
|
418
|
+
resolveUpdate(collectionName, selector, modifier, onlyFirst) {
|
|
419
|
+
const { $setOnInsert, ...restModifier } = modifier;
|
|
420
|
+
const upserts = [];
|
|
421
|
+
const deletes = [];
|
|
422
|
+
this.matchObservableItems(collectionName, selector, onlyFirst).forEach((item) => {
|
|
423
|
+
const modifiedItem = require_modify.default(require_deepClone.default(item), restModifier);
|
|
424
|
+
upserts.push(modifiedItem);
|
|
425
|
+
if (modifiedItem.id !== item.id) deletes.push(item.id);
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
upserts,
|
|
429
|
+
deletes
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
resolveRemoval(collectionName, selector, onlyFirst) {
|
|
433
|
+
return this.matchObservableItems(collectionName, selector, onlyFirst).map((item) => item.id);
|
|
434
|
+
}
|
|
435
|
+
resolveReplacement(item, replacement) {
|
|
436
|
+
const modifiedItem = {
|
|
437
|
+
...replacement,
|
|
438
|
+
id: replacement.id ?? item.id
|
|
439
|
+
};
|
|
440
|
+
return {
|
|
441
|
+
upserts: [modifiedItem],
|
|
442
|
+
deletes: modifiedItem.id === item.id ? [] : [item.id]
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
async withPendingWrite(collectionName, delta, run) {
|
|
446
|
+
const dropPendingWrite = this.applyPendingWrite(collectionName, delta.upserts, delta.deletes);
|
|
447
|
+
try {
|
|
448
|
+
return await run();
|
|
449
|
+
} finally {
|
|
450
|
+
dropPendingWrite();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
enqueueBatched(collectionName, method, args) {
|
|
454
|
+
const helper = this.batchExecutionHelpers.get(collectionName);
|
|
455
|
+
if (!helper) throw new Error(`Collection "${collectionName}" is not registered in WorkerDataAdapter`);
|
|
456
|
+
return helper.enqueue(method, args);
|
|
457
|
+
}
|
|
458
|
+
updateQuery(collectionName, query, update) {
|
|
459
|
+
const id = require_queryId.default(query.selector, query.options);
|
|
460
|
+
const collectionQueries = this.queries[collectionName];
|
|
461
|
+
if (!collectionQueries) return;
|
|
462
|
+
const existing = collectionQueries.get(id);
|
|
463
|
+
const newState = {
|
|
464
|
+
selector: query.selector,
|
|
465
|
+
options: query.options,
|
|
466
|
+
state: "active",
|
|
467
|
+
error: null,
|
|
468
|
+
stateChangeCallbacks: [],
|
|
469
|
+
...existing,
|
|
470
|
+
...update,
|
|
471
|
+
...update.items ? {
|
|
472
|
+
items: update.items,
|
|
473
|
+
itemsById: void 0
|
|
474
|
+
} : { items: existing?.items ?? [] }
|
|
475
|
+
};
|
|
476
|
+
collectionQueries.set(id, newState);
|
|
477
|
+
this.queries[collectionName] = collectionQueries;
|
|
478
|
+
}
|
|
479
|
+
createCollectionBackend(collection, indices) {
|
|
480
|
+
this.queries[collection.name] = /* @__PURE__ */ new Map();
|
|
481
|
+
this.execInBackground("registerCollection", collection.name, [indices]);
|
|
482
|
+
this.collectionReady.set(collection.name, this.exec("isReady", collection.name));
|
|
483
|
+
this.batchExecutionHelpers.set(collection.name, require_batchOnNextTick.default(async (method, args) => this.exec(method, collection.name, args)));
|
|
484
|
+
return {
|
|
485
|
+
insert: async (item) => {
|
|
486
|
+
return this.withPendingWrite(collection.name, {
|
|
487
|
+
upserts: [item],
|
|
488
|
+
deletes: []
|
|
489
|
+
}, () => this.enqueueBatched(collection.name, "insert", [item]));
|
|
490
|
+
},
|
|
491
|
+
updateOne: async (selector, modifier) => {
|
|
492
|
+
return this.withPendingWrite(collection.name, this.resolveUpdate(collection.name, selector, modifier, true), () => this.enqueueBatched(collection.name, "updateOne", [selector, modifier]));
|
|
493
|
+
},
|
|
494
|
+
updateMany: async (selector, modifier) => {
|
|
495
|
+
return this.withPendingWrite(collection.name, this.resolveUpdate(collection.name, selector, modifier, false), () => this.enqueueBatched(collection.name, "updateMany", [selector, modifier]));
|
|
496
|
+
},
|
|
497
|
+
replaceOne: async (selector, replacement) => {
|
|
498
|
+
const [item] = this.matchObservableItems(collection.name, selector, true);
|
|
499
|
+
return this.withPendingWrite(collection.name, item == null ? {
|
|
500
|
+
upserts: [],
|
|
501
|
+
deletes: []
|
|
502
|
+
} : this.resolveReplacement(item, replacement), () => this.enqueueBatched(collection.name, "replaceOne", [selector, replacement]));
|
|
503
|
+
},
|
|
504
|
+
removeOne: async (selector) => {
|
|
505
|
+
return this.withPendingWrite(collection.name, {
|
|
506
|
+
upserts: [],
|
|
507
|
+
deletes: this.resolveRemoval(collection.name, selector, true)
|
|
508
|
+
}, () => this.enqueueBatched(collection.name, "removeOne", [selector]));
|
|
509
|
+
},
|
|
510
|
+
removeMany: async (selector) => {
|
|
511
|
+
return this.withPendingWrite(collection.name, {
|
|
512
|
+
upserts: [],
|
|
513
|
+
deletes: this.resolveRemoval(collection.name, selector, false)
|
|
514
|
+
}, () => this.enqueueBatched(collection.name, "removeMany", [selector]));
|
|
515
|
+
},
|
|
516
|
+
registerQuery: (selector, options) => {
|
|
517
|
+
this.updateQuery(collection.name, {
|
|
518
|
+
selector,
|
|
519
|
+
options
|
|
520
|
+
}, {
|
|
521
|
+
state: "active",
|
|
522
|
+
error: null,
|
|
523
|
+
items: []
|
|
524
|
+
});
|
|
525
|
+
this.execInBackground("registerQuery", collection.name, [selector, options], (error) => {
|
|
526
|
+
const query = this.queries[collection.name]?.get(require_queryId.default(selector, options));
|
|
527
|
+
if (!query) return;
|
|
528
|
+
this.updateQuery(collection.name, {
|
|
529
|
+
selector,
|
|
530
|
+
options
|
|
531
|
+
}, {
|
|
532
|
+
state: "error",
|
|
533
|
+
error
|
|
534
|
+
});
|
|
535
|
+
query.stateChangeCallbacks.forEach((callback) => callback("error"));
|
|
536
|
+
});
|
|
537
|
+
},
|
|
538
|
+
unregisterQuery: (selector, options) => {
|
|
539
|
+
this.queries[collection.name]?.delete(require_queryId.default(selector, options));
|
|
540
|
+
this.execInBackground("unregisterQuery", collection.name, [selector, options]);
|
|
541
|
+
},
|
|
542
|
+
getQueryState: (selector, options) => {
|
|
543
|
+
return (this.queries[collection.name]?.get(require_queryId.default(selector, options)))?.state || "active";
|
|
544
|
+
},
|
|
545
|
+
getQueryError: (selector, options) => {
|
|
546
|
+
return (this.queries[collection.name]?.get(require_queryId.default(selector, options)))?.error || null;
|
|
547
|
+
},
|
|
548
|
+
getQueryResult: (selector, options) => {
|
|
549
|
+
const query = this.queries[collection.name]?.get(require_queryId.default(selector, options));
|
|
550
|
+
if (!query) return [];
|
|
551
|
+
return this.servedResult(collection.name, query);
|
|
552
|
+
},
|
|
553
|
+
onQueryStateChange: (selector, options, callback) => {
|
|
554
|
+
this.updateQuery(collection.name, {
|
|
555
|
+
selector,
|
|
556
|
+
options
|
|
557
|
+
}, { stateChangeCallbacks: [...this.queries[collection.name]?.get(require_queryId.default(selector, options))?.stateChangeCallbacks || [], callback] });
|
|
558
|
+
return () => {
|
|
559
|
+
const currentCallbacks = this.queries[collection.name]?.get(require_queryId.default(selector, options))?.stateChangeCallbacks;
|
|
560
|
+
if (!currentCallbacks) return;
|
|
561
|
+
this.updateQuery(collection.name, {
|
|
562
|
+
selector,
|
|
563
|
+
options
|
|
564
|
+
}, { stateChangeCallbacks: currentCallbacks.filter((existingCallback) => existingCallback !== callback) });
|
|
565
|
+
};
|
|
566
|
+
},
|
|
567
|
+
executeQuery: (selector, options) => this.exec("executeQuery", collection.name, selector, options),
|
|
568
|
+
dispose: async () => {
|
|
569
|
+
await this.exec("unregisterCollection", collection.name);
|
|
570
|
+
this.isDisposed = true;
|
|
571
|
+
this.worker.terminate?.();
|
|
572
|
+
},
|
|
573
|
+
isReady: async () => {
|
|
574
|
+
await this.collectionReady.get(collection.name);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
//#endregion
|
|
580
|
+
exports.default = WorkerDataAdapter;
|