@arcote.tech/arc-adapter-db-sqlite 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4782 -0
- package/package.json +4 -1
- package/src/bun-sqlite.ts +0 -82
- package/src/index.ts +0 -3
- package/src/sqlite-adapter.ts +0 -812
- package/tsconfig.json +0 -11
package/dist/index.js
ADDED
|
@@ -0,0 +1,4782 @@
|
|
|
1
|
+
// src/bun-sqlite.ts
|
|
2
|
+
import { Database } from "bun:sqlite";
|
|
3
|
+
|
|
4
|
+
// ../../../../node_modules/mutative/dist/mutative.esm.mjs
|
|
5
|
+
var Operation = {
|
|
6
|
+
Remove: "remove",
|
|
7
|
+
Replace: "replace",
|
|
8
|
+
Add: "add"
|
|
9
|
+
};
|
|
10
|
+
var PROXY_DRAFT = Symbol.for("__MUTATIVE_PROXY_DRAFT__");
|
|
11
|
+
var RAW_RETURN_SYMBOL = Symbol("__MUTATIVE_RAW_RETURN_SYMBOL__");
|
|
12
|
+
var iteratorSymbol = Symbol.iterator;
|
|
13
|
+
var dataTypes = {
|
|
14
|
+
mutable: "mutable",
|
|
15
|
+
immutable: "immutable"
|
|
16
|
+
};
|
|
17
|
+
var internal = {};
|
|
18
|
+
function has(target, key) {
|
|
19
|
+
return target instanceof Map ? target.has(key) : Object.prototype.hasOwnProperty.call(target, key);
|
|
20
|
+
}
|
|
21
|
+
function getDescriptor(target, key) {
|
|
22
|
+
if (key in target) {
|
|
23
|
+
let prototype = Reflect.getPrototypeOf(target);
|
|
24
|
+
while (prototype) {
|
|
25
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(prototype, key);
|
|
26
|
+
if (descriptor)
|
|
27
|
+
return descriptor;
|
|
28
|
+
prototype = Reflect.getPrototypeOf(prototype);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
function isBaseSetInstance(obj) {
|
|
34
|
+
return Object.getPrototypeOf(obj) === Set.prototype;
|
|
35
|
+
}
|
|
36
|
+
function isBaseMapInstance(obj) {
|
|
37
|
+
return Object.getPrototypeOf(obj) === Map.prototype;
|
|
38
|
+
}
|
|
39
|
+
function latest(proxyDraft) {
|
|
40
|
+
var _a;
|
|
41
|
+
return (_a = proxyDraft.copy) !== null && _a !== undefined ? _a : proxyDraft.original;
|
|
42
|
+
}
|
|
43
|
+
function isDraft(target) {
|
|
44
|
+
return !!getProxyDraft(target);
|
|
45
|
+
}
|
|
46
|
+
function getProxyDraft(value) {
|
|
47
|
+
if (typeof value !== "object")
|
|
48
|
+
return null;
|
|
49
|
+
return value === null || value === undefined ? undefined : value[PROXY_DRAFT];
|
|
50
|
+
}
|
|
51
|
+
function getValue(value) {
|
|
52
|
+
var _a;
|
|
53
|
+
const proxyDraft = getProxyDraft(value);
|
|
54
|
+
return proxyDraft ? (_a = proxyDraft.copy) !== null && _a !== undefined ? _a : proxyDraft.original : value;
|
|
55
|
+
}
|
|
56
|
+
function isDraftable(value, options) {
|
|
57
|
+
if (!value || typeof value !== "object")
|
|
58
|
+
return false;
|
|
59
|
+
let markResult;
|
|
60
|
+
return Object.getPrototypeOf(value) === Object.prototype || Array.isArray(value) || value instanceof Map || value instanceof Set || !!(options === null || options === undefined ? undefined : options.mark) && ((markResult = options.mark(value, dataTypes)) === dataTypes.immutable || typeof markResult === "function");
|
|
61
|
+
}
|
|
62
|
+
function getPath(target, path = []) {
|
|
63
|
+
if (Object.hasOwnProperty.call(target, "key")) {
|
|
64
|
+
const parentCopy = target.parent.copy;
|
|
65
|
+
const proxyDraft = getProxyDraft(get(parentCopy, target.key));
|
|
66
|
+
if (proxyDraft !== null && (proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.original) !== target.original) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const isSet = target.parent.type === 3;
|
|
70
|
+
const key = isSet ? Array.from(target.parent.setMap.keys()).indexOf(target.key) : target.key;
|
|
71
|
+
if (!(isSet && parentCopy.size > key || has(parentCopy, key)))
|
|
72
|
+
return null;
|
|
73
|
+
path.push(key);
|
|
74
|
+
}
|
|
75
|
+
if (target.parent) {
|
|
76
|
+
return getPath(target.parent, path);
|
|
77
|
+
}
|
|
78
|
+
path.reverse();
|
|
79
|
+
try {
|
|
80
|
+
resolvePath(target.copy, path);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return path;
|
|
85
|
+
}
|
|
86
|
+
function getType(target) {
|
|
87
|
+
if (Array.isArray(target))
|
|
88
|
+
return 1;
|
|
89
|
+
if (target instanceof Map)
|
|
90
|
+
return 2;
|
|
91
|
+
if (target instanceof Set)
|
|
92
|
+
return 3;
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
function get(target, key) {
|
|
96
|
+
return getType(target) === 2 ? target.get(key) : target[key];
|
|
97
|
+
}
|
|
98
|
+
function set(target, key, value) {
|
|
99
|
+
const type = getType(target);
|
|
100
|
+
if (type === 2) {
|
|
101
|
+
target.set(key, value);
|
|
102
|
+
} else {
|
|
103
|
+
target[key] = value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function peek(target, key) {
|
|
107
|
+
const state = getProxyDraft(target);
|
|
108
|
+
const source = state ? latest(state) : target;
|
|
109
|
+
return source[key];
|
|
110
|
+
}
|
|
111
|
+
function isEqual(x, y) {
|
|
112
|
+
if (x === y) {
|
|
113
|
+
return x !== 0 || 1 / x === 1 / y;
|
|
114
|
+
} else {
|
|
115
|
+
return x !== x && y !== y;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function revokeProxy(proxyDraft) {
|
|
119
|
+
if (!proxyDraft)
|
|
120
|
+
return;
|
|
121
|
+
while (proxyDraft.finalities.revoke.length > 0) {
|
|
122
|
+
const revoke = proxyDraft.finalities.revoke.pop();
|
|
123
|
+
revoke();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function escapePath(path, pathAsArray) {
|
|
127
|
+
return pathAsArray ? path : [""].concat(path).map((_item) => {
|
|
128
|
+
const item = `${_item}`;
|
|
129
|
+
if (item.indexOf("/") === -1 && item.indexOf("~") === -1)
|
|
130
|
+
return item;
|
|
131
|
+
return item.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
132
|
+
}).join("/");
|
|
133
|
+
}
|
|
134
|
+
function unescapePath(path) {
|
|
135
|
+
if (Array.isArray(path))
|
|
136
|
+
return path;
|
|
137
|
+
return path.split("/").map((_item) => _item.replace(/~1/g, "/").replace(/~0/g, "~")).slice(1);
|
|
138
|
+
}
|
|
139
|
+
function resolvePath(base, path) {
|
|
140
|
+
for (let index = 0;index < path.length - 1; index += 1) {
|
|
141
|
+
const key = path[index];
|
|
142
|
+
base = get(getType(base) === 3 ? Array.from(base) : base, key);
|
|
143
|
+
if (typeof base !== "object") {
|
|
144
|
+
throw new Error(`Cannot resolve patch at '${path.join("/")}'.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return base;
|
|
148
|
+
}
|
|
149
|
+
function strictCopy(target) {
|
|
150
|
+
const copy = Object.create(Object.getPrototypeOf(target));
|
|
151
|
+
Reflect.ownKeys(target).forEach((key) => {
|
|
152
|
+
let desc = Reflect.getOwnPropertyDescriptor(target, key);
|
|
153
|
+
if (desc.enumerable && desc.configurable && desc.writable) {
|
|
154
|
+
copy[key] = target[key];
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!desc.writable) {
|
|
158
|
+
desc.writable = true;
|
|
159
|
+
desc.configurable = true;
|
|
160
|
+
}
|
|
161
|
+
if (desc.get || desc.set)
|
|
162
|
+
desc = {
|
|
163
|
+
configurable: true,
|
|
164
|
+
writable: true,
|
|
165
|
+
enumerable: desc.enumerable,
|
|
166
|
+
value: target[key]
|
|
167
|
+
};
|
|
168
|
+
Reflect.defineProperty(copy, key, desc);
|
|
169
|
+
});
|
|
170
|
+
return copy;
|
|
171
|
+
}
|
|
172
|
+
var propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
173
|
+
function shallowCopy(original, options) {
|
|
174
|
+
let markResult;
|
|
175
|
+
if (Array.isArray(original)) {
|
|
176
|
+
return Array.prototype.concat.call(original);
|
|
177
|
+
} else if (original instanceof Set) {
|
|
178
|
+
if (!isBaseSetInstance(original)) {
|
|
179
|
+
const SubClass = Object.getPrototypeOf(original).constructor;
|
|
180
|
+
return new SubClass(original.values());
|
|
181
|
+
}
|
|
182
|
+
return Set.prototype.difference ? Set.prototype.difference.call(original, new Set) : new Set(original.values());
|
|
183
|
+
} else if (original instanceof Map) {
|
|
184
|
+
if (!isBaseMapInstance(original)) {
|
|
185
|
+
const SubClass = Object.getPrototypeOf(original).constructor;
|
|
186
|
+
return new SubClass(original);
|
|
187
|
+
}
|
|
188
|
+
return new Map(original);
|
|
189
|
+
} else if ((options === null || options === undefined ? undefined : options.mark) && (markResult = options.mark(original, dataTypes), markResult !== undefined) && markResult !== dataTypes.mutable) {
|
|
190
|
+
if (markResult === dataTypes.immutable) {
|
|
191
|
+
return strictCopy(original);
|
|
192
|
+
} else if (typeof markResult === "function") {
|
|
193
|
+
if (options.enablePatches || options.enableAutoFreeze) {
|
|
194
|
+
throw new Error(`You can't use mark and patches or auto freeze together.`);
|
|
195
|
+
}
|
|
196
|
+
return markResult();
|
|
197
|
+
}
|
|
198
|
+
throw new Error(`Unsupported mark result: ${markResult}`);
|
|
199
|
+
} else if (typeof original === "object" && Object.getPrototypeOf(original) === Object.prototype) {
|
|
200
|
+
const copy = {};
|
|
201
|
+
Object.keys(original).forEach((key) => {
|
|
202
|
+
copy[key] = original[key];
|
|
203
|
+
});
|
|
204
|
+
Object.getOwnPropertySymbols(original).forEach((key) => {
|
|
205
|
+
if (propIsEnum.call(original, key)) {
|
|
206
|
+
copy[key] = original[key];
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return copy;
|
|
210
|
+
} else {
|
|
211
|
+
throw new Error(`Please check mark() to ensure that it is a stable marker draftable function.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function ensureShallowCopy(target) {
|
|
215
|
+
if (target.copy)
|
|
216
|
+
return;
|
|
217
|
+
target.copy = shallowCopy(target.original, target.options);
|
|
218
|
+
}
|
|
219
|
+
function deepClone(target) {
|
|
220
|
+
if (!isDraftable(target))
|
|
221
|
+
return getValue(target);
|
|
222
|
+
if (Array.isArray(target))
|
|
223
|
+
return target.map(deepClone);
|
|
224
|
+
if (target instanceof Map) {
|
|
225
|
+
const iterable = Array.from(target.entries()).map(([k, v]) => [
|
|
226
|
+
k,
|
|
227
|
+
deepClone(v)
|
|
228
|
+
]);
|
|
229
|
+
if (!isBaseMapInstance(target)) {
|
|
230
|
+
const SubClass = Object.getPrototypeOf(target).constructor;
|
|
231
|
+
return new SubClass(iterable);
|
|
232
|
+
}
|
|
233
|
+
return new Map(iterable);
|
|
234
|
+
}
|
|
235
|
+
if (target instanceof Set) {
|
|
236
|
+
const iterable = Array.from(target).map(deepClone);
|
|
237
|
+
if (!isBaseSetInstance(target)) {
|
|
238
|
+
const SubClass = Object.getPrototypeOf(target).constructor;
|
|
239
|
+
return new SubClass(iterable);
|
|
240
|
+
}
|
|
241
|
+
return new Set(iterable);
|
|
242
|
+
}
|
|
243
|
+
const copy = Object.create(Object.getPrototypeOf(target));
|
|
244
|
+
for (const key in target)
|
|
245
|
+
copy[key] = deepClone(target[key]);
|
|
246
|
+
return copy;
|
|
247
|
+
}
|
|
248
|
+
function cloneIfNeeded(target) {
|
|
249
|
+
return isDraft(target) ? deepClone(target) : target;
|
|
250
|
+
}
|
|
251
|
+
function markChanged(proxyDraft) {
|
|
252
|
+
var _a;
|
|
253
|
+
proxyDraft.assignedMap = (_a = proxyDraft.assignedMap) !== null && _a !== undefined ? _a : new Map;
|
|
254
|
+
if (!proxyDraft.operated) {
|
|
255
|
+
proxyDraft.operated = true;
|
|
256
|
+
if (proxyDraft.parent) {
|
|
257
|
+
markChanged(proxyDraft.parent);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function throwFrozenError() {
|
|
262
|
+
throw new Error("Cannot modify frozen object");
|
|
263
|
+
}
|
|
264
|
+
function deepFreeze(target, subKey, updatedValues, stack, keys) {
|
|
265
|
+
{
|
|
266
|
+
updatedValues = updatedValues !== null && updatedValues !== undefined ? updatedValues : new WeakMap;
|
|
267
|
+
stack = stack !== null && stack !== undefined ? stack : [];
|
|
268
|
+
keys = keys !== null && keys !== undefined ? keys : [];
|
|
269
|
+
const value = updatedValues.has(target) ? updatedValues.get(target) : target;
|
|
270
|
+
if (stack.length > 0) {
|
|
271
|
+
const index = stack.indexOf(value);
|
|
272
|
+
if (value && typeof value === "object" && index !== -1) {
|
|
273
|
+
if (stack[0] === value) {
|
|
274
|
+
throw new Error(`Forbids circular reference`);
|
|
275
|
+
}
|
|
276
|
+
throw new Error(`Forbids circular reference: ~/${keys.slice(0, index).map((key, index2) => {
|
|
277
|
+
if (typeof key === "symbol")
|
|
278
|
+
return `[${key.toString()}]`;
|
|
279
|
+
const parent = stack[index2];
|
|
280
|
+
if (typeof key === "object" && (parent instanceof Map || parent instanceof Set))
|
|
281
|
+
return Array.from(parent.keys()).indexOf(key);
|
|
282
|
+
return key;
|
|
283
|
+
}).join("/")}`);
|
|
284
|
+
}
|
|
285
|
+
stack.push(value);
|
|
286
|
+
keys.push(subKey);
|
|
287
|
+
} else {
|
|
288
|
+
stack.push(value);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (Object.isFrozen(target) || isDraft(target)) {
|
|
292
|
+
{
|
|
293
|
+
stack.pop();
|
|
294
|
+
keys.pop();
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const type = getType(target);
|
|
299
|
+
switch (type) {
|
|
300
|
+
case 2:
|
|
301
|
+
for (const [key, value] of target) {
|
|
302
|
+
deepFreeze(key, key, updatedValues, stack, keys);
|
|
303
|
+
deepFreeze(value, key, updatedValues, stack, keys);
|
|
304
|
+
}
|
|
305
|
+
target.set = target.clear = target.delete = throwFrozenError;
|
|
306
|
+
break;
|
|
307
|
+
case 3:
|
|
308
|
+
for (const value of target) {
|
|
309
|
+
deepFreeze(value, value, updatedValues, stack, keys);
|
|
310
|
+
}
|
|
311
|
+
target.add = target.clear = target.delete = throwFrozenError;
|
|
312
|
+
break;
|
|
313
|
+
case 1:
|
|
314
|
+
Object.freeze(target);
|
|
315
|
+
let index = 0;
|
|
316
|
+
for (const value of target) {
|
|
317
|
+
deepFreeze(value, index, updatedValues, stack, keys);
|
|
318
|
+
index += 1;
|
|
319
|
+
}
|
|
320
|
+
break;
|
|
321
|
+
default:
|
|
322
|
+
Object.freeze(target);
|
|
323
|
+
Object.keys(target).forEach((name) => {
|
|
324
|
+
const value = target[name];
|
|
325
|
+
deepFreeze(value, name, updatedValues, stack, keys);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
{
|
|
329
|
+
stack.pop();
|
|
330
|
+
keys.pop();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function forEach(target, iter) {
|
|
334
|
+
const type = getType(target);
|
|
335
|
+
if (type === 0) {
|
|
336
|
+
Reflect.ownKeys(target).forEach((key) => {
|
|
337
|
+
iter(key, target[key], target);
|
|
338
|
+
});
|
|
339
|
+
} else if (type === 1) {
|
|
340
|
+
let index = 0;
|
|
341
|
+
for (const entry of target) {
|
|
342
|
+
iter(index, entry, target);
|
|
343
|
+
index += 1;
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
target.forEach((entry, index) => iter(index, entry, target));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function handleValue(target, handledSet, options) {
|
|
350
|
+
if (isDraft(target) || !isDraftable(target, options) || handledSet.has(target) || Object.isFrozen(target))
|
|
351
|
+
return;
|
|
352
|
+
const isSet = target instanceof Set;
|
|
353
|
+
const setMap = isSet ? new Map : undefined;
|
|
354
|
+
handledSet.add(target);
|
|
355
|
+
forEach(target, (key, value) => {
|
|
356
|
+
var _a;
|
|
357
|
+
if (isDraft(value)) {
|
|
358
|
+
const proxyDraft = getProxyDraft(value);
|
|
359
|
+
ensureShallowCopy(proxyDraft);
|
|
360
|
+
const updatedValue = ((_a = proxyDraft.assignedMap) === null || _a === undefined ? undefined : _a.size) || proxyDraft.operated ? proxyDraft.copy : proxyDraft.original;
|
|
361
|
+
set(isSet ? setMap : target, key, updatedValue);
|
|
362
|
+
} else {
|
|
363
|
+
handleValue(value, handledSet, options);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
if (setMap) {
|
|
367
|
+
const set2 = target;
|
|
368
|
+
const values = Array.from(set2);
|
|
369
|
+
set2.clear();
|
|
370
|
+
values.forEach((value) => {
|
|
371
|
+
set2.add(setMap.has(value) ? setMap.get(value) : value);
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function finalizeAssigned(proxyDraft, key) {
|
|
376
|
+
const copy = proxyDraft.type === 3 ? proxyDraft.setMap : proxyDraft.copy;
|
|
377
|
+
if (proxyDraft.finalities.revoke.length > 1 && proxyDraft.assignedMap.get(key) && copy) {
|
|
378
|
+
handleValue(get(copy, key), proxyDraft.finalities.handledSet, proxyDraft.options);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function finalizeSetValue(target) {
|
|
382
|
+
if (target.type === 3 && target.copy) {
|
|
383
|
+
target.copy.clear();
|
|
384
|
+
target.setMap.forEach((value) => {
|
|
385
|
+
target.copy.add(getValue(value));
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function finalizePatches(target, generatePatches, patches, inversePatches) {
|
|
390
|
+
const shouldFinalize = target.operated && target.assignedMap && target.assignedMap.size > 0 && !target.finalized;
|
|
391
|
+
if (shouldFinalize) {
|
|
392
|
+
if (patches && inversePatches) {
|
|
393
|
+
const basePath = getPath(target);
|
|
394
|
+
if (basePath) {
|
|
395
|
+
generatePatches(target, basePath, patches, inversePatches);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
target.finalized = true;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function markFinalization(target, key, value, generatePatches) {
|
|
402
|
+
const proxyDraft = getProxyDraft(value);
|
|
403
|
+
if (proxyDraft) {
|
|
404
|
+
if (!proxyDraft.callbacks) {
|
|
405
|
+
proxyDraft.callbacks = [];
|
|
406
|
+
}
|
|
407
|
+
proxyDraft.callbacks.push((patches, inversePatches) => {
|
|
408
|
+
var _a;
|
|
409
|
+
const copy = target.type === 3 ? target.setMap : target.copy;
|
|
410
|
+
if (isEqual(get(copy, key), value)) {
|
|
411
|
+
let updatedValue = proxyDraft.original;
|
|
412
|
+
if (proxyDraft.copy) {
|
|
413
|
+
updatedValue = proxyDraft.copy;
|
|
414
|
+
}
|
|
415
|
+
finalizeSetValue(target);
|
|
416
|
+
finalizePatches(target, generatePatches, patches, inversePatches);
|
|
417
|
+
if (target.options.enableAutoFreeze) {
|
|
418
|
+
target.options.updatedValues = (_a = target.options.updatedValues) !== null && _a !== undefined ? _a : new WeakMap;
|
|
419
|
+
target.options.updatedValues.set(updatedValue, proxyDraft.original);
|
|
420
|
+
}
|
|
421
|
+
set(copy, key, updatedValue);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
if (target.options.enableAutoFreeze) {
|
|
425
|
+
if (proxyDraft.finalities !== target.finalities) {
|
|
426
|
+
target.options.enableAutoFreeze = false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (isDraftable(value, target.options)) {
|
|
431
|
+
target.finalities.draft.push(() => {
|
|
432
|
+
const copy = target.type === 3 ? target.setMap : target.copy;
|
|
433
|
+
if (isEqual(get(copy, key), value)) {
|
|
434
|
+
finalizeAssigned(target, key);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray) {
|
|
440
|
+
let { original, assignedMap, options } = proxyState;
|
|
441
|
+
let copy = proxyState.copy;
|
|
442
|
+
if (copy.length < original.length) {
|
|
443
|
+
[original, copy] = [copy, original];
|
|
444
|
+
[patches, inversePatches] = [inversePatches, patches];
|
|
445
|
+
}
|
|
446
|
+
for (let index = 0;index < original.length; index += 1) {
|
|
447
|
+
if (assignedMap.get(index.toString()) && copy[index] !== original[index]) {
|
|
448
|
+
const _path = basePath.concat([index]);
|
|
449
|
+
const path = escapePath(_path, pathAsArray);
|
|
450
|
+
patches.push({
|
|
451
|
+
op: Operation.Replace,
|
|
452
|
+
path,
|
|
453
|
+
value: cloneIfNeeded(copy[index])
|
|
454
|
+
});
|
|
455
|
+
inversePatches.push({
|
|
456
|
+
op: Operation.Replace,
|
|
457
|
+
path,
|
|
458
|
+
value: cloneIfNeeded(original[index])
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
for (let index = original.length;index < copy.length; index += 1) {
|
|
463
|
+
const _path = basePath.concat([index]);
|
|
464
|
+
const path = escapePath(_path, pathAsArray);
|
|
465
|
+
patches.push({
|
|
466
|
+
op: Operation.Add,
|
|
467
|
+
path,
|
|
468
|
+
value: cloneIfNeeded(copy[index])
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (original.length < copy.length) {
|
|
472
|
+
const { arrayLengthAssignment = true } = options.enablePatches;
|
|
473
|
+
if (arrayLengthAssignment) {
|
|
474
|
+
const _path = basePath.concat(["length"]);
|
|
475
|
+
const path = escapePath(_path, pathAsArray);
|
|
476
|
+
inversePatches.push({
|
|
477
|
+
op: Operation.Replace,
|
|
478
|
+
path,
|
|
479
|
+
value: original.length
|
|
480
|
+
});
|
|
481
|
+
} else {
|
|
482
|
+
for (let index = copy.length;original.length < index; index -= 1) {
|
|
483
|
+
const _path = basePath.concat([index - 1]);
|
|
484
|
+
const path = escapePath(_path, pathAsArray);
|
|
485
|
+
inversePatches.push({
|
|
486
|
+
op: Operation.Remove,
|
|
487
|
+
path
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function generatePatchesFromAssigned({ original, copy, assignedMap }, basePath, patches, inversePatches, pathAsArray) {
|
|
494
|
+
assignedMap.forEach((assignedValue, key) => {
|
|
495
|
+
const originalValue = get(original, key);
|
|
496
|
+
const value = cloneIfNeeded(get(copy, key));
|
|
497
|
+
const op = !assignedValue ? Operation.Remove : has(original, key) ? Operation.Replace : Operation.Add;
|
|
498
|
+
if (isEqual(originalValue, value) && op === Operation.Replace)
|
|
499
|
+
return;
|
|
500
|
+
const _path = basePath.concat(key);
|
|
501
|
+
const path = escapePath(_path, pathAsArray);
|
|
502
|
+
patches.push(op === Operation.Remove ? { op, path } : { op, path, value });
|
|
503
|
+
inversePatches.push(op === Operation.Add ? { op: Operation.Remove, path } : op === Operation.Remove ? { op: Operation.Add, path, value: originalValue } : { op: Operation.Replace, path, value: originalValue });
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function generateSetPatches({ original, copy }, basePath, patches, inversePatches, pathAsArray) {
|
|
507
|
+
let index = 0;
|
|
508
|
+
original.forEach((value) => {
|
|
509
|
+
if (!copy.has(value)) {
|
|
510
|
+
const _path = basePath.concat([index]);
|
|
511
|
+
const path = escapePath(_path, pathAsArray);
|
|
512
|
+
patches.push({
|
|
513
|
+
op: Operation.Remove,
|
|
514
|
+
path,
|
|
515
|
+
value
|
|
516
|
+
});
|
|
517
|
+
inversePatches.unshift({
|
|
518
|
+
op: Operation.Add,
|
|
519
|
+
path,
|
|
520
|
+
value
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
index += 1;
|
|
524
|
+
});
|
|
525
|
+
index = 0;
|
|
526
|
+
copy.forEach((value) => {
|
|
527
|
+
if (!original.has(value)) {
|
|
528
|
+
const _path = basePath.concat([index]);
|
|
529
|
+
const path = escapePath(_path, pathAsArray);
|
|
530
|
+
patches.push({
|
|
531
|
+
op: Operation.Add,
|
|
532
|
+
path,
|
|
533
|
+
value
|
|
534
|
+
});
|
|
535
|
+
inversePatches.unshift({
|
|
536
|
+
op: Operation.Remove,
|
|
537
|
+
path,
|
|
538
|
+
value
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
index += 1;
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
function generatePatches(proxyState, basePath, patches, inversePatches) {
|
|
545
|
+
const { pathAsArray = true } = proxyState.options.enablePatches;
|
|
546
|
+
switch (proxyState.type) {
|
|
547
|
+
case 0:
|
|
548
|
+
case 2:
|
|
549
|
+
return generatePatchesFromAssigned(proxyState, basePath, patches, inversePatches, pathAsArray);
|
|
550
|
+
case 1:
|
|
551
|
+
return generateArrayPatches(proxyState, basePath, patches, inversePatches, pathAsArray);
|
|
552
|
+
case 3:
|
|
553
|
+
return generateSetPatches(proxyState, basePath, patches, inversePatches, pathAsArray);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
var readable = false;
|
|
557
|
+
var checkReadable = (value, options, ignoreCheckDraftable = false) => {
|
|
558
|
+
if (typeof value === "object" && value !== null && (!isDraftable(value, options) || ignoreCheckDraftable) && !readable) {
|
|
559
|
+
throw new Error(`Strict mode: Mutable data cannot be accessed directly, please use 'unsafe(callback)' wrap.`);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
var mapHandler = {
|
|
563
|
+
get size() {
|
|
564
|
+
const current = latest(getProxyDraft(this));
|
|
565
|
+
return current.size;
|
|
566
|
+
},
|
|
567
|
+
has(key) {
|
|
568
|
+
return latest(getProxyDraft(this)).has(key);
|
|
569
|
+
},
|
|
570
|
+
set(key, value) {
|
|
571
|
+
const target = getProxyDraft(this);
|
|
572
|
+
const source = latest(target);
|
|
573
|
+
if (!source.has(key) || !isEqual(source.get(key), value)) {
|
|
574
|
+
ensureShallowCopy(target);
|
|
575
|
+
markChanged(target);
|
|
576
|
+
target.assignedMap.set(key, true);
|
|
577
|
+
target.copy.set(key, value);
|
|
578
|
+
markFinalization(target, key, value, generatePatches);
|
|
579
|
+
}
|
|
580
|
+
return this;
|
|
581
|
+
},
|
|
582
|
+
delete(key) {
|
|
583
|
+
if (!this.has(key)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
const target = getProxyDraft(this);
|
|
587
|
+
ensureShallowCopy(target);
|
|
588
|
+
markChanged(target);
|
|
589
|
+
if (target.original.has(key)) {
|
|
590
|
+
target.assignedMap.set(key, false);
|
|
591
|
+
} else {
|
|
592
|
+
target.assignedMap.delete(key);
|
|
593
|
+
}
|
|
594
|
+
target.copy.delete(key);
|
|
595
|
+
return true;
|
|
596
|
+
},
|
|
597
|
+
clear() {
|
|
598
|
+
const target = getProxyDraft(this);
|
|
599
|
+
if (!this.size)
|
|
600
|
+
return;
|
|
601
|
+
ensureShallowCopy(target);
|
|
602
|
+
markChanged(target);
|
|
603
|
+
target.assignedMap = new Map;
|
|
604
|
+
for (const [key] of target.original) {
|
|
605
|
+
target.assignedMap.set(key, false);
|
|
606
|
+
}
|
|
607
|
+
target.copy.clear();
|
|
608
|
+
},
|
|
609
|
+
forEach(callback, thisArg) {
|
|
610
|
+
const target = getProxyDraft(this);
|
|
611
|
+
latest(target).forEach((_value, _key) => {
|
|
612
|
+
callback.call(thisArg, this.get(_key), _key, this);
|
|
613
|
+
});
|
|
614
|
+
},
|
|
615
|
+
get(key) {
|
|
616
|
+
var _a, _b;
|
|
617
|
+
const target = getProxyDraft(this);
|
|
618
|
+
const value = latest(target).get(key);
|
|
619
|
+
const mutable = ((_b = (_a = target.options).mark) === null || _b === undefined ? undefined : _b.call(_a, value, dataTypes)) === dataTypes.mutable;
|
|
620
|
+
if (target.options.strict) {
|
|
621
|
+
checkReadable(value, target.options, mutable);
|
|
622
|
+
}
|
|
623
|
+
if (mutable) {
|
|
624
|
+
return value;
|
|
625
|
+
}
|
|
626
|
+
if (target.finalized || !isDraftable(value, target.options)) {
|
|
627
|
+
return value;
|
|
628
|
+
}
|
|
629
|
+
if (value !== target.original.get(key)) {
|
|
630
|
+
return value;
|
|
631
|
+
}
|
|
632
|
+
const draft = internal.createDraft({
|
|
633
|
+
original: value,
|
|
634
|
+
parentDraft: target,
|
|
635
|
+
key,
|
|
636
|
+
finalities: target.finalities,
|
|
637
|
+
options: target.options
|
|
638
|
+
});
|
|
639
|
+
ensureShallowCopy(target);
|
|
640
|
+
target.copy.set(key, draft);
|
|
641
|
+
return draft;
|
|
642
|
+
},
|
|
643
|
+
keys() {
|
|
644
|
+
return latest(getProxyDraft(this)).keys();
|
|
645
|
+
},
|
|
646
|
+
values() {
|
|
647
|
+
const iterator = this.keys();
|
|
648
|
+
return {
|
|
649
|
+
[iteratorSymbol]: () => this.values(),
|
|
650
|
+
next: () => {
|
|
651
|
+
const result = iterator.next();
|
|
652
|
+
if (result.done)
|
|
653
|
+
return result;
|
|
654
|
+
const value = this.get(result.value);
|
|
655
|
+
return {
|
|
656
|
+
done: false,
|
|
657
|
+
value
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
},
|
|
662
|
+
entries() {
|
|
663
|
+
const iterator = this.keys();
|
|
664
|
+
return {
|
|
665
|
+
[iteratorSymbol]: () => this.entries(),
|
|
666
|
+
next: () => {
|
|
667
|
+
const result = iterator.next();
|
|
668
|
+
if (result.done)
|
|
669
|
+
return result;
|
|
670
|
+
const value = this.get(result.value);
|
|
671
|
+
return {
|
|
672
|
+
done: false,
|
|
673
|
+
value: [result.value, value]
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
},
|
|
678
|
+
[iteratorSymbol]() {
|
|
679
|
+
return this.entries();
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
var mapHandlerKeys = Reflect.ownKeys(mapHandler);
|
|
683
|
+
var getNextIterator = (target, iterator, { isValuesIterator }) => () => {
|
|
684
|
+
var _a, _b;
|
|
685
|
+
const result = iterator.next();
|
|
686
|
+
if (result.done)
|
|
687
|
+
return result;
|
|
688
|
+
const key = result.value;
|
|
689
|
+
let value = target.setMap.get(key);
|
|
690
|
+
const currentDraft = getProxyDraft(value);
|
|
691
|
+
const mutable = ((_b = (_a = target.options).mark) === null || _b === undefined ? undefined : _b.call(_a, value, dataTypes)) === dataTypes.mutable;
|
|
692
|
+
if (target.options.strict) {
|
|
693
|
+
checkReadable(key, target.options, mutable);
|
|
694
|
+
}
|
|
695
|
+
if (!mutable && !currentDraft && isDraftable(key, target.options) && !target.finalized && target.original.has(key)) {
|
|
696
|
+
const proxy = internal.createDraft({
|
|
697
|
+
original: key,
|
|
698
|
+
parentDraft: target,
|
|
699
|
+
key,
|
|
700
|
+
finalities: target.finalities,
|
|
701
|
+
options: target.options
|
|
702
|
+
});
|
|
703
|
+
target.setMap.set(key, proxy);
|
|
704
|
+
value = proxy;
|
|
705
|
+
} else if (currentDraft) {
|
|
706
|
+
value = currentDraft.proxy;
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
done: false,
|
|
710
|
+
value: isValuesIterator ? value : [value, value]
|
|
711
|
+
};
|
|
712
|
+
};
|
|
713
|
+
var setHandler = {
|
|
714
|
+
get size() {
|
|
715
|
+
const target = getProxyDraft(this);
|
|
716
|
+
return target.setMap.size;
|
|
717
|
+
},
|
|
718
|
+
has(value) {
|
|
719
|
+
const target = getProxyDraft(this);
|
|
720
|
+
if (target.setMap.has(value))
|
|
721
|
+
return true;
|
|
722
|
+
ensureShallowCopy(target);
|
|
723
|
+
const valueProxyDraft = getProxyDraft(value);
|
|
724
|
+
if (valueProxyDraft && target.setMap.has(valueProxyDraft.original))
|
|
725
|
+
return true;
|
|
726
|
+
return false;
|
|
727
|
+
},
|
|
728
|
+
add(value) {
|
|
729
|
+
const target = getProxyDraft(this);
|
|
730
|
+
if (!this.has(value)) {
|
|
731
|
+
ensureShallowCopy(target);
|
|
732
|
+
markChanged(target);
|
|
733
|
+
target.assignedMap.set(value, true);
|
|
734
|
+
target.setMap.set(value, value);
|
|
735
|
+
markFinalization(target, value, value, generatePatches);
|
|
736
|
+
}
|
|
737
|
+
return this;
|
|
738
|
+
},
|
|
739
|
+
delete(value) {
|
|
740
|
+
if (!this.has(value)) {
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
const target = getProxyDraft(this);
|
|
744
|
+
ensureShallowCopy(target);
|
|
745
|
+
markChanged(target);
|
|
746
|
+
const valueProxyDraft = getProxyDraft(value);
|
|
747
|
+
if (valueProxyDraft && target.setMap.has(valueProxyDraft.original)) {
|
|
748
|
+
target.assignedMap.set(valueProxyDraft.original, false);
|
|
749
|
+
return target.setMap.delete(valueProxyDraft.original);
|
|
750
|
+
}
|
|
751
|
+
if (!valueProxyDraft && target.setMap.has(value)) {
|
|
752
|
+
target.assignedMap.set(value, false);
|
|
753
|
+
} else {
|
|
754
|
+
target.assignedMap.delete(value);
|
|
755
|
+
}
|
|
756
|
+
return target.setMap.delete(value);
|
|
757
|
+
},
|
|
758
|
+
clear() {
|
|
759
|
+
if (!this.size)
|
|
760
|
+
return;
|
|
761
|
+
const target = getProxyDraft(this);
|
|
762
|
+
ensureShallowCopy(target);
|
|
763
|
+
markChanged(target);
|
|
764
|
+
for (const value of target.original) {
|
|
765
|
+
target.assignedMap.set(value, false);
|
|
766
|
+
}
|
|
767
|
+
target.setMap.clear();
|
|
768
|
+
},
|
|
769
|
+
values() {
|
|
770
|
+
const target = getProxyDraft(this);
|
|
771
|
+
ensureShallowCopy(target);
|
|
772
|
+
const iterator = target.setMap.keys();
|
|
773
|
+
return {
|
|
774
|
+
[Symbol.iterator]: () => this.values(),
|
|
775
|
+
next: getNextIterator(target, iterator, { isValuesIterator: true })
|
|
776
|
+
};
|
|
777
|
+
},
|
|
778
|
+
entries() {
|
|
779
|
+
const target = getProxyDraft(this);
|
|
780
|
+
ensureShallowCopy(target);
|
|
781
|
+
const iterator = target.setMap.keys();
|
|
782
|
+
return {
|
|
783
|
+
[Symbol.iterator]: () => this.entries(),
|
|
784
|
+
next: getNextIterator(target, iterator, {
|
|
785
|
+
isValuesIterator: false
|
|
786
|
+
})
|
|
787
|
+
};
|
|
788
|
+
},
|
|
789
|
+
keys() {
|
|
790
|
+
return this.values();
|
|
791
|
+
},
|
|
792
|
+
[iteratorSymbol]() {
|
|
793
|
+
return this.values();
|
|
794
|
+
},
|
|
795
|
+
forEach(callback, thisArg) {
|
|
796
|
+
const iterator = this.values();
|
|
797
|
+
let result = iterator.next();
|
|
798
|
+
while (!result.done) {
|
|
799
|
+
callback.call(thisArg, result.value, result.value, this);
|
|
800
|
+
result = iterator.next();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
if (Set.prototype.difference) {
|
|
805
|
+
Object.assign(setHandler, {
|
|
806
|
+
intersection(other) {
|
|
807
|
+
return Set.prototype.intersection.call(new Set(this.values()), other);
|
|
808
|
+
},
|
|
809
|
+
union(other) {
|
|
810
|
+
return Set.prototype.union.call(new Set(this.values()), other);
|
|
811
|
+
},
|
|
812
|
+
difference(other) {
|
|
813
|
+
return Set.prototype.difference.call(new Set(this.values()), other);
|
|
814
|
+
},
|
|
815
|
+
symmetricDifference(other) {
|
|
816
|
+
return Set.prototype.symmetricDifference.call(new Set(this.values()), other);
|
|
817
|
+
},
|
|
818
|
+
isSubsetOf(other) {
|
|
819
|
+
return Set.prototype.isSubsetOf.call(new Set(this.values()), other);
|
|
820
|
+
},
|
|
821
|
+
isSupersetOf(other) {
|
|
822
|
+
return Set.prototype.isSupersetOf.call(new Set(this.values()), other);
|
|
823
|
+
},
|
|
824
|
+
isDisjointFrom(other) {
|
|
825
|
+
return Set.prototype.isDisjointFrom.call(new Set(this.values()), other);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
var setHandlerKeys = Reflect.ownKeys(setHandler);
|
|
830
|
+
var proxyHandler = {
|
|
831
|
+
get(target, key, receiver) {
|
|
832
|
+
var _a, _b;
|
|
833
|
+
const copy = (_a = target.copy) === null || _a === undefined ? undefined : _a[key];
|
|
834
|
+
if (copy && target.finalities.draftsCache.has(copy)) {
|
|
835
|
+
return copy;
|
|
836
|
+
}
|
|
837
|
+
if (key === PROXY_DRAFT)
|
|
838
|
+
return target;
|
|
839
|
+
let markResult;
|
|
840
|
+
if (target.options.mark) {
|
|
841
|
+
const value2 = key === "size" && (target.original instanceof Map || target.original instanceof Set) ? Reflect.get(target.original, key) : Reflect.get(target.original, key, receiver);
|
|
842
|
+
markResult = target.options.mark(value2, dataTypes);
|
|
843
|
+
if (markResult === dataTypes.mutable) {
|
|
844
|
+
if (target.options.strict) {
|
|
845
|
+
checkReadable(value2, target.options, true);
|
|
846
|
+
}
|
|
847
|
+
return value2;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const source = latest(target);
|
|
851
|
+
if (source instanceof Map && mapHandlerKeys.includes(key)) {
|
|
852
|
+
if (key === "size") {
|
|
853
|
+
return Object.getOwnPropertyDescriptor(mapHandler, "size").get.call(target.proxy);
|
|
854
|
+
}
|
|
855
|
+
const handle = mapHandler[key];
|
|
856
|
+
return handle.bind(target.proxy);
|
|
857
|
+
}
|
|
858
|
+
if (source instanceof Set && setHandlerKeys.includes(key)) {
|
|
859
|
+
if (key === "size") {
|
|
860
|
+
return Object.getOwnPropertyDescriptor(setHandler, "size").get.call(target.proxy);
|
|
861
|
+
}
|
|
862
|
+
const handle = setHandler[key];
|
|
863
|
+
return handle.bind(target.proxy);
|
|
864
|
+
}
|
|
865
|
+
if (!has(source, key)) {
|
|
866
|
+
const desc = getDescriptor(source, key);
|
|
867
|
+
return desc ? `value` in desc ? desc.value : (_b = desc.get) === null || _b === undefined ? undefined : _b.call(target.proxy) : undefined;
|
|
868
|
+
}
|
|
869
|
+
const value = source[key];
|
|
870
|
+
if (target.options.strict) {
|
|
871
|
+
checkReadable(value, target.options);
|
|
872
|
+
}
|
|
873
|
+
if (target.finalized || !isDraftable(value, target.options)) {
|
|
874
|
+
return value;
|
|
875
|
+
}
|
|
876
|
+
if (value === peek(target.original, key)) {
|
|
877
|
+
ensureShallowCopy(target);
|
|
878
|
+
target.copy[key] = createDraft({
|
|
879
|
+
original: target.original[key],
|
|
880
|
+
parentDraft: target,
|
|
881
|
+
key: target.type === 1 ? Number(key) : key,
|
|
882
|
+
finalities: target.finalities,
|
|
883
|
+
options: target.options
|
|
884
|
+
});
|
|
885
|
+
if (typeof markResult === "function") {
|
|
886
|
+
const subProxyDraft = getProxyDraft(target.copy[key]);
|
|
887
|
+
ensureShallowCopy(subProxyDraft);
|
|
888
|
+
markChanged(subProxyDraft);
|
|
889
|
+
return subProxyDraft.copy;
|
|
890
|
+
}
|
|
891
|
+
return target.copy[key];
|
|
892
|
+
}
|
|
893
|
+
if (isDraft(value)) {
|
|
894
|
+
target.finalities.draftsCache.add(value);
|
|
895
|
+
}
|
|
896
|
+
return value;
|
|
897
|
+
},
|
|
898
|
+
set(target, key, value) {
|
|
899
|
+
var _a;
|
|
900
|
+
if (target.type === 3 || target.type === 2) {
|
|
901
|
+
throw new Error(`Map/Set draft does not support any property assignment.`);
|
|
902
|
+
}
|
|
903
|
+
let _key;
|
|
904
|
+
if (target.type === 1 && key !== "length" && !(Number.isInteger(_key = Number(key)) && _key >= 0 && (key === 0 || _key === 0 || String(_key) === String(key)))) {
|
|
905
|
+
throw new Error(`Only supports setting array indices and the 'length' property.`);
|
|
906
|
+
}
|
|
907
|
+
const desc = getDescriptor(latest(target), key);
|
|
908
|
+
if (desc === null || desc === undefined ? undefined : desc.set) {
|
|
909
|
+
desc.set.call(target.proxy, value);
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
const current = peek(latest(target), key);
|
|
913
|
+
const currentProxyDraft = getProxyDraft(current);
|
|
914
|
+
if (currentProxyDraft && isEqual(currentProxyDraft.original, value)) {
|
|
915
|
+
target.copy[key] = value;
|
|
916
|
+
target.assignedMap = (_a = target.assignedMap) !== null && _a !== undefined ? _a : new Map;
|
|
917
|
+
target.assignedMap.set(key, false);
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
if (isEqual(value, current) && (value !== undefined || has(target.original, key)))
|
|
921
|
+
return true;
|
|
922
|
+
ensureShallowCopy(target);
|
|
923
|
+
markChanged(target);
|
|
924
|
+
if (has(target.original, key) && isEqual(value, target.original[key])) {
|
|
925
|
+
target.assignedMap.delete(key);
|
|
926
|
+
} else {
|
|
927
|
+
target.assignedMap.set(key, true);
|
|
928
|
+
}
|
|
929
|
+
target.copy[key] = value;
|
|
930
|
+
markFinalization(target, key, value, generatePatches);
|
|
931
|
+
return true;
|
|
932
|
+
},
|
|
933
|
+
has(target, key) {
|
|
934
|
+
return key in latest(target);
|
|
935
|
+
},
|
|
936
|
+
ownKeys(target) {
|
|
937
|
+
return Reflect.ownKeys(latest(target));
|
|
938
|
+
},
|
|
939
|
+
getOwnPropertyDescriptor(target, key) {
|
|
940
|
+
const source = latest(target);
|
|
941
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
|
|
942
|
+
if (!descriptor)
|
|
943
|
+
return descriptor;
|
|
944
|
+
return {
|
|
945
|
+
writable: true,
|
|
946
|
+
configurable: target.type !== 1 || key !== "length",
|
|
947
|
+
enumerable: descriptor.enumerable,
|
|
948
|
+
value: source[key]
|
|
949
|
+
};
|
|
950
|
+
},
|
|
951
|
+
getPrototypeOf(target) {
|
|
952
|
+
return Reflect.getPrototypeOf(target.original);
|
|
953
|
+
},
|
|
954
|
+
setPrototypeOf() {
|
|
955
|
+
throw new Error(`Cannot call 'setPrototypeOf()' on drafts`);
|
|
956
|
+
},
|
|
957
|
+
defineProperty() {
|
|
958
|
+
throw new Error(`Cannot call 'defineProperty()' on drafts`);
|
|
959
|
+
},
|
|
960
|
+
deleteProperty(target, key) {
|
|
961
|
+
var _a;
|
|
962
|
+
if (target.type === 1) {
|
|
963
|
+
return proxyHandler.set.call(this, target, key, undefined, target.proxy);
|
|
964
|
+
}
|
|
965
|
+
if (peek(target.original, key) !== undefined || key in target.original) {
|
|
966
|
+
ensureShallowCopy(target);
|
|
967
|
+
markChanged(target);
|
|
968
|
+
target.assignedMap.set(key, false);
|
|
969
|
+
} else {
|
|
970
|
+
target.assignedMap = (_a = target.assignedMap) !== null && _a !== undefined ? _a : new Map;
|
|
971
|
+
target.assignedMap.delete(key);
|
|
972
|
+
}
|
|
973
|
+
if (target.copy)
|
|
974
|
+
delete target.copy[key];
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
function createDraft(createDraftOptions) {
|
|
979
|
+
const { original, parentDraft, key, finalities, options } = createDraftOptions;
|
|
980
|
+
const type = getType(original);
|
|
981
|
+
const proxyDraft = {
|
|
982
|
+
type,
|
|
983
|
+
finalized: false,
|
|
984
|
+
parent: parentDraft,
|
|
985
|
+
original,
|
|
986
|
+
copy: null,
|
|
987
|
+
proxy: null,
|
|
988
|
+
finalities,
|
|
989
|
+
options,
|
|
990
|
+
setMap: type === 3 ? new Map(original.entries()) : undefined
|
|
991
|
+
};
|
|
992
|
+
if (key || "key" in createDraftOptions) {
|
|
993
|
+
proxyDraft.key = key;
|
|
994
|
+
}
|
|
995
|
+
const { proxy, revoke } = Proxy.revocable(type === 1 ? Object.assign([], proxyDraft) : proxyDraft, proxyHandler);
|
|
996
|
+
finalities.revoke.push(revoke);
|
|
997
|
+
proxyDraft.proxy = proxy;
|
|
998
|
+
if (parentDraft) {
|
|
999
|
+
const target = parentDraft;
|
|
1000
|
+
target.finalities.draft.push((patches, inversePatches) => {
|
|
1001
|
+
var _a, _b;
|
|
1002
|
+
const oldProxyDraft = getProxyDraft(proxy);
|
|
1003
|
+
let copy = target.type === 3 ? target.setMap : target.copy;
|
|
1004
|
+
const draft = get(copy, key);
|
|
1005
|
+
const proxyDraft2 = getProxyDraft(draft);
|
|
1006
|
+
if (proxyDraft2) {
|
|
1007
|
+
let updatedValue = proxyDraft2.original;
|
|
1008
|
+
if (proxyDraft2.operated) {
|
|
1009
|
+
updatedValue = getValue(draft);
|
|
1010
|
+
}
|
|
1011
|
+
finalizeSetValue(proxyDraft2);
|
|
1012
|
+
finalizePatches(proxyDraft2, generatePatches, patches, inversePatches);
|
|
1013
|
+
if (target.options.enableAutoFreeze) {
|
|
1014
|
+
target.options.updatedValues = (_a = target.options.updatedValues) !== null && _a !== undefined ? _a : new WeakMap;
|
|
1015
|
+
target.options.updatedValues.set(updatedValue, proxyDraft2.original);
|
|
1016
|
+
}
|
|
1017
|
+
set(copy, key, updatedValue);
|
|
1018
|
+
}
|
|
1019
|
+
(_b = oldProxyDraft.callbacks) === null || _b === undefined || _b.forEach((callback) => {
|
|
1020
|
+
callback(patches, inversePatches);
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
} else {
|
|
1024
|
+
const target = getProxyDraft(proxy);
|
|
1025
|
+
target.finalities.draft.push((patches, inversePatches) => {
|
|
1026
|
+
finalizeSetValue(target);
|
|
1027
|
+
finalizePatches(target, generatePatches, patches, inversePatches);
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
return proxy;
|
|
1031
|
+
}
|
|
1032
|
+
internal.createDraft = createDraft;
|
|
1033
|
+
function finalizeDraft(result, returnedValue, patches, inversePatches, enableAutoFreeze) {
|
|
1034
|
+
var _a;
|
|
1035
|
+
const proxyDraft = getProxyDraft(result);
|
|
1036
|
+
const original = (_a = proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.original) !== null && _a !== undefined ? _a : result;
|
|
1037
|
+
const hasReturnedValue = !!returnedValue.length;
|
|
1038
|
+
if (proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.operated) {
|
|
1039
|
+
while (proxyDraft.finalities.draft.length > 0) {
|
|
1040
|
+
const finalize = proxyDraft.finalities.draft.pop();
|
|
1041
|
+
finalize(patches, inversePatches);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
const state = hasReturnedValue ? returnedValue[0] : proxyDraft ? proxyDraft.operated ? proxyDraft.copy : proxyDraft.original : result;
|
|
1045
|
+
if (proxyDraft)
|
|
1046
|
+
revokeProxy(proxyDraft);
|
|
1047
|
+
if (enableAutoFreeze) {
|
|
1048
|
+
deepFreeze(state, state, proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.options.updatedValues);
|
|
1049
|
+
}
|
|
1050
|
+
return [
|
|
1051
|
+
state,
|
|
1052
|
+
patches && hasReturnedValue ? [{ op: Operation.Replace, path: [], value: returnedValue[0] }] : patches,
|
|
1053
|
+
inversePatches && hasReturnedValue ? [{ op: Operation.Replace, path: [], value: original }] : inversePatches
|
|
1054
|
+
];
|
|
1055
|
+
}
|
|
1056
|
+
function draftify(baseState, options) {
|
|
1057
|
+
var _a;
|
|
1058
|
+
const finalities = {
|
|
1059
|
+
draft: [],
|
|
1060
|
+
revoke: [],
|
|
1061
|
+
handledSet: new WeakSet,
|
|
1062
|
+
draftsCache: new WeakSet
|
|
1063
|
+
};
|
|
1064
|
+
let patches;
|
|
1065
|
+
let inversePatches;
|
|
1066
|
+
if (options.enablePatches) {
|
|
1067
|
+
patches = [];
|
|
1068
|
+
inversePatches = [];
|
|
1069
|
+
}
|
|
1070
|
+
const isMutable = ((_a = options.mark) === null || _a === undefined ? undefined : _a.call(options, baseState, dataTypes)) === dataTypes.mutable || !isDraftable(baseState, options);
|
|
1071
|
+
const draft = isMutable ? baseState : createDraft({
|
|
1072
|
+
original: baseState,
|
|
1073
|
+
parentDraft: null,
|
|
1074
|
+
finalities,
|
|
1075
|
+
options
|
|
1076
|
+
});
|
|
1077
|
+
return [
|
|
1078
|
+
draft,
|
|
1079
|
+
(returnedValue = []) => {
|
|
1080
|
+
const [finalizedState, finalizedPatches, finalizedInversePatches] = finalizeDraft(draft, returnedValue, patches, inversePatches, options.enableAutoFreeze);
|
|
1081
|
+
return options.enablePatches ? [finalizedState, finalizedPatches, finalizedInversePatches] : finalizedState;
|
|
1082
|
+
}
|
|
1083
|
+
];
|
|
1084
|
+
}
|
|
1085
|
+
function handleReturnValue(options) {
|
|
1086
|
+
const { rootDraft, value, useRawReturn = false, isRoot = true } = options;
|
|
1087
|
+
forEach(value, (key, item, source) => {
|
|
1088
|
+
const proxyDraft = getProxyDraft(item);
|
|
1089
|
+
if (proxyDraft && rootDraft && proxyDraft.finalities === rootDraft.finalities) {
|
|
1090
|
+
options.isContainDraft = true;
|
|
1091
|
+
const currentValue = proxyDraft.original;
|
|
1092
|
+
if (source instanceof Set) {
|
|
1093
|
+
const arr = Array.from(source);
|
|
1094
|
+
source.clear();
|
|
1095
|
+
arr.forEach((_item) => source.add(key === _item ? currentValue : _item));
|
|
1096
|
+
} else {
|
|
1097
|
+
set(source, key, currentValue);
|
|
1098
|
+
}
|
|
1099
|
+
} else if (typeof item === "object" && item !== null) {
|
|
1100
|
+
options.value = item;
|
|
1101
|
+
options.isRoot = false;
|
|
1102
|
+
handleReturnValue(options);
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
if (isRoot) {
|
|
1106
|
+
if (!options.isContainDraft)
|
|
1107
|
+
console.warn(`The return value does not contain any draft, please use 'rawReturn()' to wrap the return value to improve performance.`);
|
|
1108
|
+
if (useRawReturn) {
|
|
1109
|
+
console.warn(`The return value contains drafts, please don't use 'rawReturn()' to wrap the return value.`);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
function getCurrent(target) {
|
|
1114
|
+
var _a;
|
|
1115
|
+
const proxyDraft = getProxyDraft(target);
|
|
1116
|
+
if (!isDraftable(target, proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.options))
|
|
1117
|
+
return target;
|
|
1118
|
+
const type = getType(target);
|
|
1119
|
+
if (proxyDraft && !proxyDraft.operated)
|
|
1120
|
+
return proxyDraft.original;
|
|
1121
|
+
let currentValue;
|
|
1122
|
+
function ensureShallowCopy2() {
|
|
1123
|
+
currentValue = type === 2 ? !isBaseMapInstance(target) ? new (Object.getPrototypeOf(target)).constructor(target) : new Map(target) : type === 3 ? Array.from(proxyDraft.setMap.values()) : shallowCopy(target, proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.options);
|
|
1124
|
+
}
|
|
1125
|
+
if (proxyDraft) {
|
|
1126
|
+
proxyDraft.finalized = true;
|
|
1127
|
+
try {
|
|
1128
|
+
ensureShallowCopy2();
|
|
1129
|
+
} finally {
|
|
1130
|
+
proxyDraft.finalized = false;
|
|
1131
|
+
}
|
|
1132
|
+
} else {
|
|
1133
|
+
currentValue = target;
|
|
1134
|
+
}
|
|
1135
|
+
forEach(currentValue, (key, value) => {
|
|
1136
|
+
if (proxyDraft && isEqual(get(proxyDraft.original, key), value))
|
|
1137
|
+
return;
|
|
1138
|
+
const newValue = getCurrent(value);
|
|
1139
|
+
if (newValue !== value) {
|
|
1140
|
+
if (currentValue === target)
|
|
1141
|
+
ensureShallowCopy2();
|
|
1142
|
+
set(currentValue, key, newValue);
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
if (type === 3) {
|
|
1146
|
+
const value = (_a = proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.original) !== null && _a !== undefined ? _a : currentValue;
|
|
1147
|
+
return !isBaseSetInstance(value) ? new (Object.getPrototypeOf(value)).constructor(currentValue) : new Set(currentValue);
|
|
1148
|
+
}
|
|
1149
|
+
return currentValue;
|
|
1150
|
+
}
|
|
1151
|
+
function current(target) {
|
|
1152
|
+
if (!isDraft(target)) {
|
|
1153
|
+
throw new Error(`current() is only used for Draft, parameter: ${target}`);
|
|
1154
|
+
}
|
|
1155
|
+
return getCurrent(target);
|
|
1156
|
+
}
|
|
1157
|
+
var makeCreator = (arg) => {
|
|
1158
|
+
if (arg !== undefined && Object.prototype.toString.call(arg) !== "[object Object]") {
|
|
1159
|
+
throw new Error(`Invalid options: ${String(arg)}, 'options' should be an object.`);
|
|
1160
|
+
}
|
|
1161
|
+
return function create(arg0, arg1, arg2) {
|
|
1162
|
+
var _a, _b, _c;
|
|
1163
|
+
if (typeof arg0 === "function" && typeof arg1 !== "function") {
|
|
1164
|
+
return function(base2, ...args) {
|
|
1165
|
+
return create(base2, (draft2) => arg0.call(this, draft2, ...args), arg1);
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
const base = arg0;
|
|
1169
|
+
const mutate = arg1;
|
|
1170
|
+
let options = arg2;
|
|
1171
|
+
if (typeof arg1 !== "function") {
|
|
1172
|
+
options = arg1;
|
|
1173
|
+
}
|
|
1174
|
+
if (options !== undefined && Object.prototype.toString.call(options) !== "[object Object]") {
|
|
1175
|
+
throw new Error(`Invalid options: ${options}, 'options' should be an object.`);
|
|
1176
|
+
}
|
|
1177
|
+
options = Object.assign(Object.assign({}, arg), options);
|
|
1178
|
+
const state = isDraft(base) ? current(base) : base;
|
|
1179
|
+
const mark = Array.isArray(options.mark) ? (value, types) => {
|
|
1180
|
+
for (const mark2 of options.mark) {
|
|
1181
|
+
if (typeof mark2 !== "function") {
|
|
1182
|
+
throw new Error(`Invalid mark: ${mark2}, 'mark' should be a function.`);
|
|
1183
|
+
}
|
|
1184
|
+
const result2 = mark2(value, types);
|
|
1185
|
+
if (result2) {
|
|
1186
|
+
return result2;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return;
|
|
1190
|
+
} : options.mark;
|
|
1191
|
+
const enablePatches = (_a = options.enablePatches) !== null && _a !== undefined ? _a : false;
|
|
1192
|
+
const strict = (_b = options.strict) !== null && _b !== undefined ? _b : false;
|
|
1193
|
+
const enableAutoFreeze = (_c = options.enableAutoFreeze) !== null && _c !== undefined ? _c : false;
|
|
1194
|
+
const _options = {
|
|
1195
|
+
enableAutoFreeze,
|
|
1196
|
+
mark,
|
|
1197
|
+
strict,
|
|
1198
|
+
enablePatches
|
|
1199
|
+
};
|
|
1200
|
+
if (!isDraftable(state, _options) && typeof state === "object" && state !== null) {
|
|
1201
|
+
throw new Error(`Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable.`);
|
|
1202
|
+
}
|
|
1203
|
+
const [draft, finalize] = draftify(state, _options);
|
|
1204
|
+
if (typeof arg1 !== "function") {
|
|
1205
|
+
if (!isDraftable(state, _options)) {
|
|
1206
|
+
throw new Error(`Invalid base state: create() only supports plain objects, arrays, Set, Map or using mark() to mark the state as immutable.`);
|
|
1207
|
+
}
|
|
1208
|
+
return [draft, finalize];
|
|
1209
|
+
}
|
|
1210
|
+
let result;
|
|
1211
|
+
try {
|
|
1212
|
+
result = mutate(draft);
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
revokeProxy(getProxyDraft(draft));
|
|
1215
|
+
throw error;
|
|
1216
|
+
}
|
|
1217
|
+
const returnValue = (value) => {
|
|
1218
|
+
const proxyDraft = getProxyDraft(draft);
|
|
1219
|
+
if (!isDraft(value)) {
|
|
1220
|
+
if (value !== undefined && !isEqual(value, draft) && (proxyDraft === null || proxyDraft === undefined ? undefined : proxyDraft.operated)) {
|
|
1221
|
+
throw new Error(`Either the value is returned as a new non-draft value, or only the draft is modified without returning any value.`);
|
|
1222
|
+
}
|
|
1223
|
+
const rawReturnValue = value === null || value === undefined ? undefined : value[RAW_RETURN_SYMBOL];
|
|
1224
|
+
if (rawReturnValue) {
|
|
1225
|
+
const _value = rawReturnValue[0];
|
|
1226
|
+
if (_options.strict && typeof value === "object" && value !== null) {
|
|
1227
|
+
handleReturnValue({
|
|
1228
|
+
rootDraft: proxyDraft,
|
|
1229
|
+
value,
|
|
1230
|
+
useRawReturn: true
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
return finalize([_value]);
|
|
1234
|
+
}
|
|
1235
|
+
if (value !== undefined) {
|
|
1236
|
+
if (typeof value === "object" && value !== null) {
|
|
1237
|
+
handleReturnValue({ rootDraft: proxyDraft, value });
|
|
1238
|
+
}
|
|
1239
|
+
return finalize([value]);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (value === draft || value === undefined) {
|
|
1243
|
+
return finalize([]);
|
|
1244
|
+
}
|
|
1245
|
+
const returnedProxyDraft = getProxyDraft(value);
|
|
1246
|
+
if (_options === returnedProxyDraft.options) {
|
|
1247
|
+
if (returnedProxyDraft.operated) {
|
|
1248
|
+
throw new Error(`Cannot return a modified child draft.`);
|
|
1249
|
+
}
|
|
1250
|
+
return finalize([current(value)]);
|
|
1251
|
+
}
|
|
1252
|
+
return finalize([value]);
|
|
1253
|
+
};
|
|
1254
|
+
if (result instanceof Promise) {
|
|
1255
|
+
return result.then(returnValue, (error) => {
|
|
1256
|
+
revokeProxy(getProxyDraft(draft));
|
|
1257
|
+
throw error;
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
return returnValue(result);
|
|
1261
|
+
};
|
|
1262
|
+
};
|
|
1263
|
+
var create = makeCreator();
|
|
1264
|
+
function apply(state, patches, applyOptions) {
|
|
1265
|
+
let i;
|
|
1266
|
+
for (i = patches.length - 1;i >= 0; i -= 1) {
|
|
1267
|
+
const { value, op, path } = patches[i];
|
|
1268
|
+
if (!path.length && op === Operation.Replace || path === "" && op === Operation.Add) {
|
|
1269
|
+
state = value;
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
if (i > -1) {
|
|
1274
|
+
patches = patches.slice(i + 1);
|
|
1275
|
+
}
|
|
1276
|
+
const mutate = (draft) => {
|
|
1277
|
+
patches.forEach((patch) => {
|
|
1278
|
+
const { path: _path, op } = patch;
|
|
1279
|
+
const path = unescapePath(_path);
|
|
1280
|
+
let base = draft;
|
|
1281
|
+
for (let index = 0;index < path.length - 1; index += 1) {
|
|
1282
|
+
const parentType = getType(base);
|
|
1283
|
+
let key2 = path[index];
|
|
1284
|
+
if (typeof key2 !== "string" && typeof key2 !== "number") {
|
|
1285
|
+
key2 = String(key2);
|
|
1286
|
+
}
|
|
1287
|
+
if ((parentType === 0 || parentType === 1) && (key2 === "__proto__" || key2 === "constructor") || typeof base === "function" && key2 === "prototype") {
|
|
1288
|
+
throw new Error(`Patching reserved attributes like __proto__ and constructor is not allowed.`);
|
|
1289
|
+
}
|
|
1290
|
+
base = get(parentType === 3 ? Array.from(base) : base, key2);
|
|
1291
|
+
if (typeof base !== "object") {
|
|
1292
|
+
throw new Error(`Cannot apply patch at '${path.join("/")}'.`);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
const type = getType(base);
|
|
1296
|
+
const value = deepClone(patch.value);
|
|
1297
|
+
const key = path[path.length - 1];
|
|
1298
|
+
switch (op) {
|
|
1299
|
+
case Operation.Replace:
|
|
1300
|
+
switch (type) {
|
|
1301
|
+
case 2:
|
|
1302
|
+
return base.set(key, value);
|
|
1303
|
+
case 3:
|
|
1304
|
+
throw new Error(`Cannot apply replace patch to set.`);
|
|
1305
|
+
default:
|
|
1306
|
+
return base[key] = value;
|
|
1307
|
+
}
|
|
1308
|
+
case Operation.Add:
|
|
1309
|
+
switch (type) {
|
|
1310
|
+
case 1:
|
|
1311
|
+
return key === "-" ? base.push(value) : base.splice(key, 0, value);
|
|
1312
|
+
case 2:
|
|
1313
|
+
return base.set(key, value);
|
|
1314
|
+
case 3:
|
|
1315
|
+
return base.add(value);
|
|
1316
|
+
default:
|
|
1317
|
+
return base[key] = value;
|
|
1318
|
+
}
|
|
1319
|
+
case Operation.Remove:
|
|
1320
|
+
switch (type) {
|
|
1321
|
+
case 1:
|
|
1322
|
+
return base.splice(key, 1);
|
|
1323
|
+
case 2:
|
|
1324
|
+
return base.delete(key);
|
|
1325
|
+
case 3:
|
|
1326
|
+
return base.delete(patch.value);
|
|
1327
|
+
default:
|
|
1328
|
+
return delete base[key];
|
|
1329
|
+
}
|
|
1330
|
+
default:
|
|
1331
|
+
throw new Error(`Unsupported patch operation: ${op}.`);
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
};
|
|
1335
|
+
if (applyOptions === null || applyOptions === undefined ? undefined : applyOptions.mutable) {
|
|
1336
|
+
{
|
|
1337
|
+
if (Object.keys(applyOptions).filter((key) => key !== "mutable").length) {
|
|
1338
|
+
console.warn('The "mutable" option is not allowed to be used with other options.');
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
mutate(state);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (isDraft(state)) {
|
|
1345
|
+
if (applyOptions !== undefined) {
|
|
1346
|
+
throw new Error(`Cannot apply patches with options to a draft.`);
|
|
1347
|
+
}
|
|
1348
|
+
mutate(state);
|
|
1349
|
+
return state;
|
|
1350
|
+
}
|
|
1351
|
+
return create(state, mutate, Object.assign(Object.assign({}, applyOptions), { enablePatches: false }));
|
|
1352
|
+
}
|
|
1353
|
+
var constructorString = Object.prototype.constructor.toString();
|
|
1354
|
+
|
|
1355
|
+
// ../../../core/dist/index.js
|
|
1356
|
+
var TOKEN_PREFIX = "arc:token:";
|
|
1357
|
+
function hasLocalStorage() {
|
|
1358
|
+
return typeof localStorage !== "undefined";
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
class AuthAdapter {
|
|
1362
|
+
scopes = new Map;
|
|
1363
|
+
setToken(token, scope = "default") {
|
|
1364
|
+
if (!token) {
|
|
1365
|
+
this.scopes.delete(scope);
|
|
1366
|
+
if (hasLocalStorage())
|
|
1367
|
+
localStorage.removeItem(TOKEN_PREFIX + scope);
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
try {
|
|
1371
|
+
const parts = token.split(".");
|
|
1372
|
+
if (parts.length !== 3) {
|
|
1373
|
+
this.scopes.delete(scope);
|
|
1374
|
+
if (hasLocalStorage())
|
|
1375
|
+
localStorage.removeItem(TOKEN_PREFIX + scope);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
1379
|
+
this.scopes.set(scope, {
|
|
1380
|
+
raw: token,
|
|
1381
|
+
decoded: {
|
|
1382
|
+
tokenName: payload.tokenName,
|
|
1383
|
+
params: payload.params || {},
|
|
1384
|
+
iat: payload.iat,
|
|
1385
|
+
exp: payload.exp
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
if (hasLocalStorage())
|
|
1389
|
+
localStorage.setItem(TOKEN_PREFIX + scope, token);
|
|
1390
|
+
} catch {
|
|
1391
|
+
this.scopes.delete(scope);
|
|
1392
|
+
if (hasLocalStorage())
|
|
1393
|
+
localStorage.removeItem(TOKEN_PREFIX + scope);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
loadPersisted() {
|
|
1397
|
+
if (!hasLocalStorage())
|
|
1398
|
+
return;
|
|
1399
|
+
for (let i = 0;i < localStorage.length; i++) {
|
|
1400
|
+
const key = localStorage.key(i);
|
|
1401
|
+
if (key?.startsWith(TOKEN_PREFIX)) {
|
|
1402
|
+
const scope = key.slice(TOKEN_PREFIX.length);
|
|
1403
|
+
const raw = localStorage.getItem(key);
|
|
1404
|
+
if (raw) {
|
|
1405
|
+
try {
|
|
1406
|
+
const parts = raw.split(".");
|
|
1407
|
+
if (parts.length !== 3)
|
|
1408
|
+
continue;
|
|
1409
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
1410
|
+
this.scopes.set(scope, {
|
|
1411
|
+
raw,
|
|
1412
|
+
decoded: {
|
|
1413
|
+
tokenName: payload.tokenName,
|
|
1414
|
+
params: payload.params || {},
|
|
1415
|
+
iat: payload.iat,
|
|
1416
|
+
exp: payload.exp
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
} catch {
|
|
1420
|
+
localStorage.removeItem(key);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
getToken(scope = "default") {
|
|
1427
|
+
return this.scopes.get(scope)?.raw ?? null;
|
|
1428
|
+
}
|
|
1429
|
+
getDecoded(scope = "default") {
|
|
1430
|
+
return this.scopes.get(scope)?.decoded ?? null;
|
|
1431
|
+
}
|
|
1432
|
+
getParams(scope = "default") {
|
|
1433
|
+
return this.scopes.get(scope)?.decoded?.params ?? null;
|
|
1434
|
+
}
|
|
1435
|
+
getTokenName(scope = "default") {
|
|
1436
|
+
return this.scopes.get(scope)?.decoded?.tokenName ?? null;
|
|
1437
|
+
}
|
|
1438
|
+
isAuthenticated(scope) {
|
|
1439
|
+
if (scope !== undefined) {
|
|
1440
|
+
return this.scopes.has(scope);
|
|
1441
|
+
}
|
|
1442
|
+
return this.scopes.size > 0;
|
|
1443
|
+
}
|
|
1444
|
+
isExpired(scope = "default") {
|
|
1445
|
+
const entry = this.scopes.get(scope);
|
|
1446
|
+
if (!entry?.decoded?.exp)
|
|
1447
|
+
return false;
|
|
1448
|
+
return Date.now() > entry.decoded.exp;
|
|
1449
|
+
}
|
|
1450
|
+
getAllScopes() {
|
|
1451
|
+
return Array.from(this.scopes.keys());
|
|
1452
|
+
}
|
|
1453
|
+
clear() {
|
|
1454
|
+
if (hasLocalStorage()) {
|
|
1455
|
+
for (const scope of this.scopes.keys()) {
|
|
1456
|
+
localStorage.removeItem(TOKEN_PREFIX + scope);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
this.scopes.clear();
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
var eventWireInstanceCounter = 0;
|
|
1463
|
+
|
|
1464
|
+
class EventWire {
|
|
1465
|
+
baseUrl;
|
|
1466
|
+
instanceId;
|
|
1467
|
+
ws = null;
|
|
1468
|
+
state = "disconnected";
|
|
1469
|
+
scopeTokens = new Map;
|
|
1470
|
+
lastHostEventId = null;
|
|
1471
|
+
pendingEvents = [];
|
|
1472
|
+
onEventCallback;
|
|
1473
|
+
onSyncedCallback;
|
|
1474
|
+
reconnectTimeout;
|
|
1475
|
+
syncRequested = false;
|
|
1476
|
+
viewSubscriptions = new Map;
|
|
1477
|
+
viewSubCounter = 0;
|
|
1478
|
+
pendingViewSubs = [];
|
|
1479
|
+
constructor(baseUrl) {
|
|
1480
|
+
this.baseUrl = baseUrl;
|
|
1481
|
+
this.instanceId = ++eventWireInstanceCounter;
|
|
1482
|
+
}
|
|
1483
|
+
setScopeToken(scope, token) {
|
|
1484
|
+
if (token === null) {
|
|
1485
|
+
this.scopeTokens.delete(scope);
|
|
1486
|
+
} else {
|
|
1487
|
+
this.scopeTokens.set(scope, token);
|
|
1488
|
+
}
|
|
1489
|
+
if (this.state === "connected" && this.ws) {
|
|
1490
|
+
if (token !== null) {
|
|
1491
|
+
this.ws.send(JSON.stringify({
|
|
1492
|
+
type: "scope:auth",
|
|
1493
|
+
scope,
|
|
1494
|
+
token
|
|
1495
|
+
}));
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
hasScopeTokens() {
|
|
1500
|
+
return this.scopeTokens.size > 0;
|
|
1501
|
+
}
|
|
1502
|
+
setLastHostEventId(id) {
|
|
1503
|
+
this.lastHostEventId = id;
|
|
1504
|
+
}
|
|
1505
|
+
getLastHostEventId() {
|
|
1506
|
+
return this.lastHostEventId;
|
|
1507
|
+
}
|
|
1508
|
+
connect() {
|
|
1509
|
+
if (this.state !== "disconnected")
|
|
1510
|
+
return;
|
|
1511
|
+
this.state = "connecting";
|
|
1512
|
+
let wsUrl;
|
|
1513
|
+
if (this.baseUrl.startsWith("http://") || this.baseUrl.startsWith("https://")) {
|
|
1514
|
+
wsUrl = this.baseUrl.replace(/^http/, "ws");
|
|
1515
|
+
} else {
|
|
1516
|
+
const protocol = typeof window !== "undefined" && window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
1517
|
+
const host = typeof window !== "undefined" ? window.location.host : "localhost";
|
|
1518
|
+
wsUrl = `${protocol}//${host}${this.baseUrl}`;
|
|
1519
|
+
}
|
|
1520
|
+
const url = `${wsUrl}/ws`;
|
|
1521
|
+
try {
|
|
1522
|
+
this.ws = new WebSocket(url);
|
|
1523
|
+
this.ws.onopen = () => {
|
|
1524
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
1525
|
+
this.state = "connected";
|
|
1526
|
+
this.sendAllScopeTokens();
|
|
1527
|
+
this.requestSync();
|
|
1528
|
+
this.flushPendingEvents();
|
|
1529
|
+
this.flushPendingViewSubs();
|
|
1530
|
+
} else {
|
|
1531
|
+
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
this.ws.onmessage = (event) => {
|
|
1535
|
+
try {
|
|
1536
|
+
const message = JSON.parse(event.data);
|
|
1537
|
+
this.handleMessage(message);
|
|
1538
|
+
} catch (err) {
|
|
1539
|
+
console.error("EventWire: Failed to parse message", err);
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
this.ws.onclose = (event) => {
|
|
1543
|
+
this.state = "disconnected";
|
|
1544
|
+
this.ws = null;
|
|
1545
|
+
console.log("EventWire disconnected");
|
|
1546
|
+
this.scheduleReconnect();
|
|
1547
|
+
};
|
|
1548
|
+
this.ws.onerror = (err) => {
|
|
1549
|
+
console.error("EventWire error:", err);
|
|
1550
|
+
if (this.state === "connecting") {
|
|
1551
|
+
this.state = "disconnected";
|
|
1552
|
+
this.ws = null;
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
this.state = "disconnected";
|
|
1557
|
+
this.ws = null;
|
|
1558
|
+
console.error("EventWire: Failed to connect", err);
|
|
1559
|
+
this.scheduleReconnect();
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
disconnect() {
|
|
1563
|
+
if (this.reconnectTimeout) {
|
|
1564
|
+
clearTimeout(this.reconnectTimeout);
|
|
1565
|
+
this.reconnectTimeout = undefined;
|
|
1566
|
+
}
|
|
1567
|
+
if (this.ws) {
|
|
1568
|
+
this.ws.close();
|
|
1569
|
+
this.ws = null;
|
|
1570
|
+
}
|
|
1571
|
+
this.state = "disconnected";
|
|
1572
|
+
this.syncRequested = false;
|
|
1573
|
+
}
|
|
1574
|
+
syncEvents(events) {
|
|
1575
|
+
const isActuallyConnected = this.ws && this.ws.readyState === WebSocket.OPEN;
|
|
1576
|
+
if (!isActuallyConnected) {
|
|
1577
|
+
if (this.state === "connected" && !this.ws) {
|
|
1578
|
+
this.state = "disconnected";
|
|
1579
|
+
}
|
|
1580
|
+
this.pendingEvents.push(...events);
|
|
1581
|
+
if (this.state === "disconnected") {
|
|
1582
|
+
this.connect();
|
|
1583
|
+
}
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (this.ws) {
|
|
1587
|
+
this.ws.send(JSON.stringify({
|
|
1588
|
+
type: "sync-events",
|
|
1589
|
+
events: events.map((e) => ({
|
|
1590
|
+
localId: e.localId,
|
|
1591
|
+
type: e.type,
|
|
1592
|
+
payload: e.payload,
|
|
1593
|
+
createdAt: e.createdAt
|
|
1594
|
+
}))
|
|
1595
|
+
}));
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
onEvent(callback) {
|
|
1599
|
+
this.onEventCallback = callback;
|
|
1600
|
+
}
|
|
1601
|
+
onSynced(callback) {
|
|
1602
|
+
this.onSyncedCallback = callback;
|
|
1603
|
+
}
|
|
1604
|
+
subscribeQuery(descriptor, callback, scope) {
|
|
1605
|
+
const subscriptionId = `qs_${++this.viewSubCounter}_${Date.now()}`;
|
|
1606
|
+
this.viewSubscriptions.set(subscriptionId, callback);
|
|
1607
|
+
if (this.state === "connected" && this.ws) {
|
|
1608
|
+
this.ws.send(JSON.stringify({
|
|
1609
|
+
type: "subscribe-query",
|
|
1610
|
+
subscriptionId,
|
|
1611
|
+
descriptor,
|
|
1612
|
+
scope
|
|
1613
|
+
}));
|
|
1614
|
+
} else {
|
|
1615
|
+
this.pendingViewSubs.push({ subscriptionId, descriptor, scope });
|
|
1616
|
+
}
|
|
1617
|
+
return subscriptionId;
|
|
1618
|
+
}
|
|
1619
|
+
unsubscribeQuery(subscriptionId) {
|
|
1620
|
+
this.viewSubscriptions.delete(subscriptionId);
|
|
1621
|
+
if (this.state === "connected" && this.ws) {
|
|
1622
|
+
this.ws.send(JSON.stringify({
|
|
1623
|
+
type: "unsubscribe-query",
|
|
1624
|
+
subscriptionId
|
|
1625
|
+
}));
|
|
1626
|
+
}
|
|
1627
|
+
this.pendingViewSubs = this.pendingViewSubs.filter((s) => s.subscriptionId !== subscriptionId);
|
|
1628
|
+
}
|
|
1629
|
+
getState() {
|
|
1630
|
+
return this.state;
|
|
1631
|
+
}
|
|
1632
|
+
sendAllScopeTokens() {
|
|
1633
|
+
if (!this.ws || this.state !== "connected")
|
|
1634
|
+
return;
|
|
1635
|
+
for (const [scope, token] of this.scopeTokens) {
|
|
1636
|
+
this.ws.send(JSON.stringify({
|
|
1637
|
+
type: "scope:auth",
|
|
1638
|
+
scope,
|
|
1639
|
+
token
|
|
1640
|
+
}));
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
handleMessage(message) {
|
|
1644
|
+
switch (message.type) {
|
|
1645
|
+
case "events":
|
|
1646
|
+
if (message.events && Array.isArray(message.events)) {
|
|
1647
|
+
for (const event of message.events) {
|
|
1648
|
+
if (this.onEventCallback) {
|
|
1649
|
+
this.onEventCallback(event);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
break;
|
|
1654
|
+
case "sync-complete":
|
|
1655
|
+
this.syncRequested = false;
|
|
1656
|
+
if (message.lastHostEventId) {
|
|
1657
|
+
this.lastHostEventId = message.lastHostEventId;
|
|
1658
|
+
}
|
|
1659
|
+
break;
|
|
1660
|
+
case "query-data": {
|
|
1661
|
+
const cb = this.viewSubscriptions.get(message.subscriptionId);
|
|
1662
|
+
if (cb) {
|
|
1663
|
+
cb(message.data);
|
|
1664
|
+
}
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
case "command-result":
|
|
1668
|
+
break;
|
|
1669
|
+
case "error":
|
|
1670
|
+
console.error("EventWire error from host:", message.message);
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
requestSync() {
|
|
1675
|
+
if (!this.ws || this.state !== "connected")
|
|
1676
|
+
return;
|
|
1677
|
+
if (this.syncRequested)
|
|
1678
|
+
return;
|
|
1679
|
+
this.syncRequested = true;
|
|
1680
|
+
this.ws.send(JSON.stringify({
|
|
1681
|
+
type: "request-sync",
|
|
1682
|
+
lastHostEventId: this.lastHostEventId
|
|
1683
|
+
}));
|
|
1684
|
+
}
|
|
1685
|
+
flushPendingEvents() {
|
|
1686
|
+
if (this.pendingEvents.length > 0) {
|
|
1687
|
+
this.syncEvents(this.pendingEvents);
|
|
1688
|
+
this.pendingEvents = [];
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
flushPendingViewSubs() {
|
|
1692
|
+
if (!this.ws || this.state !== "connected")
|
|
1693
|
+
return;
|
|
1694
|
+
for (const sub of this.pendingViewSubs) {
|
|
1695
|
+
this.ws.send(JSON.stringify({
|
|
1696
|
+
type: "subscribe-query",
|
|
1697
|
+
subscriptionId: sub.subscriptionId,
|
|
1698
|
+
descriptor: sub.descriptor,
|
|
1699
|
+
scope: sub.scope
|
|
1700
|
+
}));
|
|
1701
|
+
}
|
|
1702
|
+
this.pendingViewSubs = [];
|
|
1703
|
+
}
|
|
1704
|
+
scheduleReconnect() {
|
|
1705
|
+
if (this.reconnectTimeout)
|
|
1706
|
+
return;
|
|
1707
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
1708
|
+
this.reconnectTimeout = undefined;
|
|
1709
|
+
this.connect();
|
|
1710
|
+
}, 3000);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
var EVENT_TABLES = {
|
|
1714
|
+
events: "events",
|
|
1715
|
+
tags: "event_tags",
|
|
1716
|
+
syncStatus: "event_sync_status"
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
class LocalEventPublisher {
|
|
1720
|
+
dataStorage;
|
|
1721
|
+
views = [];
|
|
1722
|
+
syncCallback;
|
|
1723
|
+
subscribers = new Map;
|
|
1724
|
+
constructor(dataStorage) {
|
|
1725
|
+
this.dataStorage = dataStorage;
|
|
1726
|
+
}
|
|
1727
|
+
onPublish(callback) {
|
|
1728
|
+
this.syncCallback = callback;
|
|
1729
|
+
}
|
|
1730
|
+
registerViews(views) {
|
|
1731
|
+
this.views = views;
|
|
1732
|
+
}
|
|
1733
|
+
subscribe(eventType, callback) {
|
|
1734
|
+
if (!this.subscribers.has(eventType)) {
|
|
1735
|
+
this.subscribers.set(eventType, new Set);
|
|
1736
|
+
}
|
|
1737
|
+
this.subscribers.get(eventType).add(callback);
|
|
1738
|
+
return () => {
|
|
1739
|
+
const subs = this.subscribers.get(eventType);
|
|
1740
|
+
if (subs) {
|
|
1741
|
+
subs.delete(callback);
|
|
1742
|
+
if (subs.size === 0) {
|
|
1743
|
+
this.subscribers.delete(eventType);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
async publish(event) {
|
|
1749
|
+
const allChanges = [];
|
|
1750
|
+
const storedEvent = {
|
|
1751
|
+
_id: event.id,
|
|
1752
|
+
type: event.type,
|
|
1753
|
+
payload: JSON.stringify(event.payload),
|
|
1754
|
+
createdAt: event.createdAt.toISOString()
|
|
1755
|
+
};
|
|
1756
|
+
allChanges.push({
|
|
1757
|
+
store: EVENT_TABLES.events,
|
|
1758
|
+
changes: [{ type: "set", data: storedEvent }]
|
|
1759
|
+
});
|
|
1760
|
+
if (event.payload && typeof event.payload === "object") {
|
|
1761
|
+
const tagChanges = [];
|
|
1762
|
+
for (const [key, value] of Object.entries(event.payload)) {
|
|
1763
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1764
|
+
tagChanges.push({
|
|
1765
|
+
type: "set",
|
|
1766
|
+
data: {
|
|
1767
|
+
_id: `${event.id}_${key}`,
|
|
1768
|
+
eventId: event.id,
|
|
1769
|
+
tagKey: key,
|
|
1770
|
+
tagValue: String(value)
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (tagChanges.length > 0) {
|
|
1776
|
+
allChanges.push({
|
|
1777
|
+
store: EVENT_TABLES.tags,
|
|
1778
|
+
changes: tagChanges
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
const syncStatus = {
|
|
1783
|
+
_id: event.id,
|
|
1784
|
+
eventId: event.id,
|
|
1785
|
+
synced: false,
|
|
1786
|
+
timestamp: Date.now(),
|
|
1787
|
+
retryCount: 0
|
|
1788
|
+
};
|
|
1789
|
+
allChanges.push({
|
|
1790
|
+
store: EVENT_TABLES.syncStatus,
|
|
1791
|
+
changes: [{ type: "set", data: syncStatus }]
|
|
1792
|
+
});
|
|
1793
|
+
const viewChanges = await this.collectViewChanges(event);
|
|
1794
|
+
allChanges.push(...viewChanges);
|
|
1795
|
+
await this.dataStorage.commitChanges(allChanges);
|
|
1796
|
+
await this.notifySubscribers(event);
|
|
1797
|
+
if (this.syncCallback) {
|
|
1798
|
+
this.syncCallback(event);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
async notifySubscribers(event) {
|
|
1802
|
+
const wildcardSubs = this.subscribers.get("*");
|
|
1803
|
+
if (wildcardSubs) {
|
|
1804
|
+
for (const callback of wildcardSubs) {
|
|
1805
|
+
try {
|
|
1806
|
+
const result = callback(event);
|
|
1807
|
+
if (result instanceof Promise)
|
|
1808
|
+
await result;
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
console.error(`[EventPublisher] Wildcard subscriber error:`, error);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
const subs = this.subscribers.get(event.type);
|
|
1815
|
+
if (!subs || subs.size === 0)
|
|
1816
|
+
return;
|
|
1817
|
+
const promises = [];
|
|
1818
|
+
for (const callback of subs) {
|
|
1819
|
+
try {
|
|
1820
|
+
const result = callback(event);
|
|
1821
|
+
if (result instanceof Promise) {
|
|
1822
|
+
promises.push(result);
|
|
1823
|
+
}
|
|
1824
|
+
} catch (error) {
|
|
1825
|
+
console.error(`Listener error for event "${event.type}":`, error);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
if (promises.length > 0) {
|
|
1829
|
+
await Promise.all(promises.map((p) => p.catch((e) => console.error(e))));
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
async markSynced(eventIds) {
|
|
1833
|
+
const syncStatusStore = this.dataStorage.getStore(EVENT_TABLES.syncStatus);
|
|
1834
|
+
const changes = eventIds.map((eventId) => ({
|
|
1835
|
+
type: "modify",
|
|
1836
|
+
id: eventId,
|
|
1837
|
+
data: { synced: true }
|
|
1838
|
+
}));
|
|
1839
|
+
await syncStatusStore.applyChanges(changes);
|
|
1840
|
+
}
|
|
1841
|
+
async getUnsyncedEvents(eventType) {
|
|
1842
|
+
const syncStatusStore = this.dataStorage.getStore(EVENT_TABLES.syncStatus);
|
|
1843
|
+
const eventsStore = this.dataStorage.getStore(EVENT_TABLES.events);
|
|
1844
|
+
const tagsStore = this.dataStorage.getStore(EVENT_TABLES.tags);
|
|
1845
|
+
const unsyncedStatuses = await syncStatusStore.find({
|
|
1846
|
+
where: { synced: false }
|
|
1847
|
+
});
|
|
1848
|
+
const results = [];
|
|
1849
|
+
for (const syncStatus of unsyncedStatuses) {
|
|
1850
|
+
const whereClause = { _id: syncStatus.eventId };
|
|
1851
|
+
if (eventType) {
|
|
1852
|
+
whereClause.type = eventType;
|
|
1853
|
+
}
|
|
1854
|
+
const events = await eventsStore.find({
|
|
1855
|
+
where: whereClause
|
|
1856
|
+
});
|
|
1857
|
+
if (events.length === 0)
|
|
1858
|
+
continue;
|
|
1859
|
+
const tags = await tagsStore.find({
|
|
1860
|
+
where: { eventId: syncStatus.eventId }
|
|
1861
|
+
});
|
|
1862
|
+
results.push({
|
|
1863
|
+
event: events[0],
|
|
1864
|
+
syncStatus,
|
|
1865
|
+
tags
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
return results;
|
|
1869
|
+
}
|
|
1870
|
+
async collectViewChanges(event) {
|
|
1871
|
+
const allViewChanges = [];
|
|
1872
|
+
for (const view of this.views) {
|
|
1873
|
+
const handlers = view.getHandlers();
|
|
1874
|
+
const handler = handlers[event.type];
|
|
1875
|
+
if (!handler)
|
|
1876
|
+
continue;
|
|
1877
|
+
const viewChanges = [];
|
|
1878
|
+
const ctx = {
|
|
1879
|
+
set: async (id, data) => {
|
|
1880
|
+
viewChanges.push({
|
|
1881
|
+
type: "set",
|
|
1882
|
+
data: { _id: String(id), ...data }
|
|
1883
|
+
});
|
|
1884
|
+
},
|
|
1885
|
+
modify: async (id, data) => {
|
|
1886
|
+
viewChanges.push({
|
|
1887
|
+
type: "modify",
|
|
1888
|
+
id: String(id),
|
|
1889
|
+
data
|
|
1890
|
+
});
|
|
1891
|
+
},
|
|
1892
|
+
remove: async (id) => {
|
|
1893
|
+
viewChanges.push({
|
|
1894
|
+
type: "delete",
|
|
1895
|
+
id: String(id)
|
|
1896
|
+
});
|
|
1897
|
+
},
|
|
1898
|
+
find: async (options) => {
|
|
1899
|
+
const viewStore = this.dataStorage.getStore(view.name);
|
|
1900
|
+
return viewStore.find(options);
|
|
1901
|
+
},
|
|
1902
|
+
findOne: async (where) => {
|
|
1903
|
+
const viewStore = this.dataStorage.getStore(view.name);
|
|
1904
|
+
const results = await viewStore.find({ where });
|
|
1905
|
+
return results[0];
|
|
1906
|
+
},
|
|
1907
|
+
$auth: {}
|
|
1908
|
+
};
|
|
1909
|
+
await handler(ctx, event);
|
|
1910
|
+
if (viewChanges.length > 0) {
|
|
1911
|
+
allViewChanges.push({
|
|
1912
|
+
store: view.name,
|
|
1913
|
+
changes: viewChanges
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
return allViewChanges;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
class ArcOptional {
|
|
1921
|
+
parent;
|
|
1922
|
+
constructor(parent) {
|
|
1923
|
+
this.parent = parent;
|
|
1924
|
+
}
|
|
1925
|
+
parse(value) {
|
|
1926
|
+
if (!value)
|
|
1927
|
+
return null;
|
|
1928
|
+
return this.parent.parse(value);
|
|
1929
|
+
}
|
|
1930
|
+
serialize(value) {
|
|
1931
|
+
if (value)
|
|
1932
|
+
return this.parent.serialize(value);
|
|
1933
|
+
return null;
|
|
1934
|
+
}
|
|
1935
|
+
deserialize(value) {
|
|
1936
|
+
if (value == null)
|
|
1937
|
+
return;
|
|
1938
|
+
return this.parent.deserialize(value);
|
|
1939
|
+
}
|
|
1940
|
+
validate(value) {
|
|
1941
|
+
if (!value)
|
|
1942
|
+
return false;
|
|
1943
|
+
return this.parent.validate(value);
|
|
1944
|
+
}
|
|
1945
|
+
toJsonSchema() {
|
|
1946
|
+
const parentSchema = this.parent.toJsonSchema?.() ?? {};
|
|
1947
|
+
return {
|
|
1948
|
+
anyOf: [parentSchema, { type: "null" }]
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
getColumnData() {
|
|
1952
|
+
const parentColumnData = this.parent.getColumnData?.();
|
|
1953
|
+
if (!parentColumnData) {
|
|
1954
|
+
throw new Error(`Parent element does not implement getColumnData()`);
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
...parentColumnData,
|
|
1958
|
+
storeData: {
|
|
1959
|
+
...parentColumnData.storeData,
|
|
1960
|
+
isNullable: true
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
class ArcBranded {
|
|
1967
|
+
parent;
|
|
1968
|
+
brand;
|
|
1969
|
+
constructor(parent, brand) {
|
|
1970
|
+
this.parent = parent;
|
|
1971
|
+
this.brand = brand;
|
|
1972
|
+
}
|
|
1973
|
+
get name() {
|
|
1974
|
+
return this.brand;
|
|
1975
|
+
}
|
|
1976
|
+
serialize(value) {
|
|
1977
|
+
return this.parent.serialize(value);
|
|
1978
|
+
}
|
|
1979
|
+
deserialize(value) {
|
|
1980
|
+
return this.parent.deserialize(value);
|
|
1981
|
+
}
|
|
1982
|
+
parse(value) {
|
|
1983
|
+
return this.parent.parse(value);
|
|
1984
|
+
}
|
|
1985
|
+
optional() {
|
|
1986
|
+
return new ArcOptional(this);
|
|
1987
|
+
}
|
|
1988
|
+
validate(value) {
|
|
1989
|
+
return this.parent.validate(value);
|
|
1990
|
+
}
|
|
1991
|
+
toJsonSchema() {
|
|
1992
|
+
return this.parent.toJsonSchema?.() ?? {};
|
|
1993
|
+
}
|
|
1994
|
+
getColumnData() {
|
|
1995
|
+
const parentColumnData = this.parent.getColumnData?.();
|
|
1996
|
+
if (!parentColumnData) {
|
|
1997
|
+
throw new Error(`Parent element does not implement getColumnData()`);
|
|
1998
|
+
}
|
|
1999
|
+
return parentColumnData;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
class ArcDefault {
|
|
2004
|
+
parent;
|
|
2005
|
+
defaultValueOrCallback;
|
|
2006
|
+
constructor(parent, defaultValueOrCallback) {
|
|
2007
|
+
this.parent = parent;
|
|
2008
|
+
this.defaultValueOrCallback = defaultValueOrCallback;
|
|
2009
|
+
}
|
|
2010
|
+
validate(value) {
|
|
2011
|
+
throw new Error("Method not implemented.");
|
|
2012
|
+
}
|
|
2013
|
+
parse(value) {
|
|
2014
|
+
if (value)
|
|
2015
|
+
return this.parent.parse(value);
|
|
2016
|
+
if (typeof this.defaultValueOrCallback === "function") {
|
|
2017
|
+
return this.defaultValueOrCallback();
|
|
2018
|
+
} else
|
|
2019
|
+
return this.defaultValueOrCallback;
|
|
2020
|
+
}
|
|
2021
|
+
serialize(value) {
|
|
2022
|
+
return this.parent.serialize(value);
|
|
2023
|
+
}
|
|
2024
|
+
deserialize(value) {
|
|
2025
|
+
if (value)
|
|
2026
|
+
return this.parent.deserialize(value);
|
|
2027
|
+
if (typeof this.defaultValueOrCallback === "function") {
|
|
2028
|
+
return this.defaultValueOrCallback();
|
|
2029
|
+
} else
|
|
2030
|
+
return this.defaultValueOrCallback;
|
|
2031
|
+
}
|
|
2032
|
+
toJsonSchema() {
|
|
2033
|
+
const schema = this.parent.toJsonSchema?.() ?? {};
|
|
2034
|
+
const defaultValue = typeof this.defaultValueOrCallback === "function" ? this.defaultValueOrCallback() : this.defaultValueOrCallback;
|
|
2035
|
+
return {
|
|
2036
|
+
...schema,
|
|
2037
|
+
default: defaultValue
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
getColumnData() {
|
|
2041
|
+
const parentColumnData = this.parent.getColumnData?.();
|
|
2042
|
+
if (!parentColumnData) {
|
|
2043
|
+
throw new Error(`Parent element does not implement getColumnData()`);
|
|
2044
|
+
}
|
|
2045
|
+
const defaultValue = typeof this.defaultValueOrCallback === "function" ? this.defaultValueOrCallback() : this.defaultValueOrCallback;
|
|
2046
|
+
return {
|
|
2047
|
+
...parentColumnData,
|
|
2048
|
+
defaultValue
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
class ArcAbstract {
|
|
2054
|
+
validations;
|
|
2055
|
+
_description;
|
|
2056
|
+
_storeData;
|
|
2057
|
+
constructor(validations = []) {
|
|
2058
|
+
this.validations = validations;
|
|
2059
|
+
}
|
|
2060
|
+
description(description) {
|
|
2061
|
+
const clone = this.clone();
|
|
2062
|
+
clone._description = description;
|
|
2063
|
+
return clone;
|
|
2064
|
+
}
|
|
2065
|
+
default(defaultValueOrCallback) {
|
|
2066
|
+
return new ArcDefault(this, defaultValueOrCallback);
|
|
2067
|
+
}
|
|
2068
|
+
optional() {
|
|
2069
|
+
return new ArcOptional(this);
|
|
2070
|
+
}
|
|
2071
|
+
branded(name) {
|
|
2072
|
+
return new ArcBranded(this, name);
|
|
2073
|
+
}
|
|
2074
|
+
clone() {
|
|
2075
|
+
const Constructor = this.constructor;
|
|
2076
|
+
const newInstance = Object.assign(new Constructor, this);
|
|
2077
|
+
newInstance._description = this._description;
|
|
2078
|
+
newInstance._storeData = this._storeData ? { ...this._storeData } : undefined;
|
|
2079
|
+
return newInstance;
|
|
2080
|
+
}
|
|
2081
|
+
validate(value) {
|
|
2082
|
+
const errors = this.validations.reduce((acc, { name, validator }) => {
|
|
2083
|
+
try {
|
|
2084
|
+
acc[name] = validator(value);
|
|
2085
|
+
} catch (error) {
|
|
2086
|
+
acc[name] = error;
|
|
2087
|
+
}
|
|
2088
|
+
return acc;
|
|
2089
|
+
}, {});
|
|
2090
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
2091
|
+
return errors;
|
|
2092
|
+
}
|
|
2093
|
+
return false;
|
|
2094
|
+
}
|
|
2095
|
+
pipeValidation(name, validator) {
|
|
2096
|
+
const newInstance = this.clone();
|
|
2097
|
+
newInstance.validations = [...this.validations, { name, validator }];
|
|
2098
|
+
return newInstance;
|
|
2099
|
+
}
|
|
2100
|
+
toJsonSchema() {
|
|
2101
|
+
const schema = {};
|
|
2102
|
+
if (this._description) {
|
|
2103
|
+
schema.description = this._description;
|
|
2104
|
+
}
|
|
2105
|
+
return schema;
|
|
2106
|
+
}
|
|
2107
|
+
getValidations() {
|
|
2108
|
+
return this.validations;
|
|
2109
|
+
}
|
|
2110
|
+
primaryKey() {
|
|
2111
|
+
const clone = this.clone();
|
|
2112
|
+
clone._storeData = { ...clone._storeData, isPrimaryKey: true };
|
|
2113
|
+
return clone;
|
|
2114
|
+
}
|
|
2115
|
+
autoIncrement() {
|
|
2116
|
+
const clone = this.clone();
|
|
2117
|
+
clone._storeData = { ...clone._storeData, isAutoIncrement: true };
|
|
2118
|
+
return clone;
|
|
2119
|
+
}
|
|
2120
|
+
unique() {
|
|
2121
|
+
const clone = this.clone();
|
|
2122
|
+
clone._storeData = { ...clone._storeData, isUnique: true };
|
|
2123
|
+
return clone;
|
|
2124
|
+
}
|
|
2125
|
+
index() {
|
|
2126
|
+
const clone = this.clone();
|
|
2127
|
+
clone._storeData = { ...clone._storeData, hasIndex: true };
|
|
2128
|
+
return clone;
|
|
2129
|
+
}
|
|
2130
|
+
foreignKey(table, column, options) {
|
|
2131
|
+
const clone = this.clone();
|
|
2132
|
+
clone._storeData = {
|
|
2133
|
+
...clone._storeData,
|
|
2134
|
+
foreignKey: { table, column, ...options }
|
|
2135
|
+
};
|
|
2136
|
+
return clone;
|
|
2137
|
+
}
|
|
2138
|
+
databaseType(overrides) {
|
|
2139
|
+
const clone = this.clone();
|
|
2140
|
+
clone._storeData = {
|
|
2141
|
+
...clone._storeData,
|
|
2142
|
+
databaseType: { ...clone._storeData?.databaseType, ...overrides }
|
|
2143
|
+
};
|
|
2144
|
+
return clone;
|
|
2145
|
+
}
|
|
2146
|
+
columnOptions(options) {
|
|
2147
|
+
const clone = this.clone();
|
|
2148
|
+
clone._storeData = {
|
|
2149
|
+
...clone._storeData,
|
|
2150
|
+
columnOptions: { ...clone._storeData?.columnOptions, ...options }
|
|
2151
|
+
};
|
|
2152
|
+
return clone;
|
|
2153
|
+
}
|
|
2154
|
+
getStoreData() {
|
|
2155
|
+
return this._storeData;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
function primitiveTypeComparatorStrategyFactory(type) {
|
|
2159
|
+
return (value) => typeof value === type;
|
|
2160
|
+
}
|
|
2161
|
+
function typeValidatorBuilder(typeName, comparatorStrategy) {
|
|
2162
|
+
const comparator = comparatorStrategy || primitiveTypeComparatorStrategyFactory(typeName);
|
|
2163
|
+
return {
|
|
2164
|
+
name: "type",
|
|
2165
|
+
validator: (value) => {
|
|
2166
|
+
const valueType = typeof value;
|
|
2167
|
+
if (!comparator(value))
|
|
2168
|
+
return { current: valueType, expected: typeName };
|
|
2169
|
+
return false;
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
var arrayValidator = typeValidatorBuilder("array", Array.isArray);
|
|
2174
|
+
|
|
2175
|
+
class ArcArray extends ArcAbstract {
|
|
2176
|
+
parent;
|
|
2177
|
+
constructor(parent) {
|
|
2178
|
+
super([
|
|
2179
|
+
arrayValidator,
|
|
2180
|
+
{
|
|
2181
|
+
name: "schema",
|
|
2182
|
+
validator: (values) => {
|
|
2183
|
+
const errors = values.reduce((acc, value, index) => {
|
|
2184
|
+
const error = parent.validate(value);
|
|
2185
|
+
if (error)
|
|
2186
|
+
acc[index] = error;
|
|
2187
|
+
return acc;
|
|
2188
|
+
}, {});
|
|
2189
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
2190
|
+
return errors;
|
|
2191
|
+
}
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
]);
|
|
2196
|
+
this.parent = parent;
|
|
2197
|
+
}
|
|
2198
|
+
minLength(min) {
|
|
2199
|
+
return this.validation("minLength", (value) => {
|
|
2200
|
+
if (value.length < min)
|
|
2201
|
+
return {
|
|
2202
|
+
currentLength: value.length,
|
|
2203
|
+
minLength: min
|
|
2204
|
+
};
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
maxLength(max) {
|
|
2208
|
+
return this.validation("maxLength", (value) => {
|
|
2209
|
+
if (value.length > max)
|
|
2210
|
+
return {
|
|
2211
|
+
currentLength: value.length,
|
|
2212
|
+
maxLength: max
|
|
2213
|
+
};
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
length(number) {
|
|
2217
|
+
return this.validation("length", (value) => {
|
|
2218
|
+
if (value.length !== number) {
|
|
2219
|
+
return {
|
|
2220
|
+
currentLength: value.length,
|
|
2221
|
+
length: number
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
nonEmpty() {
|
|
2227
|
+
return this.validation("nonEmpty", (value) => {
|
|
2228
|
+
if (value.length === 0)
|
|
2229
|
+
return { msg: "array is empty" };
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
parse(value) {
|
|
2233
|
+
return value.map((v) => this.parent.parse(v));
|
|
2234
|
+
}
|
|
2235
|
+
serialize(value) {
|
|
2236
|
+
return value.map((v) => this.parent.serialize(v));
|
|
2237
|
+
}
|
|
2238
|
+
deserialize(value) {
|
|
2239
|
+
if (!Array.isArray(value))
|
|
2240
|
+
return [];
|
|
2241
|
+
return value.map((v) => this.parent.deserialize(v));
|
|
2242
|
+
}
|
|
2243
|
+
deserializePath(path, value) {
|
|
2244
|
+
if (path.length === 0) {
|
|
2245
|
+
return this.deserialize(value);
|
|
2246
|
+
}
|
|
2247
|
+
if (this.parent instanceof ArcObject || this.parent instanceof ArcArray) {
|
|
2248
|
+
return this.parent.deserializePath(path, value);
|
|
2249
|
+
}
|
|
2250
|
+
return this.parent.deserialize(value);
|
|
2251
|
+
}
|
|
2252
|
+
validation(name, validator) {
|
|
2253
|
+
const instance = this.pipeValidation(name, validator);
|
|
2254
|
+
return instance;
|
|
2255
|
+
}
|
|
2256
|
+
toJsonSchema() {
|
|
2257
|
+
const schema = {
|
|
2258
|
+
type: "array",
|
|
2259
|
+
items: this.parent.toJsonSchema?.() ?? {}
|
|
2260
|
+
};
|
|
2261
|
+
if (this._description) {
|
|
2262
|
+
schema.description = this._description;
|
|
2263
|
+
}
|
|
2264
|
+
return schema;
|
|
2265
|
+
}
|
|
2266
|
+
getColumnData() {
|
|
2267
|
+
const storeData = this.getStoreData();
|
|
2268
|
+
return {
|
|
2269
|
+
type: "array",
|
|
2270
|
+
storeData: {
|
|
2271
|
+
...storeData,
|
|
2272
|
+
isNullable: false
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
var objectValidator = typeValidatorBuilder("object");
|
|
2278
|
+
|
|
2279
|
+
class ArcObject extends ArcAbstract {
|
|
2280
|
+
rawShape;
|
|
2281
|
+
constructor(rawShape) {
|
|
2282
|
+
super([
|
|
2283
|
+
objectValidator,
|
|
2284
|
+
{
|
|
2285
|
+
name: "schema",
|
|
2286
|
+
validator: (value) => {
|
|
2287
|
+
const errors = Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
2288
|
+
if (!element.validate) {
|
|
2289
|
+
return acc;
|
|
2290
|
+
}
|
|
2291
|
+
acc[key] = element.validate(value[key]);
|
|
2292
|
+
return acc;
|
|
2293
|
+
}, {});
|
|
2294
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
2295
|
+
return errors;
|
|
2296
|
+
}
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
]);
|
|
2301
|
+
this.rawShape = rawShape;
|
|
2302
|
+
}
|
|
2303
|
+
parse(value) {
|
|
2304
|
+
return Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
2305
|
+
acc[key] = element.parse(value[key]);
|
|
2306
|
+
return acc;
|
|
2307
|
+
}, {});
|
|
2308
|
+
}
|
|
2309
|
+
serialize(value) {
|
|
2310
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
2311
|
+
if (!this.rawShape[key]) {
|
|
2312
|
+
acc[key] = value2;
|
|
2313
|
+
} else {
|
|
2314
|
+
acc[key] = this.rawShape[key].serialize(value2);
|
|
2315
|
+
}
|
|
2316
|
+
return acc;
|
|
2317
|
+
}, {});
|
|
2318
|
+
}
|
|
2319
|
+
deserialize(value) {
|
|
2320
|
+
return Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
2321
|
+
key,
|
|
2322
|
+
element.deserialize(value[key])
|
|
2323
|
+
]));
|
|
2324
|
+
}
|
|
2325
|
+
deserializePath(path, value) {
|
|
2326
|
+
if (path.length === 0) {
|
|
2327
|
+
return this.deserialize(value);
|
|
2328
|
+
}
|
|
2329
|
+
const [key, ...restPath] = path;
|
|
2330
|
+
const element = this.rawShape[key];
|
|
2331
|
+
if (!element) {
|
|
2332
|
+
console.warn(`No element found for key: ${key}`);
|
|
2333
|
+
return value;
|
|
2334
|
+
}
|
|
2335
|
+
if (element instanceof ArcObject || element instanceof ArcArray) {
|
|
2336
|
+
return element.deserializePath(restPath, value);
|
|
2337
|
+
}
|
|
2338
|
+
return element.deserialize(value);
|
|
2339
|
+
}
|
|
2340
|
+
parsePartial(value) {
|
|
2341
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
2342
|
+
acc[key] = this.rawShape[key].parse(value2);
|
|
2343
|
+
return acc;
|
|
2344
|
+
}, {});
|
|
2345
|
+
}
|
|
2346
|
+
deserializePartial(value) {
|
|
2347
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
2348
|
+
acc[key] = this.rawShape[key].deserialize(value2);
|
|
2349
|
+
return acc;
|
|
2350
|
+
}, {});
|
|
2351
|
+
}
|
|
2352
|
+
serializePartial(value) {
|
|
2353
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
2354
|
+
if (!this.rawShape[key]) {
|
|
2355
|
+
console.warn(`No element found for key: ${key}`);
|
|
2356
|
+
return acc;
|
|
2357
|
+
}
|
|
2358
|
+
acc[key] = this.rawShape[key].serialize(value2);
|
|
2359
|
+
return acc;
|
|
2360
|
+
}, {});
|
|
2361
|
+
}
|
|
2362
|
+
validatePartial(value) {
|
|
2363
|
+
const errors = Object.entries(value).reduce((acc, [key, val]) => {
|
|
2364
|
+
if (!this.rawShape[key]) {
|
|
2365
|
+
return acc;
|
|
2366
|
+
}
|
|
2367
|
+
const result = this.rawShape[key].validate(val);
|
|
2368
|
+
if (result) {
|
|
2369
|
+
acc[key] = result;
|
|
2370
|
+
}
|
|
2371
|
+
return acc;
|
|
2372
|
+
}, {});
|
|
2373
|
+
if (Object.keys(errors).length > 0) {
|
|
2374
|
+
return { schema: errors };
|
|
2375
|
+
}
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2378
|
+
pick(...keys) {
|
|
2379
|
+
return new ArcObject(Object.fromEntries(keys.map((key) => [key, this.rawShape[key]])));
|
|
2380
|
+
}
|
|
2381
|
+
omit(...keys) {
|
|
2382
|
+
return new ArcObject(Object.fromEntries(Object.entries(this.rawShape).filter(([key]) => !keys.includes(key))));
|
|
2383
|
+
}
|
|
2384
|
+
entries() {
|
|
2385
|
+
return Object.entries(this.rawShape);
|
|
2386
|
+
}
|
|
2387
|
+
keys() {
|
|
2388
|
+
return Object.keys(this.rawShape);
|
|
2389
|
+
}
|
|
2390
|
+
toJsonSchema() {
|
|
2391
|
+
const properties = {};
|
|
2392
|
+
const required = [];
|
|
2393
|
+
for (const [key, element] of Object.entries(this.rawShape)) {
|
|
2394
|
+
if (element instanceof ArcOptional) {
|
|
2395
|
+
properties[key] = element.parent.toJsonSchema?.() ?? {};
|
|
2396
|
+
} else {
|
|
2397
|
+
properties[key] = element.toJsonSchema?.() ?? {};
|
|
2398
|
+
required.push(key);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
const schema = {
|
|
2402
|
+
type: "object",
|
|
2403
|
+
properties
|
|
2404
|
+
};
|
|
2405
|
+
if (required.length)
|
|
2406
|
+
schema.required = required;
|
|
2407
|
+
if (this._description) {
|
|
2408
|
+
schema.description = this._description;
|
|
2409
|
+
}
|
|
2410
|
+
return schema;
|
|
2411
|
+
}
|
|
2412
|
+
getColumnData() {
|
|
2413
|
+
const storeData = this.getStoreData();
|
|
2414
|
+
return {
|
|
2415
|
+
type: "object",
|
|
2416
|
+
storeData: {
|
|
2417
|
+
...storeData,
|
|
2418
|
+
isNullable: false
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
2422
|
+
merge(otherShape) {
|
|
2423
|
+
const mergedShape = { ...this.rawShape, ...otherShape };
|
|
2424
|
+
return new ArcObject(mergedShape);
|
|
2425
|
+
}
|
|
2426
|
+
partial() {
|
|
2427
|
+
const partialShape = Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
2428
|
+
key,
|
|
2429
|
+
element instanceof ArcOptional ? element : new ArcOptional(element)
|
|
2430
|
+
]));
|
|
2431
|
+
return new ArcObject(partialShape);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
class ArcPrimitive extends ArcAbstract {
|
|
2435
|
+
serialize(value) {
|
|
2436
|
+
return value;
|
|
2437
|
+
}
|
|
2438
|
+
parse(value) {
|
|
2439
|
+
return value;
|
|
2440
|
+
}
|
|
2441
|
+
deserialize(value) {
|
|
2442
|
+
return value;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
class ArcBoolean extends ArcPrimitive {
|
|
2447
|
+
hasToBeTrue() {
|
|
2448
|
+
return this.validation("hasToBeTrue", (value) => {
|
|
2449
|
+
if (!value)
|
|
2450
|
+
return {
|
|
2451
|
+
current: value
|
|
2452
|
+
};
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
validation(name, validator) {
|
|
2456
|
+
const instance = this.pipeValidation(name, validator);
|
|
2457
|
+
return instance;
|
|
2458
|
+
}
|
|
2459
|
+
toJsonSchema() {
|
|
2460
|
+
const schema = { type: "boolean" };
|
|
2461
|
+
if (this._description) {
|
|
2462
|
+
schema.description = this._description;
|
|
2463
|
+
}
|
|
2464
|
+
return schema;
|
|
2465
|
+
}
|
|
2466
|
+
getColumnData() {
|
|
2467
|
+
const storeData = this.getStoreData();
|
|
2468
|
+
return {
|
|
2469
|
+
type: "boolean",
|
|
2470
|
+
storeData: {
|
|
2471
|
+
...storeData,
|
|
2472
|
+
isNullable: false
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
var stringValidator = typeValidatorBuilder("string");
|
|
2478
|
+
|
|
2479
|
+
class ArcString extends ArcPrimitive {
|
|
2480
|
+
constructor() {
|
|
2481
|
+
super([stringValidator]);
|
|
2482
|
+
}
|
|
2483
|
+
minLength(min) {
|
|
2484
|
+
return this.validation("minLength", (value) => {
|
|
2485
|
+
if (value.length < min)
|
|
2486
|
+
return {
|
|
2487
|
+
currentLength: value.length,
|
|
2488
|
+
minLength: min
|
|
2489
|
+
};
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
maxLength(max) {
|
|
2493
|
+
return this.validation("maxLength", (value) => {
|
|
2494
|
+
if (value.length > max)
|
|
2495
|
+
return {
|
|
2496
|
+
currentLength: value.length,
|
|
2497
|
+
maxLength: max
|
|
2498
|
+
};
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
length(number) {
|
|
2502
|
+
return this.validation("length", (value) => {
|
|
2503
|
+
if (value.length !== number) {
|
|
2504
|
+
return {
|
|
2505
|
+
currentLength: value.length,
|
|
2506
|
+
length: number
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
includes(str) {
|
|
2512
|
+
return this.validation("includes", (value) => {
|
|
2513
|
+
if (!value.includes(str)) {
|
|
2514
|
+
return {
|
|
2515
|
+
currentValue: value,
|
|
2516
|
+
includes: str
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
startsWith(str) {
|
|
2522
|
+
return this.validation("startsWith", (value) => {
|
|
2523
|
+
if (!value.startsWith(str)) {
|
|
2524
|
+
return {
|
|
2525
|
+
currentValue: value,
|
|
2526
|
+
startsWith: str
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
endsWith(str) {
|
|
2532
|
+
return this.validation("endsWith", (value) => {
|
|
2533
|
+
if (!value.endsWith(str)) {
|
|
2534
|
+
return {
|
|
2535
|
+
currentValue: value,
|
|
2536
|
+
endsWith: str
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2541
|
+
regex(regex) {
|
|
2542
|
+
return this.validation("regex", (value) => {
|
|
2543
|
+
if (!regex.test(value)) {
|
|
2544
|
+
return {
|
|
2545
|
+
currentValue: value,
|
|
2546
|
+
regex
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
email() {
|
|
2552
|
+
const regex = /^(?:(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+)*)|(?:"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f])*"))@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|(?:\[(?:\d{1,3}\.){3}\d{1,3}\])$/;
|
|
2553
|
+
return this.validation("email", (value) => {
|
|
2554
|
+
if (!regex.test(value)) {
|
|
2555
|
+
return {
|
|
2556
|
+
currentEmail: value
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
toJsonSchema() {
|
|
2562
|
+
const schema = { type: "string" };
|
|
2563
|
+
if (this._description) {
|
|
2564
|
+
schema.description = this._description;
|
|
2565
|
+
}
|
|
2566
|
+
return schema;
|
|
2567
|
+
}
|
|
2568
|
+
url() {
|
|
2569
|
+
const regex = /^(https?):\/\/(?![-0-9])(?!www\.[0-9])(?:[a-zA-Z0-9-]+(?::[a-zA-Z0-9-]+)?@)?(?:localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|[a-zA-Z0-9-]+\.)?[a-zA-Z0-9-]+\.(?:[a-zA-Z]{2,}|[a-zA-Z]{2}\.[a-zA-Z]{2})(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$|^(https?):\/\/(localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$/;
|
|
2570
|
+
return this.validation("url", (value) => {
|
|
2571
|
+
if (!regex.test(value)) {
|
|
2572
|
+
return {
|
|
2573
|
+
currentUrl: value
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
ip() {
|
|
2579
|
+
const IPv4regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
2580
|
+
const IPv6regex = /^(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:(:[0-9a-fA-F]{1,4}){1,7}|([0-9a-fA-F]{1,4}:){1,6}:([0-9a-fA-F]{1,4}:){1,6}[0-9a-fA-F]{1,4})$/;
|
|
2581
|
+
return this.validation("ip", (value) => {
|
|
2582
|
+
if (!(IPv4regex.test(value) || IPv6regex.test(value))) {
|
|
2583
|
+
return {
|
|
2584
|
+
currentIP: value
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
validation(name, validator) {
|
|
2590
|
+
const instance = this.pipeValidation(name, validator);
|
|
2591
|
+
return instance;
|
|
2592
|
+
}
|
|
2593
|
+
getColumnData() {
|
|
2594
|
+
const storeData = this.getStoreData();
|
|
2595
|
+
const validationInfo = {};
|
|
2596
|
+
for (const validation of this.validations) {
|
|
2597
|
+
switch (validation.name) {
|
|
2598
|
+
case "minLength":
|
|
2599
|
+
break;
|
|
2600
|
+
case "maxLength":
|
|
2601
|
+
break;
|
|
2602
|
+
case "regex":
|
|
2603
|
+
break;
|
|
2604
|
+
case "email":
|
|
2605
|
+
validationInfo.pattern = "email";
|
|
2606
|
+
break;
|
|
2607
|
+
case "url":
|
|
2608
|
+
validationInfo.pattern = "url";
|
|
2609
|
+
break;
|
|
2610
|
+
case "ip":
|
|
2611
|
+
validationInfo.pattern = "ip";
|
|
2612
|
+
break;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
return {
|
|
2616
|
+
type: "string",
|
|
2617
|
+
storeData: {
|
|
2618
|
+
...storeData,
|
|
2619
|
+
isNullable: false
|
|
2620
|
+
},
|
|
2621
|
+
validationInfo
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function string() {
|
|
2626
|
+
return new ArcString;
|
|
2627
|
+
}
|
|
2628
|
+
class ArcId extends ArcBranded {
|
|
2629
|
+
generateFn;
|
|
2630
|
+
constructor(name, generateFn) {
|
|
2631
|
+
super(string(), name);
|
|
2632
|
+
this.generateFn = generateFn;
|
|
2633
|
+
}
|
|
2634
|
+
generate() {
|
|
2635
|
+
if (this.generateFn) {
|
|
2636
|
+
return this.generateFn();
|
|
2637
|
+
}
|
|
2638
|
+
var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
|
|
2639
|
+
return timestamp + "xxxxxxxxxxxxxxxx".replace(/[x]/g, function() {
|
|
2640
|
+
return (Math.random() * 16 | 0).toString(16);
|
|
2641
|
+
}).toLowerCase();
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function id(name, generateFn) {
|
|
2645
|
+
return new ArcId(name, generateFn);
|
|
2646
|
+
}
|
|
2647
|
+
var numberValidator = typeValidatorBuilder("number");
|
|
2648
|
+
|
|
2649
|
+
class ArcNumber extends ArcPrimitive {
|
|
2650
|
+
constructor() {
|
|
2651
|
+
super([numberValidator]);
|
|
2652
|
+
}
|
|
2653
|
+
min(min) {
|
|
2654
|
+
return this.validation("min", (value) => {
|
|
2655
|
+
if (value < min)
|
|
2656
|
+
return { current: value, min };
|
|
2657
|
+
});
|
|
2658
|
+
}
|
|
2659
|
+
max(max) {
|
|
2660
|
+
return this.validation("max", (value) => {
|
|
2661
|
+
if (value > max)
|
|
2662
|
+
return { current: value, max };
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
validation(name, validator) {
|
|
2666
|
+
const instance = this.pipeValidation(name, validator);
|
|
2667
|
+
return instance;
|
|
2668
|
+
}
|
|
2669
|
+
toJsonSchema() {
|
|
2670
|
+
const schema = { type: "number" };
|
|
2671
|
+
if (this._description) {
|
|
2672
|
+
schema.description = this._description;
|
|
2673
|
+
}
|
|
2674
|
+
return schema;
|
|
2675
|
+
}
|
|
2676
|
+
getColumnData() {
|
|
2677
|
+
const storeData = this.getStoreData();
|
|
2678
|
+
const validationInfo = {};
|
|
2679
|
+
for (const validation of this.validations) {
|
|
2680
|
+
switch (validation.name) {
|
|
2681
|
+
case "min":
|
|
2682
|
+
break;
|
|
2683
|
+
case "max":
|
|
2684
|
+
break;
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
return {
|
|
2688
|
+
type: "number",
|
|
2689
|
+
storeData: {
|
|
2690
|
+
...storeData,
|
|
2691
|
+
isNullable: false
|
|
2692
|
+
},
|
|
2693
|
+
validationInfo
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
class ArcFragmentBase {
|
|
2698
|
+
is(type) {
|
|
2699
|
+
return this.types.includes(type);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
class ArcContextElement extends ArcFragmentBase {
|
|
2704
|
+
name;
|
|
2705
|
+
types = ["context-element"];
|
|
2706
|
+
get id() {
|
|
2707
|
+
return this.name;
|
|
2708
|
+
}
|
|
2709
|
+
constructor(name) {
|
|
2710
|
+
super();
|
|
2711
|
+
this.name = name;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
class ArcEvent extends ArcContextElement {
|
|
2716
|
+
data;
|
|
2717
|
+
eventId = id("event");
|
|
2718
|
+
constructor(data) {
|
|
2719
|
+
super(data.name);
|
|
2720
|
+
this.data = data;
|
|
2721
|
+
}
|
|
2722
|
+
get payload() {
|
|
2723
|
+
return this.data.payload;
|
|
2724
|
+
}
|
|
2725
|
+
description(description) {
|
|
2726
|
+
return new ArcEvent({
|
|
2727
|
+
...this.data,
|
|
2728
|
+
description
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
mutateContext(adapters) {
|
|
2732
|
+
return {
|
|
2733
|
+
emit: async (payload) => {
|
|
2734
|
+
if (!adapters.eventPublisher) {
|
|
2735
|
+
throw new Error(`Event "${this.data.name}" cannot be emitted: no eventPublisher adapter available`);
|
|
2736
|
+
}
|
|
2737
|
+
const event = {
|
|
2738
|
+
type: this.data.name,
|
|
2739
|
+
payload,
|
|
2740
|
+
id: this.eventId.generate(),
|
|
2741
|
+
createdAt: new Date
|
|
2742
|
+
};
|
|
2743
|
+
await adapters.eventPublisher.publish(event);
|
|
2744
|
+
}
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
protectBy(token, protectionFn) {
|
|
2748
|
+
const existingProtections = this.data.protections || [];
|
|
2749
|
+
return new ArcEvent({
|
|
2750
|
+
...this.data,
|
|
2751
|
+
protections: [...existingProtections, { token, protectionFn }]
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
get hasProtections() {
|
|
2755
|
+
return (this.data.protections?.length ?? 0) > 0;
|
|
2756
|
+
}
|
|
2757
|
+
get protections() {
|
|
2758
|
+
return this.data.protections || [];
|
|
2759
|
+
}
|
|
2760
|
+
getProtectionFor(tokenInstance) {
|
|
2761
|
+
if (!this.data.protections) {
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
for (const protection of this.data.protections) {
|
|
2765
|
+
if (tokenInstance.getTokenDefinition() === protection.token) {
|
|
2766
|
+
return protection.protectionFn(tokenInstance.params);
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
return null;
|
|
2770
|
+
}
|
|
2771
|
+
tags(tagFields) {
|
|
2772
|
+
return new ArcEvent({
|
|
2773
|
+
...this.data,
|
|
2774
|
+
tagFields
|
|
2775
|
+
});
|
|
2776
|
+
}
|
|
2777
|
+
get tagFields() {
|
|
2778
|
+
return this.data.tagFields || [];
|
|
2779
|
+
}
|
|
2780
|
+
static _sharedSchema = null;
|
|
2781
|
+
static sharedDatabaseStoreSchema() {
|
|
2782
|
+
if (ArcEvent._sharedSchema) {
|
|
2783
|
+
return ArcEvent._sharedSchema;
|
|
2784
|
+
}
|
|
2785
|
+
const eventsSchema = new ArcObject({
|
|
2786
|
+
_id: new ArcString().primaryKey(),
|
|
2787
|
+
type: new ArcString().index(),
|
|
2788
|
+
payload: new ArcString,
|
|
2789
|
+
createdAt: new ArcString().index()
|
|
2790
|
+
});
|
|
2791
|
+
const tagsSchema = new ArcObject({
|
|
2792
|
+
_id: new ArcString().primaryKey(),
|
|
2793
|
+
eventId: new ArcString().index(),
|
|
2794
|
+
tagKey: new ArcString().index(),
|
|
2795
|
+
tagValue: new ArcString().index()
|
|
2796
|
+
});
|
|
2797
|
+
const syncStatusSchema = new ArcObject({
|
|
2798
|
+
_id: new ArcString().primaryKey(),
|
|
2799
|
+
eventId: new ArcString().index(),
|
|
2800
|
+
synced: new ArcBoolean().index(),
|
|
2801
|
+
timestamp: new ArcNumber,
|
|
2802
|
+
retryCount: new ArcNumber
|
|
2803
|
+
});
|
|
2804
|
+
ArcEvent._sharedSchema = {
|
|
2805
|
+
tables: [
|
|
2806
|
+
{ name: "events", schema: eventsSchema },
|
|
2807
|
+
{ name: "event_tags", schema: tagsSchema },
|
|
2808
|
+
{ name: "event_sync_status", schema: syncStatusSchema }
|
|
2809
|
+
]
|
|
2810
|
+
};
|
|
2811
|
+
return ArcEvent._sharedSchema;
|
|
2812
|
+
}
|
|
2813
|
+
databaseStoreSchema() {
|
|
2814
|
+
return ArcEvent.sharedDatabaseStoreSchema();
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
class AggregateBase {
|
|
2818
|
+
value;
|
|
2819
|
+
_id;
|
|
2820
|
+
#adapters;
|
|
2821
|
+
constructor(row, adapters) {
|
|
2822
|
+
this._id = row._id;
|
|
2823
|
+
const { _id, ...rest } = row;
|
|
2824
|
+
this.value = rest;
|
|
2825
|
+
this.#adapters = adapters;
|
|
2826
|
+
}
|
|
2827
|
+
toJSON() {
|
|
2828
|
+
return { _id: this._id, ...this.value };
|
|
2829
|
+
}
|
|
2830
|
+
get ctx() {
|
|
2831
|
+
const events = this.constructor.__aggregateEvents ?? [];
|
|
2832
|
+
const adapters = this.#adapters;
|
|
2833
|
+
const result = {};
|
|
2834
|
+
for (const entry of events) {
|
|
2835
|
+
const arcEvent = entry.event;
|
|
2836
|
+
result[arcEvent.name] = {
|
|
2837
|
+
emit: async (payload) => {
|
|
2838
|
+
if (!adapters.eventPublisher) {
|
|
2839
|
+
throw new Error(`Cannot emit event "${arcEvent.name}": no eventPublisher adapter available`);
|
|
2840
|
+
}
|
|
2841
|
+
const eventInstance = {
|
|
2842
|
+
type: arcEvent.name,
|
|
2843
|
+
payload,
|
|
2844
|
+
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
2845
|
+
createdAt: new Date
|
|
2846
|
+
};
|
|
2847
|
+
await adapters.eventPublisher.publish(eventInstance);
|
|
2848
|
+
}
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
return result;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
class DataStorage {
|
|
2855
|
+
async commitChanges(changes) {
|
|
2856
|
+
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
class StoreState {
|
|
2861
|
+
storeName;
|
|
2862
|
+
dataStorage;
|
|
2863
|
+
deserialize;
|
|
2864
|
+
listeners = new Map;
|
|
2865
|
+
constructor(storeName, dataStorage, deserialize) {
|
|
2866
|
+
this.storeName = storeName;
|
|
2867
|
+
this.dataStorage = dataStorage;
|
|
2868
|
+
this.deserialize = deserialize;
|
|
2869
|
+
}
|
|
2870
|
+
fork() {
|
|
2871
|
+
return new ForkedStoreState(this.storeName, this.dataStorage, this);
|
|
2872
|
+
}
|
|
2873
|
+
async set(item) {
|
|
2874
|
+
const change = {
|
|
2875
|
+
type: "set",
|
|
2876
|
+
data: item
|
|
2877
|
+
};
|
|
2878
|
+
await this.applyChanges([change]);
|
|
2879
|
+
}
|
|
2880
|
+
async remove(id2) {
|
|
2881
|
+
const change = {
|
|
2882
|
+
type: "delete",
|
|
2883
|
+
id: id2
|
|
2884
|
+
};
|
|
2885
|
+
await this.applyChanges([change]);
|
|
2886
|
+
}
|
|
2887
|
+
async modify(id2, data) {
|
|
2888
|
+
const change = {
|
|
2889
|
+
type: "modify",
|
|
2890
|
+
id: id2,
|
|
2891
|
+
data
|
|
2892
|
+
};
|
|
2893
|
+
const { from, to } = await this.applyChange(change);
|
|
2894
|
+
return { from, to };
|
|
2895
|
+
}
|
|
2896
|
+
async mutate(id2, editCallback) {
|
|
2897
|
+
const result = await this.find({ where: { _id: id2 } });
|
|
2898
|
+
const object2 = result[0];
|
|
2899
|
+
const [draft, finalize] = create(object2 || {}, { enablePatches: true });
|
|
2900
|
+
await editCallback(draft);
|
|
2901
|
+
const [_, patches] = finalize();
|
|
2902
|
+
const change = {
|
|
2903
|
+
type: "mutate",
|
|
2904
|
+
id: id2,
|
|
2905
|
+
patches
|
|
2906
|
+
};
|
|
2907
|
+
const { from, to } = await this.applyChange(change);
|
|
2908
|
+
return { from, to };
|
|
2909
|
+
}
|
|
2910
|
+
applySerializedChanges(changes) {
|
|
2911
|
+
return Promise.all(changes.map((change) => this.applyChange(change)));
|
|
2912
|
+
}
|
|
2913
|
+
get listenerCount() {
|
|
2914
|
+
return this.listeners.size;
|
|
2915
|
+
}
|
|
2916
|
+
unsubscribe(listener4) {
|
|
2917
|
+
this.listeners.delete(listener4);
|
|
2918
|
+
}
|
|
2919
|
+
notifyListeners(events) {
|
|
2920
|
+
for (const listener4 of this.listeners.values()) {
|
|
2921
|
+
listener4.callback(events);
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
function deepMerge(target, source) {
|
|
2926
|
+
const output = { ...target };
|
|
2927
|
+
for (const key in source) {
|
|
2928
|
+
if (source[key] === undefined) {
|
|
2929
|
+
output[key] = undefined;
|
|
2930
|
+
continue;
|
|
2931
|
+
}
|
|
2932
|
+
if (isPlainObject(source[key]) && isPlainObject(target[key])) {
|
|
2933
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
2934
|
+
} else {
|
|
2935
|
+
output[key] = source[key];
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return output;
|
|
2939
|
+
}
|
|
2940
|
+
function isPlainObject(item) {
|
|
2941
|
+
return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
|
|
2942
|
+
}
|
|
2943
|
+
class ForkedStoreState extends StoreState {
|
|
2944
|
+
master;
|
|
2945
|
+
changedItems = new Map;
|
|
2946
|
+
changes = [];
|
|
2947
|
+
constructor(storeName, dataStorage, master) {
|
|
2948
|
+
super(storeName, dataStorage, master.deserialize);
|
|
2949
|
+
this.master = master;
|
|
2950
|
+
}
|
|
2951
|
+
async applyChangeAndReturnEvent(change) {
|
|
2952
|
+
this.changes.push(change);
|
|
2953
|
+
if (change.type === "set") {
|
|
2954
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
2955
|
+
this.changedItems.set(change.data._id, item);
|
|
2956
|
+
return {
|
|
2957
|
+
from: null,
|
|
2958
|
+
to: item,
|
|
2959
|
+
event: {
|
|
2960
|
+
type: "set",
|
|
2961
|
+
item: change.data,
|
|
2962
|
+
id: change.data._id
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
if (change.type === "delete") {
|
|
2967
|
+
const from = await this.find({ where: { _id: change.id } }).then((results) => results[0] || null);
|
|
2968
|
+
this.changedItems.set(change.id, null);
|
|
2969
|
+
return {
|
|
2970
|
+
from,
|
|
2971
|
+
to: null,
|
|
2972
|
+
event: {
|
|
2973
|
+
type: "delete",
|
|
2974
|
+
item: null,
|
|
2975
|
+
id: change.id
|
|
2976
|
+
}
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
if (change.type === "modify") {
|
|
2980
|
+
const existing = await this.find({
|
|
2981
|
+
where: { _id: change.id }
|
|
2982
|
+
}).then((results) => results[0]);
|
|
2983
|
+
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
2984
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
2985
|
+
this.changedItems.set(change.id, item);
|
|
2986
|
+
return {
|
|
2987
|
+
from: existing || null,
|
|
2988
|
+
to: item,
|
|
2989
|
+
event: {
|
|
2990
|
+
type: "set",
|
|
2991
|
+
item,
|
|
2992
|
+
id: change.id
|
|
2993
|
+
}
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
if (change.type === "mutate") {
|
|
2997
|
+
const existing = await this.find({
|
|
2998
|
+
where: { _id: change.id }
|
|
2999
|
+
}).then((results) => results[0]);
|
|
3000
|
+
const updated = apply(existing || {}, change.patches);
|
|
3001
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
3002
|
+
this.changedItems.set(change.id, item);
|
|
3003
|
+
return {
|
|
3004
|
+
from: existing || null,
|
|
3005
|
+
to: item,
|
|
3006
|
+
event: {
|
|
3007
|
+
type: "set",
|
|
3008
|
+
item,
|
|
3009
|
+
id: change.id
|
|
3010
|
+
}
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
throw new Error("Unknown change type");
|
|
3014
|
+
}
|
|
3015
|
+
async applyChange(change) {
|
|
3016
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
3017
|
+
this.notifyListeners([event3]);
|
|
3018
|
+
return { from, to };
|
|
3019
|
+
}
|
|
3020
|
+
async applyChanges(changes) {
|
|
3021
|
+
const events = [];
|
|
3022
|
+
for (const change of changes) {
|
|
3023
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(change);
|
|
3024
|
+
if (event3)
|
|
3025
|
+
events.push(event3);
|
|
3026
|
+
}
|
|
3027
|
+
if (events.length > 0) {
|
|
3028
|
+
this.notifyListeners(events);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
async find(options, listener4) {
|
|
3032
|
+
if (listener4) {
|
|
3033
|
+
this.listeners.set(listener4, { callback: listener4 });
|
|
3034
|
+
}
|
|
3035
|
+
const parentResults = await this.master.find(options);
|
|
3036
|
+
const results = new Map;
|
|
3037
|
+
parentResults.forEach((item) => results.set(item._id, item));
|
|
3038
|
+
for (const [id2, changedItem] of this.changedItems) {
|
|
3039
|
+
if (changedItem === null) {
|
|
3040
|
+
results.delete(id2);
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
const matches = !options.where || Object.entries(options.where).every(([key, value]) => changedItem[key] === value);
|
|
3044
|
+
if (matches) {
|
|
3045
|
+
results.set(id2, changedItem);
|
|
3046
|
+
} else {
|
|
3047
|
+
results.delete(id2);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
return Array.from(results.values());
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
class ForkedDataStorage extends DataStorage {
|
|
3055
|
+
master;
|
|
3056
|
+
stores = new Map;
|
|
3057
|
+
constructor(master) {
|
|
3058
|
+
super();
|
|
3059
|
+
this.master = master;
|
|
3060
|
+
}
|
|
3061
|
+
getReadTransaction() {
|
|
3062
|
+
return this.master.getReadTransaction();
|
|
3063
|
+
}
|
|
3064
|
+
getReadWriteTransaction() {
|
|
3065
|
+
return this.master.getReadWriteTransaction();
|
|
3066
|
+
}
|
|
3067
|
+
getStore(storeName) {
|
|
3068
|
+
if (this.stores.has(storeName))
|
|
3069
|
+
return this.stores.get(storeName);
|
|
3070
|
+
const masterStorage = this.master.getStore(storeName);
|
|
3071
|
+
const storage = new ForkedStoreState(storeName, this, masterStorage);
|
|
3072
|
+
this.stores.set(storeName, storage);
|
|
3073
|
+
return storage;
|
|
3074
|
+
}
|
|
3075
|
+
fork() {
|
|
3076
|
+
return new ForkedDataStorage(this);
|
|
3077
|
+
}
|
|
3078
|
+
async merge() {
|
|
3079
|
+
const changes = Array.from(this.stores.values()).filter((store) => store.changes.length > 0).map((store) => ({
|
|
3080
|
+
store: store.storeName,
|
|
3081
|
+
changes: store.changes
|
|
3082
|
+
}));
|
|
3083
|
+
await this.master.commitChanges(changes);
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
class MasterStoreState extends StoreState {
|
|
3088
|
+
constructor(storeName, dataStorage, deserialize) {
|
|
3089
|
+
super(storeName, dataStorage, deserialize);
|
|
3090
|
+
}
|
|
3091
|
+
async applyChangeAndReturnEvent(transaction, change, transactionCache) {
|
|
3092
|
+
if (change.type === "set") {
|
|
3093
|
+
await transaction.set(this.storeName, change.data);
|
|
3094
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
3095
|
+
if (transactionCache) {
|
|
3096
|
+
transactionCache.set(change.data._id, item);
|
|
3097
|
+
}
|
|
3098
|
+
return {
|
|
3099
|
+
from: null,
|
|
3100
|
+
to: item,
|
|
3101
|
+
event: {
|
|
3102
|
+
type: "set",
|
|
3103
|
+
item: change.data,
|
|
3104
|
+
id: change.data._id
|
|
3105
|
+
}
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
if (change.type === "delete") {
|
|
3109
|
+
await transaction.remove(this.storeName, change.id);
|
|
3110
|
+
return {
|
|
3111
|
+
from: null,
|
|
3112
|
+
to: null,
|
|
3113
|
+
event: {
|
|
3114
|
+
type: "delete",
|
|
3115
|
+
item: null,
|
|
3116
|
+
id: change.id
|
|
3117
|
+
}
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
if (change.type === "modify") {
|
|
3121
|
+
let existing;
|
|
3122
|
+
if (transactionCache && transactionCache.has(change.id)) {
|
|
3123
|
+
existing = transactionCache.get(change.id);
|
|
3124
|
+
} else {
|
|
3125
|
+
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
3126
|
+
}
|
|
3127
|
+
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
3128
|
+
await transaction.set(this.storeName, updated);
|
|
3129
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
3130
|
+
if (transactionCache) {
|
|
3131
|
+
transactionCache.set(change.id, item);
|
|
3132
|
+
}
|
|
3133
|
+
return {
|
|
3134
|
+
from: null,
|
|
3135
|
+
to: item,
|
|
3136
|
+
event: {
|
|
3137
|
+
type: "set",
|
|
3138
|
+
item,
|
|
3139
|
+
id: change.id
|
|
3140
|
+
}
|
|
3141
|
+
};
|
|
3142
|
+
}
|
|
3143
|
+
if (change.type === "mutate") {
|
|
3144
|
+
let existing;
|
|
3145
|
+
if (transactionCache && transactionCache.has(change.id)) {
|
|
3146
|
+
existing = transactionCache.get(change.id);
|
|
3147
|
+
} else {
|
|
3148
|
+
existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
3149
|
+
}
|
|
3150
|
+
const updated = apply(existing || {}, change.patches);
|
|
3151
|
+
await transaction.set(this.storeName, updated);
|
|
3152
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
3153
|
+
if (transactionCache) {
|
|
3154
|
+
transactionCache.set(change.id, item);
|
|
3155
|
+
}
|
|
3156
|
+
return {
|
|
3157
|
+
from: null,
|
|
3158
|
+
to: item,
|
|
3159
|
+
event: {
|
|
3160
|
+
type: "set",
|
|
3161
|
+
item,
|
|
3162
|
+
id: change.id
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
throw new Error("Unknown change type");
|
|
3167
|
+
}
|
|
3168
|
+
async applyChange(change) {
|
|
3169
|
+
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
3170
|
+
const transactionCache = new Map;
|
|
3171
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
3172
|
+
await transaction.commit();
|
|
3173
|
+
this.notifyListeners([event3]);
|
|
3174
|
+
return { from, to };
|
|
3175
|
+
}
|
|
3176
|
+
async applyChanges(changes) {
|
|
3177
|
+
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
3178
|
+
const transactionCache = new Map;
|
|
3179
|
+
const events = [];
|
|
3180
|
+
for (const change of changes) {
|
|
3181
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
3182
|
+
if (event3)
|
|
3183
|
+
events.push(event3);
|
|
3184
|
+
}
|
|
3185
|
+
await transaction.commit();
|
|
3186
|
+
if (events.length > 0) {
|
|
3187
|
+
this.notifyListeners(events);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
async find(options, listener4) {
|
|
3191
|
+
if (listener4) {
|
|
3192
|
+
this.listeners.set(listener4, { callback: listener4 });
|
|
3193
|
+
}
|
|
3194
|
+
const transaction = await this.dataStorage.getReadTransaction();
|
|
3195
|
+
const results = await transaction.find(this.storeName, options);
|
|
3196
|
+
return results.map((item) => this.deserialize ? this.deserialize(item) : item);
|
|
3197
|
+
}
|
|
3198
|
+
notifyListenersPublic(events) {
|
|
3199
|
+
this.notifyListeners(events);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
class MasterDataStorage extends DataStorage {
|
|
3204
|
+
dbAdapter;
|
|
3205
|
+
stores = new Map;
|
|
3206
|
+
reinitTablesExecuted = false;
|
|
3207
|
+
constructor(dbAdapter) {
|
|
3208
|
+
super();
|
|
3209
|
+
this.dbAdapter = dbAdapter;
|
|
3210
|
+
this.executeReinitTablesOnReady();
|
|
3211
|
+
}
|
|
3212
|
+
async executeReinitTablesOnReady() {
|
|
3213
|
+
try {
|
|
3214
|
+
const adapter = await this.dbAdapter;
|
|
3215
|
+
if (!this.reinitTablesExecuted) {
|
|
3216
|
+
await adapter.executeReinitTables(this);
|
|
3217
|
+
this.reinitTablesExecuted = true;
|
|
3218
|
+
}
|
|
3219
|
+
} catch (error) {
|
|
3220
|
+
console.error("Failed to execute reinitTable functions:", error);
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
async getReadTransaction() {
|
|
3224
|
+
return (await this.dbAdapter).readTransaction();
|
|
3225
|
+
}
|
|
3226
|
+
async getReadWriteTransaction() {
|
|
3227
|
+
return (await this.dbAdapter).readWriteTransaction();
|
|
3228
|
+
}
|
|
3229
|
+
getStore(storeName) {
|
|
3230
|
+
if (!this.stores.has(storeName)) {
|
|
3231
|
+
this.stores.set(storeName, new MasterStoreState(storeName, this));
|
|
3232
|
+
}
|
|
3233
|
+
return this.stores.get(storeName);
|
|
3234
|
+
}
|
|
3235
|
+
async applyChanges(changes) {
|
|
3236
|
+
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
3237
|
+
}
|
|
3238
|
+
applySerializedChanges(changes) {
|
|
3239
|
+
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
3240
|
+
}
|
|
3241
|
+
async commitChanges(changes) {
|
|
3242
|
+
const transaction = await this.getReadWriteTransaction();
|
|
3243
|
+
const transactionCache = new Map;
|
|
3244
|
+
const eventsByStore = new Map;
|
|
3245
|
+
for (const { store, changes: storeChanges } of changes) {
|
|
3246
|
+
const storeState = this.getStore(store);
|
|
3247
|
+
const storeEvents = [];
|
|
3248
|
+
for (const change of storeChanges) {
|
|
3249
|
+
const { event: event3 } = await storeState.applyChangeAndReturnEvent(transaction, change, transactionCache);
|
|
3250
|
+
if (event3)
|
|
3251
|
+
storeEvents.push(event3);
|
|
3252
|
+
}
|
|
3253
|
+
if (storeEvents.length > 0) {
|
|
3254
|
+
eventsByStore.set(store, storeEvents);
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
await transaction.commit();
|
|
3258
|
+
for (const [store, events] of eventsByStore) {
|
|
3259
|
+
const storeState = this.getStore(store);
|
|
3260
|
+
storeState.notifyListenersPublic(events);
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
fork() {
|
|
3264
|
+
return new ForkedDataStorage(this);
|
|
3265
|
+
}
|
|
3266
|
+
async destroy() {
|
|
3267
|
+
const adapter = await this.dbAdapter;
|
|
3268
|
+
if (adapter.destroy) {
|
|
3269
|
+
await adapter.destroy();
|
|
3270
|
+
}
|
|
3271
|
+
this.stores.clear();
|
|
3272
|
+
this.reinitTablesExecuted = false;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
function resolveQueryChange(currentResult, event3, options) {
|
|
3276
|
+
const index = currentResult.findIndex((e) => e._id === event3.id);
|
|
3277
|
+
const isInCurrentResult = index !== -1;
|
|
3278
|
+
if (event3.type === "delete") {
|
|
3279
|
+
if (isInCurrentResult) {
|
|
3280
|
+
return currentResult.toSpliced(index, 1);
|
|
3281
|
+
}
|
|
3282
|
+
return false;
|
|
3283
|
+
}
|
|
3284
|
+
const shouldBeInResult = checkItemMatchesWhere(event3.item, options.where);
|
|
3285
|
+
if (isInCurrentResult && shouldBeInResult) {
|
|
3286
|
+
const newResult = currentResult.toSpliced(index, 1, event3.item);
|
|
3287
|
+
return applyOrderByAndLimit(newResult, options);
|
|
3288
|
+
} else if (isInCurrentResult && !shouldBeInResult) {
|
|
3289
|
+
return currentResult.toSpliced(index, 1);
|
|
3290
|
+
} else if (!isInCurrentResult && shouldBeInResult) {
|
|
3291
|
+
const newResult = [...currentResult, event3.item];
|
|
3292
|
+
return applyOrderByAndLimit(newResult, options);
|
|
3293
|
+
}
|
|
3294
|
+
return false;
|
|
3295
|
+
}
|
|
3296
|
+
function checkItemMatchesWhere(item, where) {
|
|
3297
|
+
if (!where) {
|
|
3298
|
+
return true;
|
|
3299
|
+
}
|
|
3300
|
+
return Object.entries(where).every(([key, condition]) => {
|
|
3301
|
+
const value = item[key];
|
|
3302
|
+
if (typeof condition !== "object" || condition === null) {
|
|
3303
|
+
return value === condition;
|
|
3304
|
+
}
|
|
3305
|
+
return Object.entries(condition).every(([operator, operand]) => {
|
|
3306
|
+
switch (operator) {
|
|
3307
|
+
case "$eq":
|
|
3308
|
+
return value === operand;
|
|
3309
|
+
case "$ne":
|
|
3310
|
+
return value !== operand;
|
|
3311
|
+
case "$gt":
|
|
3312
|
+
return typeof value === "number" && typeof operand === "number" && value > operand;
|
|
3313
|
+
case "$gte":
|
|
3314
|
+
return typeof value === "number" && typeof operand === "number" && value >= operand;
|
|
3315
|
+
case "$lt":
|
|
3316
|
+
return typeof value === "number" && typeof operand === "number" && value < operand;
|
|
3317
|
+
case "$lte":
|
|
3318
|
+
return typeof value === "number" && typeof operand === "number" && value <= operand;
|
|
3319
|
+
case "$in":
|
|
3320
|
+
return Array.isArray(operand) && operand.includes(value);
|
|
3321
|
+
case "$nin":
|
|
3322
|
+
return Array.isArray(operand) && !operand.includes(value);
|
|
3323
|
+
case "$exists":
|
|
3324
|
+
return typeof operand === "boolean" ? operand ? value !== undefined : value === undefined : true;
|
|
3325
|
+
default:
|
|
3326
|
+
return true;
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
function applyOrderByAndLimit(result, options) {
|
|
3332
|
+
let sorted = result;
|
|
3333
|
+
if (options.orderBy) {
|
|
3334
|
+
sorted = [...result].sort((a, b) => {
|
|
3335
|
+
for (const [key, direction] of Object.entries(options.orderBy)) {
|
|
3336
|
+
const aVal = a[key];
|
|
3337
|
+
const bVal = b[key];
|
|
3338
|
+
if (aVal < bVal)
|
|
3339
|
+
return direction === "asc" ? -1 : 1;
|
|
3340
|
+
if (aVal > bVal)
|
|
3341
|
+
return direction === "asc" ? 1 : -1;
|
|
3342
|
+
}
|
|
3343
|
+
return 0;
|
|
3344
|
+
});
|
|
3345
|
+
}
|
|
3346
|
+
if (options.limit !== undefined) {
|
|
3347
|
+
sorted = sorted.slice(0, options.limit);
|
|
3348
|
+
}
|
|
3349
|
+
return sorted;
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
class ObservableDataStorage {
|
|
3353
|
+
source;
|
|
3354
|
+
onChange;
|
|
3355
|
+
trackedQueries = new Map;
|
|
3356
|
+
stores = new Map;
|
|
3357
|
+
constructor(source, onChange) {
|
|
3358
|
+
this.source = source;
|
|
3359
|
+
this.onChange = onChange;
|
|
3360
|
+
}
|
|
3361
|
+
getStore(storeName) {
|
|
3362
|
+
if (!this.stores.has(storeName)) {
|
|
3363
|
+
this.stores.set(storeName, new ObservableStoreState(storeName, this.source.getStore(storeName), this));
|
|
3364
|
+
}
|
|
3365
|
+
return this.stores.get(storeName);
|
|
3366
|
+
}
|
|
3367
|
+
fork() {
|
|
3368
|
+
return this.source.fork();
|
|
3369
|
+
}
|
|
3370
|
+
getReadTransaction() {
|
|
3371
|
+
return this.source.getReadTransaction();
|
|
3372
|
+
}
|
|
3373
|
+
getReadWriteTransaction() {
|
|
3374
|
+
return this.source.getReadWriteTransaction();
|
|
3375
|
+
}
|
|
3376
|
+
commitChanges(changes) {
|
|
3377
|
+
return this.source.commitChanges(changes);
|
|
3378
|
+
}
|
|
3379
|
+
trackQuery(storeName, options, result, listener4) {
|
|
3380
|
+
const key = this.getQueryKey(storeName, options);
|
|
3381
|
+
this.trackedQueries.set(key, {
|
|
3382
|
+
storeName,
|
|
3383
|
+
options,
|
|
3384
|
+
result,
|
|
3385
|
+
listener: listener4
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
handleStoreChange(storeName, events) {
|
|
3389
|
+
let hasChanges = false;
|
|
3390
|
+
for (const query of this.trackedQueries.values()) {
|
|
3391
|
+
if (query.storeName !== storeName)
|
|
3392
|
+
continue;
|
|
3393
|
+
let currentResult = query.result;
|
|
3394
|
+
let queryChanged = false;
|
|
3395
|
+
for (const event3 of events) {
|
|
3396
|
+
const newResult = resolveQueryChange(currentResult, event3, query.options);
|
|
3397
|
+
if (newResult !== false) {
|
|
3398
|
+
currentResult = newResult;
|
|
3399
|
+
queryChanged = true;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
if (queryChanged) {
|
|
3403
|
+
query.result = currentResult;
|
|
3404
|
+
hasChanges = true;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
if (hasChanges) {
|
|
3408
|
+
this.onChange();
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
getCachedResult(storeName, options) {
|
|
3412
|
+
const key = this.getQueryKey(storeName, options);
|
|
3413
|
+
return this.trackedQueries.get(key)?.result;
|
|
3414
|
+
}
|
|
3415
|
+
clear() {
|
|
3416
|
+
for (const query of this.trackedQueries.values()) {
|
|
3417
|
+
const sourceStore = this.source.getStore(query.storeName);
|
|
3418
|
+
sourceStore.unsubscribe(query.listener);
|
|
3419
|
+
}
|
|
3420
|
+
this.trackedQueries.clear();
|
|
3421
|
+
this.stores.clear();
|
|
3422
|
+
}
|
|
3423
|
+
getQueryKey(storeName, options) {
|
|
3424
|
+
return `${storeName}:${JSON.stringify(options || {})}`;
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
class ObservableStoreState {
|
|
3429
|
+
source;
|
|
3430
|
+
observable;
|
|
3431
|
+
storeName;
|
|
3432
|
+
constructor(storeName, source, observable) {
|
|
3433
|
+
this.source = source;
|
|
3434
|
+
this.observable = observable;
|
|
3435
|
+
this.storeName = storeName;
|
|
3436
|
+
}
|
|
3437
|
+
async find(options = {}, _existingListener) {
|
|
3438
|
+
const cached = this.observable.getCachedResult(this.storeName, options);
|
|
3439
|
+
if (cached !== undefined) {
|
|
3440
|
+
return cached;
|
|
3441
|
+
}
|
|
3442
|
+
const listener4 = (events) => {
|
|
3443
|
+
this.observable.handleStoreChange(this.storeName, events);
|
|
3444
|
+
};
|
|
3445
|
+
const result = await this.source.find(options, listener4);
|
|
3446
|
+
this.observable.trackQuery(this.storeName, options, result, listener4);
|
|
3447
|
+
return result;
|
|
3448
|
+
}
|
|
3449
|
+
unsubscribe(listener4) {
|
|
3450
|
+
this.source.unsubscribe(listener4);
|
|
3451
|
+
}
|
|
3452
|
+
async set(item) {
|
|
3453
|
+
return this.source.set(item);
|
|
3454
|
+
}
|
|
3455
|
+
async remove(id2) {
|
|
3456
|
+
return this.source.remove(id2);
|
|
3457
|
+
}
|
|
3458
|
+
async modify(id2, data) {
|
|
3459
|
+
return this.source.modify(id2, data);
|
|
3460
|
+
}
|
|
3461
|
+
async mutate(id2, editCallback) {
|
|
3462
|
+
return this.source.mutate(id2, editCallback);
|
|
3463
|
+
}
|
|
3464
|
+
fork() {
|
|
3465
|
+
return this.source.fork();
|
|
3466
|
+
}
|
|
3467
|
+
applyChange(change) {
|
|
3468
|
+
return this.source.applyChange(change);
|
|
3469
|
+
}
|
|
3470
|
+
applyChanges(changes) {
|
|
3471
|
+
return this.source.applyChanges(changes);
|
|
3472
|
+
}
|
|
3473
|
+
applySerializedChanges(changes) {
|
|
3474
|
+
return this.source.applySerializedChanges(changes);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
function extractDatabaseAgnosticSchema(arcObject, tableName) {
|
|
3478
|
+
const columns = [];
|
|
3479
|
+
for (const [fieldName, fieldSchema] of arcObject.entries()) {
|
|
3480
|
+
const arcElement = fieldSchema;
|
|
3481
|
+
if (typeof arcElement.getColumnData !== "function") {
|
|
3482
|
+
throw new Error(`Element for field '${fieldName}' does not implement getColumnData() method. Element type: ${arcElement.constructor.name}`);
|
|
3483
|
+
}
|
|
3484
|
+
const columnInfo = arcElement.getColumnData();
|
|
3485
|
+
const fullColumnInfo = {
|
|
3486
|
+
name: fieldName,
|
|
3487
|
+
...columnInfo
|
|
3488
|
+
};
|
|
3489
|
+
columns.push(fullColumnInfo);
|
|
3490
|
+
}
|
|
3491
|
+
return {
|
|
3492
|
+
tableName,
|
|
3493
|
+
columns
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
var dateValidator = typeValidatorBuilder("Date", (value) => value instanceof Date);
|
|
3497
|
+
function buildContextAccessor(context2, adapters, contextMethod, onCall) {
|
|
3498
|
+
const result = {};
|
|
3499
|
+
for (const element2 of context2.elements) {
|
|
3500
|
+
const ctxFn = element2[contextMethod];
|
|
3501
|
+
if (typeof ctxFn !== "function")
|
|
3502
|
+
continue;
|
|
3503
|
+
const methods = ctxFn.call(element2, adapters);
|
|
3504
|
+
if (!methods || typeof methods !== "object")
|
|
3505
|
+
continue;
|
|
3506
|
+
const wrapped = {};
|
|
3507
|
+
for (const [methodName, method] of Object.entries(methods)) {
|
|
3508
|
+
if (typeof method !== "function")
|
|
3509
|
+
continue;
|
|
3510
|
+
wrapped[methodName] = (...args) => onCall({ element: element2.name, method: methodName, args }, () => method(...args));
|
|
3511
|
+
}
|
|
3512
|
+
result[element2.name] = wrapped;
|
|
3513
|
+
}
|
|
3514
|
+
return result;
|
|
3515
|
+
}
|
|
3516
|
+
function executeDescriptor(descriptor, context2, adapters, contextMethod) {
|
|
3517
|
+
const element2 = context2.get(descriptor.element);
|
|
3518
|
+
if (!element2) {
|
|
3519
|
+
throw new Error(`Element '${descriptor.element}' not found in context`);
|
|
3520
|
+
}
|
|
3521
|
+
const ctxFn = element2[contextMethod];
|
|
3522
|
+
if (typeof ctxFn !== "function") {
|
|
3523
|
+
throw new Error(`Element '${descriptor.element}' has no ${contextMethod}`);
|
|
3524
|
+
}
|
|
3525
|
+
const methods = ctxFn.call(element2, adapters);
|
|
3526
|
+
const method = methods?.[descriptor.method];
|
|
3527
|
+
if (typeof method !== "function") {
|
|
3528
|
+
throw new Error(`Method '${descriptor.method}' not found on '${descriptor.element}'`);
|
|
3529
|
+
}
|
|
3530
|
+
return method(...descriptor.args);
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
class ScopedModel {
|
|
3534
|
+
context;
|
|
3535
|
+
scopeName;
|
|
3536
|
+
parent;
|
|
3537
|
+
authAdapter;
|
|
3538
|
+
scopedAdapters;
|
|
3539
|
+
tokenListeners = new Set;
|
|
3540
|
+
constructor(parent, scopeName) {
|
|
3541
|
+
this.parent = parent;
|
|
3542
|
+
this.context = parent.context;
|
|
3543
|
+
this.scopeName = scopeName;
|
|
3544
|
+
this.authAdapter = new AuthAdapter;
|
|
3545
|
+
this.scopedAdapters = {
|
|
3546
|
+
...parent.getAdapters(),
|
|
3547
|
+
authAdapter: this.authAdapter,
|
|
3548
|
+
scope: this
|
|
3549
|
+
};
|
|
3550
|
+
}
|
|
3551
|
+
setToken(token) {
|
|
3552
|
+
this.authAdapter.setToken(token);
|
|
3553
|
+
const eventWire = this.parent.getAdapters().eventWire;
|
|
3554
|
+
if (eventWire) {
|
|
3555
|
+
eventWire.setScopeToken(this.scopeName, token);
|
|
3556
|
+
if (token && eventWire.getState() === "disconnected") {
|
|
3557
|
+
eventWire.connect();
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
for (const listener4 of this.tokenListeners) {
|
|
3561
|
+
listener4();
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
getToken() {
|
|
3565
|
+
return this.authAdapter.getToken();
|
|
3566
|
+
}
|
|
3567
|
+
getDecoded() {
|
|
3568
|
+
return this.authAdapter.getDecoded();
|
|
3569
|
+
}
|
|
3570
|
+
getParams() {
|
|
3571
|
+
return this.authAdapter.getParams();
|
|
3572
|
+
}
|
|
3573
|
+
isAuthenticated() {
|
|
3574
|
+
return this.authAdapter.isAuthenticated();
|
|
3575
|
+
}
|
|
3576
|
+
onTokenChange(listener4) {
|
|
3577
|
+
this.tokenListeners.add(listener4);
|
|
3578
|
+
return () => {
|
|
3579
|
+
this.tokenListeners.delete(listener4);
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
getAdapters() {
|
|
3583
|
+
return this.scopedAdapters;
|
|
3584
|
+
}
|
|
3585
|
+
getEnvironment() {
|
|
3586
|
+
return this.parent.getEnvironment();
|
|
3587
|
+
}
|
|
3588
|
+
getAuth() {
|
|
3589
|
+
return { scope: this.scopeName, token: this.getToken() };
|
|
3590
|
+
}
|
|
3591
|
+
executeCommand(name, params) {
|
|
3592
|
+
const wire = this.parent.getAdapters().commandWire;
|
|
3593
|
+
if (!wire) {
|
|
3594
|
+
throw new Error(`Cannot execute command "${name}": no commandWire available.`);
|
|
3595
|
+
}
|
|
3596
|
+
return wire.executeCommand(name, params, this.getAuth());
|
|
3597
|
+
}
|
|
3598
|
+
remoteQuery(viewName, options) {
|
|
3599
|
+
const wire = this.parent.getAdapters().queryWire;
|
|
3600
|
+
if (!wire) {
|
|
3601
|
+
throw new Error(`Cannot query "${viewName}": no queryWire available.`);
|
|
3602
|
+
}
|
|
3603
|
+
return wire.query(viewName, options, this.getAuth());
|
|
3604
|
+
}
|
|
3605
|
+
subscribeQuery(descriptor, callback) {
|
|
3606
|
+
const wire = this.parent.getAdapters().eventWire;
|
|
3607
|
+
if (!wire) {
|
|
3608
|
+
throw new Error(`Cannot subscribe to query: no eventWire available.`);
|
|
3609
|
+
}
|
|
3610
|
+
return wire.subscribeQuery(descriptor, callback, this.scopeName);
|
|
3611
|
+
}
|
|
3612
|
+
get query() {
|
|
3613
|
+
return buildContextAccessor(this.context, this.scopedAdapters, "queryContext", (descriptor) => descriptor);
|
|
3614
|
+
}
|
|
3615
|
+
get command() {
|
|
3616
|
+
return buildContextAccessor(this.context, this.scopedAdapters, "mutateContext", (descriptor) => descriptor);
|
|
3617
|
+
}
|
|
3618
|
+
callQuery(descriptor) {
|
|
3619
|
+
return executeDescriptor(descriptor, this.context, this.scopedAdapters, "queryContext");
|
|
3620
|
+
}
|
|
3621
|
+
callCommand(descriptor) {
|
|
3622
|
+
return executeDescriptor(descriptor, this.context, this.scopedAdapters, "mutateContext");
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
class Model {
|
|
3627
|
+
context;
|
|
3628
|
+
adapters;
|
|
3629
|
+
environment;
|
|
3630
|
+
initialized = false;
|
|
3631
|
+
scopes = new Map;
|
|
3632
|
+
constructor(context2, options) {
|
|
3633
|
+
this.context = context2;
|
|
3634
|
+
this.adapters = options.adapters;
|
|
3635
|
+
this.environment = options.environment;
|
|
3636
|
+
}
|
|
3637
|
+
async init() {
|
|
3638
|
+
if (this.initialized) {
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3641
|
+
if (this.adapters.eventPublisher) {
|
|
3642
|
+
const views = this.context.elements.filter((element2) => ("getHandlers" in element2) && ("databaseStoreSchema" in element2) && ("schema" in element2));
|
|
3643
|
+
this.adapters.eventPublisher.registerViews(views);
|
|
3644
|
+
}
|
|
3645
|
+
for (const element2 of this.context.elements) {
|
|
3646
|
+
if (element2.init) {
|
|
3647
|
+
await element2.init(this.environment, this.adapters);
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
this.initialized = true;
|
|
3651
|
+
}
|
|
3652
|
+
getAdapters() {
|
|
3653
|
+
return this.adapters;
|
|
3654
|
+
}
|
|
3655
|
+
getEnvironment() {
|
|
3656
|
+
return this.environment;
|
|
3657
|
+
}
|
|
3658
|
+
scope(name) {
|
|
3659
|
+
let s = this.scopes.get(name);
|
|
3660
|
+
if (!s) {
|
|
3661
|
+
s = new ScopedModel(this, name);
|
|
3662
|
+
this.scopes.set(name, s);
|
|
3663
|
+
}
|
|
3664
|
+
return s;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
class StreamingQueryCache {
|
|
3668
|
+
stores = new Map;
|
|
3669
|
+
views = [];
|
|
3670
|
+
activeStreams = new Map;
|
|
3671
|
+
pendingUnsubscribes = new Map;
|
|
3672
|
+
static UNSUBSCRIBE_DELAY_MS = 5000;
|
|
3673
|
+
registerViews(views) {
|
|
3674
|
+
this.views = views;
|
|
3675
|
+
for (const view3 of views) {
|
|
3676
|
+
if (!this.stores.has(view3.name)) {
|
|
3677
|
+
this.stores.set(view3.name, new StreamingStore);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
getStore(viewName) {
|
|
3682
|
+
if (!this.stores.has(viewName)) {
|
|
3683
|
+
this.stores.set(viewName, new StreamingStore);
|
|
3684
|
+
}
|
|
3685
|
+
return this.stores.get(viewName);
|
|
3686
|
+
}
|
|
3687
|
+
hasData(viewName) {
|
|
3688
|
+
const store = this.stores.get(viewName);
|
|
3689
|
+
return store ? store.hasData() : false;
|
|
3690
|
+
}
|
|
3691
|
+
hasActiveStream(viewName) {
|
|
3692
|
+
return this.activeStreams.has(viewName);
|
|
3693
|
+
}
|
|
3694
|
+
registerStream(viewName, createStream) {
|
|
3695
|
+
const pending = this.pendingUnsubscribes.get(viewName);
|
|
3696
|
+
if (pending) {
|
|
3697
|
+
clearTimeout(pending);
|
|
3698
|
+
this.pendingUnsubscribes.delete(viewName);
|
|
3699
|
+
}
|
|
3700
|
+
const existing = this.activeStreams.get(viewName);
|
|
3701
|
+
if (existing) {
|
|
3702
|
+
existing.refCount++;
|
|
3703
|
+
return {
|
|
3704
|
+
unsubscribe: () => this.unregisterStream(viewName),
|
|
3705
|
+
wasReused: true
|
|
3706
|
+
};
|
|
3707
|
+
}
|
|
3708
|
+
const streamConn = createStream();
|
|
3709
|
+
this.activeStreams.set(viewName, {
|
|
3710
|
+
unsubscribe: streamConn.unsubscribe,
|
|
3711
|
+
refCount: 1
|
|
3712
|
+
});
|
|
3713
|
+
return {
|
|
3714
|
+
unsubscribe: () => this.unregisterStream(viewName),
|
|
3715
|
+
wasReused: false
|
|
3716
|
+
};
|
|
3717
|
+
}
|
|
3718
|
+
unregisterStream(viewName) {
|
|
3719
|
+
const stream = this.activeStreams.get(viewName);
|
|
3720
|
+
if (!stream)
|
|
3721
|
+
return;
|
|
3722
|
+
stream.refCount--;
|
|
3723
|
+
if (stream.refCount <= 0) {
|
|
3724
|
+
const timeout = setTimeout(() => {
|
|
3725
|
+
this.pendingUnsubscribes.delete(viewName);
|
|
3726
|
+
const current2 = this.activeStreams.get(viewName);
|
|
3727
|
+
if (current2 && current2.refCount <= 0) {
|
|
3728
|
+
current2.unsubscribe();
|
|
3729
|
+
this.activeStreams.delete(viewName);
|
|
3730
|
+
}
|
|
3731
|
+
}, StreamingQueryCache.UNSUBSCRIBE_DELAY_MS);
|
|
3732
|
+
this.pendingUnsubscribes.set(viewName, timeout);
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
subscribeQuery(descriptor, eventWire, scope) {
|
|
3736
|
+
const key = descriptor.element;
|
|
3737
|
+
const { unsubscribe } = this.registerStream(key, () => {
|
|
3738
|
+
const subId = eventWire.subscribeQuery(descriptor, (data) => {
|
|
3739
|
+
this.setViewData(descriptor.element, data);
|
|
3740
|
+
}, scope);
|
|
3741
|
+
return { unsubscribe: () => eventWire.unsubscribeQuery(subId) };
|
|
3742
|
+
});
|
|
3743
|
+
return unsubscribe;
|
|
3744
|
+
}
|
|
3745
|
+
setViewData(viewName, data) {
|
|
3746
|
+
const store = this.stores.get(viewName);
|
|
3747
|
+
if (!store)
|
|
3748
|
+
return;
|
|
3749
|
+
if (Array.isArray(data)) {
|
|
3750
|
+
store.setAll(data);
|
|
3751
|
+
} else if (data && typeof data === "object" && "_id" in data) {
|
|
3752
|
+
store.setAll([data]);
|
|
3753
|
+
} else {
|
|
3754
|
+
store.setAll([]);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
async applyEvent(event3) {
|
|
3758
|
+
for (const view3 of this.views) {
|
|
3759
|
+
const handlers = view3.getHandlers();
|
|
3760
|
+
const handler = handlers[event3.type];
|
|
3761
|
+
if (!handler)
|
|
3762
|
+
continue;
|
|
3763
|
+
const store = this.stores.get(view3.name);
|
|
3764
|
+
if (!store)
|
|
3765
|
+
continue;
|
|
3766
|
+
const ctx = {
|
|
3767
|
+
set: async (id3, data) => {
|
|
3768
|
+
store.set(String(id3), { _id: String(id3), ...data });
|
|
3769
|
+
},
|
|
3770
|
+
modify: async (id3, data) => {
|
|
3771
|
+
store.modify(String(id3), data);
|
|
3772
|
+
},
|
|
3773
|
+
remove: async (id3) => {
|
|
3774
|
+
store.remove(String(id3));
|
|
3775
|
+
},
|
|
3776
|
+
find: async (options) => {
|
|
3777
|
+
return store.find(options);
|
|
3778
|
+
},
|
|
3779
|
+
findOne: async (where) => {
|
|
3780
|
+
return store.findOne(where);
|
|
3781
|
+
},
|
|
3782
|
+
$auth: {}
|
|
3783
|
+
};
|
|
3784
|
+
await handler(ctx, event3);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
clear() {
|
|
3788
|
+
for (const stream of this.activeStreams.values()) {
|
|
3789
|
+
stream.unsubscribe();
|
|
3790
|
+
}
|
|
3791
|
+
this.activeStreams.clear();
|
|
3792
|
+
for (const store of this.stores.values()) {
|
|
3793
|
+
store.clear();
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
class StreamingStore {
|
|
3799
|
+
data = new Map;
|
|
3800
|
+
listeners = new Set;
|
|
3801
|
+
initialized = false;
|
|
3802
|
+
hasData() {
|
|
3803
|
+
return this.initialized;
|
|
3804
|
+
}
|
|
3805
|
+
setAll(items) {
|
|
3806
|
+
this.initialized = true;
|
|
3807
|
+
this.data.clear();
|
|
3808
|
+
for (const item of items) {
|
|
3809
|
+
this.data.set(item._id, item);
|
|
3810
|
+
}
|
|
3811
|
+
this.notifyListeners(null);
|
|
3812
|
+
}
|
|
3813
|
+
set(id3, item) {
|
|
3814
|
+
this.data.set(id3, item);
|
|
3815
|
+
this.notifyListeners([{ type: "set", id: id3, item }]);
|
|
3816
|
+
}
|
|
3817
|
+
modify(id3, updates) {
|
|
3818
|
+
const existing = this.data.get(id3);
|
|
3819
|
+
if (existing) {
|
|
3820
|
+
const updated = { ...existing, ...updates };
|
|
3821
|
+
this.data.set(id3, updated);
|
|
3822
|
+
this.notifyListeners([{ type: "set", id: id3, item: updated }]);
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
remove(id3) {
|
|
3826
|
+
if (this.data.delete(id3)) {
|
|
3827
|
+
this.notifyListeners([{ type: "delete", id: id3, item: null }]);
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
clear() {
|
|
3831
|
+
this.initialized = false;
|
|
3832
|
+
this.data.clear();
|
|
3833
|
+
this.notifyListeners(null);
|
|
3834
|
+
}
|
|
3835
|
+
find(options = {}) {
|
|
3836
|
+
let results = Array.from(this.data.values());
|
|
3837
|
+
if (options.where) {
|
|
3838
|
+
results = results.filter((item) => checkItemMatchesWhere(item, options.where));
|
|
3839
|
+
}
|
|
3840
|
+
return applyOrderByAndLimit(results, options);
|
|
3841
|
+
}
|
|
3842
|
+
findOne(where) {
|
|
3843
|
+
const results = this.find({ where });
|
|
3844
|
+
return results[0];
|
|
3845
|
+
}
|
|
3846
|
+
subscribe(listener4) {
|
|
3847
|
+
this.listeners.add(listener4);
|
|
3848
|
+
return () => {
|
|
3849
|
+
this.listeners.delete(listener4);
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
notifyListeners(events) {
|
|
3853
|
+
for (const listener4 of this.listeners) {
|
|
3854
|
+
listener4(events);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
class StreamingEventPublisher {
|
|
3860
|
+
cache;
|
|
3861
|
+
eventWire;
|
|
3862
|
+
views = [];
|
|
3863
|
+
subscribers = new Map;
|
|
3864
|
+
constructor(cache, eventWire) {
|
|
3865
|
+
this.cache = cache;
|
|
3866
|
+
this.eventWire = eventWire;
|
|
3867
|
+
}
|
|
3868
|
+
registerViews(views) {
|
|
3869
|
+
this.views = views;
|
|
3870
|
+
this.cache.registerViews(views);
|
|
3871
|
+
}
|
|
3872
|
+
async publish(event3) {
|
|
3873
|
+
await this.cache.applyEvent(event3);
|
|
3874
|
+
await this.notifySubscribers(event3);
|
|
3875
|
+
this.eventWire.syncEvents([
|
|
3876
|
+
{
|
|
3877
|
+
localId: event3.id,
|
|
3878
|
+
type: event3.type,
|
|
3879
|
+
payload: event3.payload,
|
|
3880
|
+
createdAt: event3.createdAt.toISOString()
|
|
3881
|
+
}
|
|
3882
|
+
]);
|
|
3883
|
+
}
|
|
3884
|
+
subscribe(eventType, callback) {
|
|
3885
|
+
if (!this.subscribers.has(eventType)) {
|
|
3886
|
+
this.subscribers.set(eventType, new Set);
|
|
3887
|
+
}
|
|
3888
|
+
this.subscribers.get(eventType).add(callback);
|
|
3889
|
+
return () => {
|
|
3890
|
+
const subs = this.subscribers.get(eventType);
|
|
3891
|
+
if (subs) {
|
|
3892
|
+
subs.delete(callback);
|
|
3893
|
+
if (subs.size === 0) {
|
|
3894
|
+
this.subscribers.delete(eventType);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
async markSynced(_eventIds) {}
|
|
3900
|
+
async getUnsyncedEvents(_eventType) {
|
|
3901
|
+
return [];
|
|
3902
|
+
}
|
|
3903
|
+
async notifySubscribers(event3) {
|
|
3904
|
+
const subs = this.subscribers.get(event3.type);
|
|
3905
|
+
if (!subs || subs.size === 0)
|
|
3906
|
+
return;
|
|
3907
|
+
const promises = [];
|
|
3908
|
+
for (const callback of subs) {
|
|
3909
|
+
try {
|
|
3910
|
+
const result = callback(event3);
|
|
3911
|
+
if (result instanceof Promise) {
|
|
3912
|
+
promises.push(result);
|
|
3913
|
+
}
|
|
3914
|
+
} catch (error) {
|
|
3915
|
+
console.error(`Listener error for event "${event3.type}":`, error);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
if (promises.length > 0) {
|
|
3919
|
+
await Promise.all(promises.map((p) => p.catch((e) => console.error(e))));
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3924
|
+
class SecuredStoreState {
|
|
3925
|
+
store;
|
|
3926
|
+
tokenInstance;
|
|
3927
|
+
storeName;
|
|
3928
|
+
context;
|
|
3929
|
+
protection;
|
|
3930
|
+
constructor(store, tokenInstance, storeName, context2) {
|
|
3931
|
+
this.store = store;
|
|
3932
|
+
this.tokenInstance = tokenInstance;
|
|
3933
|
+
this.storeName = storeName;
|
|
3934
|
+
this.context = context2;
|
|
3935
|
+
this.protection = this.getProtectionFromContext();
|
|
3936
|
+
}
|
|
3937
|
+
getProtectionFromContext() {
|
|
3938
|
+
if (!this.context) {
|
|
3939
|
+
return null;
|
|
3940
|
+
}
|
|
3941
|
+
const view3 = this.context.elements.find((el) => el.name === this.storeName);
|
|
3942
|
+
if (!view3 || !("getProtectionFor" in view3)) {
|
|
3943
|
+
return null;
|
|
3944
|
+
}
|
|
3945
|
+
return view3.getProtectionFor(this.tokenInstance);
|
|
3946
|
+
}
|
|
3947
|
+
async find(options = {}) {
|
|
3948
|
+
const securedOptions = this.applyTokenCondition(options, "read");
|
|
3949
|
+
if (securedOptions === null) {
|
|
3950
|
+
return [];
|
|
3951
|
+
}
|
|
3952
|
+
return this.store.find(securedOptions);
|
|
3953
|
+
}
|
|
3954
|
+
async set(item) {
|
|
3955
|
+
if (!this.canWrite()) {
|
|
3956
|
+
throw new Error(`Write access denied for store "${this.storeName}" with token "${this.tokenInstance.name}"`);
|
|
3957
|
+
}
|
|
3958
|
+
const writeCondition = this.getWriteCondition();
|
|
3959
|
+
if (writeCondition) {
|
|
3960
|
+
if (!this.itemMatchesCondition(item, writeCondition)) {
|
|
3961
|
+
throw new Error(`Item does not match write condition for store "${this.storeName}"`);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
return this.store.set(item);
|
|
3965
|
+
}
|
|
3966
|
+
async remove(id3) {
|
|
3967
|
+
if (!this.canWrite()) {
|
|
3968
|
+
throw new Error(`Write access denied for store "${this.storeName}" with token "${this.tokenInstance.name}"`);
|
|
3969
|
+
}
|
|
3970
|
+
return this.store.remove(id3);
|
|
3971
|
+
}
|
|
3972
|
+
async modify(id3, data) {
|
|
3973
|
+
if (!this.canWrite()) {
|
|
3974
|
+
throw new Error(`Write access denied for store "${this.storeName}" with token "${this.tokenInstance.name}"`);
|
|
3975
|
+
}
|
|
3976
|
+
return this.store.modify(id3, data);
|
|
3977
|
+
}
|
|
3978
|
+
async applyChanges(changes) {
|
|
3979
|
+
if (!this.canWrite()) {
|
|
3980
|
+
throw new Error(`Write access denied for store "${this.storeName}" with token "${this.tokenInstance.name}"`);
|
|
3981
|
+
}
|
|
3982
|
+
return this.store.applyChanges(changes);
|
|
3983
|
+
}
|
|
3984
|
+
fork() {
|
|
3985
|
+
return this.store.fork();
|
|
3986
|
+
}
|
|
3987
|
+
applyTokenCondition(options, operation) {
|
|
3988
|
+
if (!this.protection) {
|
|
3989
|
+
return options;
|
|
3990
|
+
}
|
|
3991
|
+
const condition = operation === "read" ? this.protection.read : this.protection.write;
|
|
3992
|
+
if (condition === false) {
|
|
3993
|
+
return null;
|
|
3994
|
+
}
|
|
3995
|
+
const mergedWhere = {
|
|
3996
|
+
...options.where,
|
|
3997
|
+
...condition
|
|
3998
|
+
};
|
|
3999
|
+
return {
|
|
4000
|
+
...options,
|
|
4001
|
+
where: mergedWhere
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
canWrite() {
|
|
4005
|
+
if (!this.protection) {
|
|
4006
|
+
return true;
|
|
4007
|
+
}
|
|
4008
|
+
return this.protection.write !== false;
|
|
4009
|
+
}
|
|
4010
|
+
getWriteCondition() {
|
|
4011
|
+
if (!this.protection || this.protection.write === false) {
|
|
4012
|
+
return null;
|
|
4013
|
+
}
|
|
4014
|
+
return this.protection.write || null;
|
|
4015
|
+
}
|
|
4016
|
+
itemMatchesCondition(item, condition) {
|
|
4017
|
+
for (const [key, value] of Object.entries(condition)) {
|
|
4018
|
+
if (item[key] !== value) {
|
|
4019
|
+
return false;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
return true;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
class SecuredDataStorage {
|
|
4027
|
+
dataStorage;
|
|
4028
|
+
tokenInstance;
|
|
4029
|
+
context;
|
|
4030
|
+
storeCache = new Map;
|
|
4031
|
+
constructor(dataStorage, tokenInstance, context2) {
|
|
4032
|
+
this.dataStorage = dataStorage;
|
|
4033
|
+
this.tokenInstance = tokenInstance;
|
|
4034
|
+
this.context = context2;
|
|
4035
|
+
}
|
|
4036
|
+
getStore(storeName) {
|
|
4037
|
+
let securedStore = this.storeCache.get(storeName);
|
|
4038
|
+
if (!securedStore) {
|
|
4039
|
+
const store = this.dataStorage.getStore(storeName);
|
|
4040
|
+
securedStore = new SecuredStoreState(store, this.tokenInstance, storeName, this.context);
|
|
4041
|
+
this.storeCache.set(storeName, securedStore);
|
|
4042
|
+
}
|
|
4043
|
+
return securedStore;
|
|
4044
|
+
}
|
|
4045
|
+
getUnsecuredStorage() {
|
|
4046
|
+
return this.dataStorage;
|
|
4047
|
+
}
|
|
4048
|
+
getTokenInstance() {
|
|
4049
|
+
return this.tokenInstance;
|
|
4050
|
+
}
|
|
4051
|
+
fork() {
|
|
4052
|
+
return this.dataStorage.fork();
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
class TokenCache {
|
|
4056
|
+
instanceCache = new Map;
|
|
4057
|
+
permissionCache = new Map;
|
|
4058
|
+
ttl;
|
|
4059
|
+
maxSize;
|
|
4060
|
+
constructor(options = {}) {
|
|
4061
|
+
this.ttl = options.ttl ?? 5 * 60 * 1000;
|
|
4062
|
+
this.maxSize = options.maxSize ?? 1000;
|
|
4063
|
+
}
|
|
4064
|
+
async getOrCreate(jwt, factory) {
|
|
4065
|
+
const cached = this.instanceCache.get(jwt);
|
|
4066
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
4067
|
+
return cached.value;
|
|
4068
|
+
}
|
|
4069
|
+
const instance = await factory();
|
|
4070
|
+
if (instance) {
|
|
4071
|
+
this.setInstance(jwt, instance);
|
|
4072
|
+
}
|
|
4073
|
+
return instance;
|
|
4074
|
+
}
|
|
4075
|
+
async getPermission(instance, permission, factory) {
|
|
4076
|
+
const key = this.getPermissionKey(instance, permission);
|
|
4077
|
+
const cached = this.permissionCache.get(key);
|
|
4078
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
4079
|
+
return cached.value;
|
|
4080
|
+
}
|
|
4081
|
+
const result = await factory();
|
|
4082
|
+
this.setPermission(key, result);
|
|
4083
|
+
return result;
|
|
4084
|
+
}
|
|
4085
|
+
setInstance(jwt, instance) {
|
|
4086
|
+
this.ensureCapacity(this.instanceCache);
|
|
4087
|
+
this.instanceCache.set(jwt, {
|
|
4088
|
+
value: instance,
|
|
4089
|
+
expiresAt: Date.now() + this.ttl
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
setPermission(key, result) {
|
|
4093
|
+
this.ensureCapacity(this.permissionCache);
|
|
4094
|
+
this.permissionCache.set(key, {
|
|
4095
|
+
value: result,
|
|
4096
|
+
expiresAt: Date.now() + this.ttl
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
getPermissionKey(instance, permission) {
|
|
4100
|
+
return `${instance.name}:${JSON.stringify(instance.params)}:${permission}`;
|
|
4101
|
+
}
|
|
4102
|
+
ensureCapacity(cache) {
|
|
4103
|
+
if (cache.size >= this.maxSize) {
|
|
4104
|
+
const toRemove = Math.floor(this.maxSize * 0.1);
|
|
4105
|
+
const keys = Array.from(cache.keys()).slice(0, toRemove);
|
|
4106
|
+
for (const key of keys) {
|
|
4107
|
+
cache.delete(key);
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
invalidateInstance(instance) {
|
|
4112
|
+
const prefix = `${instance.name}:${JSON.stringify(instance.params)}:`;
|
|
4113
|
+
for (const key of this.permissionCache.keys()) {
|
|
4114
|
+
if (key.startsWith(prefix)) {
|
|
4115
|
+
this.permissionCache.delete(key);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
invalidateJWT(jwt) {
|
|
4120
|
+
const instance = this.instanceCache.get(jwt)?.value;
|
|
4121
|
+
this.instanceCache.delete(jwt);
|
|
4122
|
+
if (instance) {
|
|
4123
|
+
this.invalidateInstance(instance);
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
clear() {
|
|
4127
|
+
this.instanceCache.clear();
|
|
4128
|
+
this.permissionCache.clear();
|
|
4129
|
+
}
|
|
4130
|
+
cleanup() {
|
|
4131
|
+
const now = Date.now();
|
|
4132
|
+
for (const [key, entry] of this.instanceCache) {
|
|
4133
|
+
if (entry.expiresAt <= now) {
|
|
4134
|
+
this.instanceCache.delete(key);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
for (const [key, entry] of this.permissionCache) {
|
|
4138
|
+
if (entry.expiresAt <= now) {
|
|
4139
|
+
this.permissionCache.delete(key);
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
getStats() {
|
|
4144
|
+
return {
|
|
4145
|
+
instanceCount: this.instanceCache.size,
|
|
4146
|
+
permissionCount: this.permissionCache.size,
|
|
4147
|
+
maxSize: this.maxSize,
|
|
4148
|
+
ttl: this.ttl
|
|
4149
|
+
};
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
// src/sqlite-adapter.ts
|
|
4154
|
+
class SQLiteReadTransaction {
|
|
4155
|
+
db;
|
|
4156
|
+
tables;
|
|
4157
|
+
adapter;
|
|
4158
|
+
constructor(db, tables, adapter) {
|
|
4159
|
+
this.db = db;
|
|
4160
|
+
this.tables = tables;
|
|
4161
|
+
this.adapter = adapter;
|
|
4162
|
+
}
|
|
4163
|
+
hasSoftDelete(tableName) {
|
|
4164
|
+
if (this.adapter) {
|
|
4165
|
+
return this.adapter.hasSoftDelete(tableName);
|
|
4166
|
+
}
|
|
4167
|
+
const table = this.tables.get(tableName);
|
|
4168
|
+
if (!table)
|
|
4169
|
+
return false;
|
|
4170
|
+
return table.columns.some((col) => col.name === "deleted");
|
|
4171
|
+
}
|
|
4172
|
+
deserializeValue(value, column) {
|
|
4173
|
+
if (value === null || value === undefined)
|
|
4174
|
+
return null;
|
|
4175
|
+
switch (column.type.toLowerCase()) {
|
|
4176
|
+
case "json":
|
|
4177
|
+
if (typeof value === "string") {
|
|
4178
|
+
try {
|
|
4179
|
+
return JSON.parse(value);
|
|
4180
|
+
} catch {
|
|
4181
|
+
return value;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
return value;
|
|
4185
|
+
case "text":
|
|
4186
|
+
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
|
|
4187
|
+
try {
|
|
4188
|
+
const parsed = JSON.parse(value);
|
|
4189
|
+
if (typeof parsed === "object" || Array.isArray(parsed)) {
|
|
4190
|
+
return parsed;
|
|
4191
|
+
}
|
|
4192
|
+
} catch {}
|
|
4193
|
+
}
|
|
4194
|
+
return value;
|
|
4195
|
+
case "datetime":
|
|
4196
|
+
case "timestamp":
|
|
4197
|
+
return new Date(value);
|
|
4198
|
+
default:
|
|
4199
|
+
return value;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
deserializeRow(row, table) {
|
|
4203
|
+
const result = {};
|
|
4204
|
+
for (const column of table.columns) {
|
|
4205
|
+
const value = row[column.name];
|
|
4206
|
+
result[column.name] = this.deserializeValue(value, column);
|
|
4207
|
+
}
|
|
4208
|
+
return result;
|
|
4209
|
+
}
|
|
4210
|
+
getId(store, id2) {
|
|
4211
|
+
return id2;
|
|
4212
|
+
}
|
|
4213
|
+
buildWhereClause(where, tableName) {
|
|
4214
|
+
const conditions = [];
|
|
4215
|
+
const params = [];
|
|
4216
|
+
if (tableName && this.hasSoftDelete(tableName)) {
|
|
4217
|
+
conditions.push('"deleted" = 0');
|
|
4218
|
+
}
|
|
4219
|
+
if (!where) {
|
|
4220
|
+
return {
|
|
4221
|
+
sql: conditions.length > 0 ? conditions.join(" AND ") : "1=1",
|
|
4222
|
+
params
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
Object.entries(where).forEach(([key, value]) => {
|
|
4226
|
+
if (typeof value === "object" && value !== null) {
|
|
4227
|
+
Object.entries(value).forEach(([operator, operand]) => {
|
|
4228
|
+
switch (operator) {
|
|
4229
|
+
case "$eq":
|
|
4230
|
+
case "$ne":
|
|
4231
|
+
case "$gt":
|
|
4232
|
+
case "$gte":
|
|
4233
|
+
case "$lt":
|
|
4234
|
+
case "$lte":
|
|
4235
|
+
conditions.push(`"${key}" ${this.getOperatorSymbol(operator)} ?`);
|
|
4236
|
+
params.push(operand);
|
|
4237
|
+
break;
|
|
4238
|
+
case "$in":
|
|
4239
|
+
case "$nin":
|
|
4240
|
+
if (Array.isArray(operand)) {
|
|
4241
|
+
conditions.push(`"${key}" ${operator === "$in" ? "IN" : "NOT IN"} (${operand.map(() => "?").join(", ")})`);
|
|
4242
|
+
params.push(...operand);
|
|
4243
|
+
}
|
|
4244
|
+
break;
|
|
4245
|
+
case "$exists":
|
|
4246
|
+
if (typeof operand === "boolean") {
|
|
4247
|
+
conditions.push(operand ? `"${key}" IS NOT NULL` : `"${key}" IS NULL`);
|
|
4248
|
+
}
|
|
4249
|
+
break;
|
|
4250
|
+
}
|
|
4251
|
+
});
|
|
4252
|
+
} else {
|
|
4253
|
+
conditions.push(`"${key}" = ?`);
|
|
4254
|
+
params.push(value);
|
|
4255
|
+
}
|
|
4256
|
+
});
|
|
4257
|
+
return {
|
|
4258
|
+
sql: conditions.join(" AND "),
|
|
4259
|
+
params
|
|
4260
|
+
};
|
|
4261
|
+
}
|
|
4262
|
+
getOperatorSymbol(operator) {
|
|
4263
|
+
const operators = {
|
|
4264
|
+
$eq: "=",
|
|
4265
|
+
$ne: "!=",
|
|
4266
|
+
$gt: ">",
|
|
4267
|
+
$gte: ">=",
|
|
4268
|
+
$lt: "<",
|
|
4269
|
+
$lte: "<="
|
|
4270
|
+
};
|
|
4271
|
+
return operators[operator] || "=";
|
|
4272
|
+
}
|
|
4273
|
+
buildOrderByClause(orderBy) {
|
|
4274
|
+
if (!orderBy)
|
|
4275
|
+
return "";
|
|
4276
|
+
const orderClauses = Object.entries(orderBy).map(([key, direction]) => `"${key}" ${direction.toUpperCase()}`).join(", ");
|
|
4277
|
+
return orderClauses ? `ORDER BY ${orderClauses}` : "";
|
|
4278
|
+
}
|
|
4279
|
+
async find(store, options) {
|
|
4280
|
+
const { where, limit, offset, orderBy } = options || {};
|
|
4281
|
+
const whereClause = this.buildWhereClause(where, store);
|
|
4282
|
+
const orderByClause = this.buildOrderByClause(orderBy);
|
|
4283
|
+
const table = this.tables.get(store);
|
|
4284
|
+
if (!table) {
|
|
4285
|
+
throw new Error(`Store ${store} not found`);
|
|
4286
|
+
}
|
|
4287
|
+
const query = `
|
|
4288
|
+
SELECT *
|
|
4289
|
+
FROM "${table.name}"
|
|
4290
|
+
WHERE ${whereClause.sql}
|
|
4291
|
+
${orderByClause}
|
|
4292
|
+
${limit ? `LIMIT ${limit}` : ""}
|
|
4293
|
+
${offset ? `OFFSET ${offset}` : ""}
|
|
4294
|
+
`;
|
|
4295
|
+
const rows = await this.db.exec(query, whereClause.params);
|
|
4296
|
+
return rows.map((row) => this.deserializeRow(row, table));
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
|
|
4300
|
+
class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
|
|
4301
|
+
adapter;
|
|
4302
|
+
queries = [];
|
|
4303
|
+
constructor(db, tables, adapter) {
|
|
4304
|
+
super(db, tables);
|
|
4305
|
+
this.adapter = adapter;
|
|
4306
|
+
}
|
|
4307
|
+
async remove(store, id2) {
|
|
4308
|
+
const table = this.tables.get(store);
|
|
4309
|
+
if (!table) {
|
|
4310
|
+
throw new Error(`Store ${store} not found`);
|
|
4311
|
+
}
|
|
4312
|
+
const hasSoftDelete = this.adapter.hasSoftDelete(store);
|
|
4313
|
+
if (hasSoftDelete) {
|
|
4314
|
+
const query = `UPDATE "${table.name}" SET "deleted" = 1, "lastUpdate" = ? WHERE "${table.primaryKey}" = ?`;
|
|
4315
|
+
this.queries.push({
|
|
4316
|
+
sql: query,
|
|
4317
|
+
params: [new Date().toISOString(), id2]
|
|
4318
|
+
});
|
|
4319
|
+
} else {
|
|
4320
|
+
const query = `DELETE FROM "${table.name}" WHERE "${table.primaryKey}" = ?`;
|
|
4321
|
+
this.queries.push({
|
|
4322
|
+
sql: query,
|
|
4323
|
+
params: [id2]
|
|
4324
|
+
});
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
async set(store, item) {
|
|
4328
|
+
const table = this.tables.get(store);
|
|
4329
|
+
if (!table) {
|
|
4330
|
+
throw new Error(`Store ${store} not found`);
|
|
4331
|
+
}
|
|
4332
|
+
const hasVersioning = this.adapter.hasVersioning(store);
|
|
4333
|
+
if (hasVersioning) {
|
|
4334
|
+
await this.setWithVersioning(store, item, table);
|
|
4335
|
+
} else {
|
|
4336
|
+
await this.setWithoutVersioning(store, item, table);
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
async setWithoutVersioning(store, item, table) {
|
|
4340
|
+
const columnNames = table.columns.map((col) => col.name);
|
|
4341
|
+
const values = table.columns.map((column) => {
|
|
4342
|
+
let value = item[column.name];
|
|
4343
|
+
if (value === undefined && column.default !== undefined) {
|
|
4344
|
+
value = column.default;
|
|
4345
|
+
}
|
|
4346
|
+
return this.serializeValue(value, column);
|
|
4347
|
+
});
|
|
4348
|
+
const placeholders = columnNames.map(() => "?").join(", ");
|
|
4349
|
+
if (store === "events") {
|
|
4350
|
+
const simpleInsertSql = `
|
|
4351
|
+
INSERT INTO "${table.name}"
|
|
4352
|
+
(${columnNames.map((c) => `"${c}"`).join(", ")})
|
|
4353
|
+
VALUES (${placeholders})
|
|
4354
|
+
`;
|
|
4355
|
+
this.queries.push({
|
|
4356
|
+
sql: simpleInsertSql,
|
|
4357
|
+
params: values
|
|
4358
|
+
});
|
|
4359
|
+
} else {
|
|
4360
|
+
const sql = `
|
|
4361
|
+
INSERT OR REPLACE INTO "${table.name}"
|
|
4362
|
+
(${columnNames.map((c) => `"${c}"`).join(", ")})
|
|
4363
|
+
VALUES (${placeholders})
|
|
4364
|
+
`;
|
|
4365
|
+
this.queries.push({
|
|
4366
|
+
sql,
|
|
4367
|
+
params: values
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
async setWithVersioning(store, item, table) {
|
|
4372
|
+
const regularColumns = table.columns.filter((col) => col.name !== "__version");
|
|
4373
|
+
const columnNames = regularColumns.map((col) => col.name);
|
|
4374
|
+
const values = regularColumns.map((column) => {
|
|
4375
|
+
let value = item[column.name];
|
|
4376
|
+
if (value === undefined && column.default !== undefined) {
|
|
4377
|
+
value = column.default;
|
|
4378
|
+
}
|
|
4379
|
+
return this.serializeValue(value, column);
|
|
4380
|
+
});
|
|
4381
|
+
columnNames.push("__version");
|
|
4382
|
+
const placeholders = regularColumns.map(() => "?").join(", ");
|
|
4383
|
+
const sql = `
|
|
4384
|
+
WITH next_version AS (
|
|
4385
|
+
INSERT INTO __arc_version_counters (table_name, last_version)
|
|
4386
|
+
VALUES (?, 1)
|
|
4387
|
+
ON CONFLICT(table_name)
|
|
4388
|
+
DO UPDATE SET last_version = last_version + 1
|
|
4389
|
+
RETURNING last_version
|
|
4390
|
+
)
|
|
4391
|
+
INSERT OR REPLACE INTO "${table.name}"
|
|
4392
|
+
(${columnNames.map((c) => `"${c}"`).join(", ")})
|
|
4393
|
+
VALUES (${placeholders}, (SELECT last_version FROM next_version))
|
|
4394
|
+
`;
|
|
4395
|
+
this.queries.push({
|
|
4396
|
+
sql,
|
|
4397
|
+
params: [...values, store]
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4400
|
+
async commit() {
|
|
4401
|
+
if (this.queries.length === 0) {
|
|
4402
|
+
return Promise.resolve();
|
|
4403
|
+
}
|
|
4404
|
+
try {
|
|
4405
|
+
await this.db.execBatch(this.queries);
|
|
4406
|
+
this.queries = [];
|
|
4407
|
+
} catch (error) {
|
|
4408
|
+
this.queries = [];
|
|
4409
|
+
throw error;
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
serializeValue(value, column) {
|
|
4413
|
+
if (value === null || value === undefined)
|
|
4414
|
+
return null;
|
|
4415
|
+
switch (column.type.toLowerCase()) {
|
|
4416
|
+
case "timestamp":
|
|
4417
|
+
case "datetime":
|
|
4418
|
+
if (value instanceof Date) {
|
|
4419
|
+
return value.toISOString();
|
|
4420
|
+
}
|
|
4421
|
+
if (typeof value === "number") {
|
|
4422
|
+
const date = value > 10000000000 ? new Date(value) : new Date(value * 1000);
|
|
4423
|
+
return date.toISOString();
|
|
4424
|
+
}
|
|
4425
|
+
if (typeof value === "string") {
|
|
4426
|
+
const date = new Date(value);
|
|
4427
|
+
if (!isNaN(date.getTime())) {
|
|
4428
|
+
return date.toISOString();
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
return value;
|
|
4432
|
+
case "json":
|
|
4433
|
+
return JSON.stringify(value);
|
|
4434
|
+
default:
|
|
4435
|
+
if (value instanceof Date) {
|
|
4436
|
+
return value.toISOString();
|
|
4437
|
+
}
|
|
4438
|
+
if (Array.isArray(value) || typeof value === "object") {
|
|
4439
|
+
return JSON.stringify(value);
|
|
4440
|
+
}
|
|
4441
|
+
return value;
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
class SQLiteAdapter {
|
|
4447
|
+
db;
|
|
4448
|
+
context;
|
|
4449
|
+
tables = new Map;
|
|
4450
|
+
tableSchemas = new Map;
|
|
4451
|
+
pendingReinitTables = [];
|
|
4452
|
+
mapType(arcType, storeData) {
|
|
4453
|
+
if (storeData?.databaseType?.sqlite) {
|
|
4454
|
+
return storeData.databaseType.sqlite;
|
|
4455
|
+
}
|
|
4456
|
+
switch (arcType) {
|
|
4457
|
+
case "string":
|
|
4458
|
+
case "id":
|
|
4459
|
+
case "customId":
|
|
4460
|
+
case "stringEnum":
|
|
4461
|
+
return "TEXT";
|
|
4462
|
+
case "number":
|
|
4463
|
+
return "INTEGER";
|
|
4464
|
+
case "boolean":
|
|
4465
|
+
return "INTEGER";
|
|
4466
|
+
case "date":
|
|
4467
|
+
return "TIMESTAMP";
|
|
4468
|
+
case "object":
|
|
4469
|
+
case "array":
|
|
4470
|
+
case "record":
|
|
4471
|
+
return "JSON";
|
|
4472
|
+
case "blob":
|
|
4473
|
+
return "BLOB";
|
|
4474
|
+
default:
|
|
4475
|
+
return "TEXT";
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
buildConstraints(storeData) {
|
|
4479
|
+
const constraints = [];
|
|
4480
|
+
if (storeData?.isPrimaryKey) {
|
|
4481
|
+
constraints.push("PRIMARY KEY");
|
|
4482
|
+
}
|
|
4483
|
+
if (storeData?.isAutoIncrement) {
|
|
4484
|
+
constraints.push("AUTOINCREMENT");
|
|
4485
|
+
}
|
|
4486
|
+
if (storeData?.isUnique) {
|
|
4487
|
+
constraints.push("UNIQUE");
|
|
4488
|
+
}
|
|
4489
|
+
if (storeData?.foreignKey) {
|
|
4490
|
+
const { table, column, onDelete, onUpdate } = storeData.foreignKey;
|
|
4491
|
+
let fkConstraint = `REFERENCES ${table}(${column})`;
|
|
4492
|
+
if (onDelete)
|
|
4493
|
+
fkConstraint += ` ON DELETE ${onDelete}`;
|
|
4494
|
+
if (onUpdate)
|
|
4495
|
+
fkConstraint += ` ON UPDATE ${onUpdate}`;
|
|
4496
|
+
constraints.push(fkConstraint);
|
|
4497
|
+
}
|
|
4498
|
+
return constraints;
|
|
4499
|
+
}
|
|
4500
|
+
generateColumnSQL(columnInfo) {
|
|
4501
|
+
const type = this.mapType(columnInfo.type, columnInfo.storeData);
|
|
4502
|
+
const constraints = [];
|
|
4503
|
+
if (!columnInfo.storeData?.isNullable) {
|
|
4504
|
+
constraints.push("NOT NULL");
|
|
4505
|
+
}
|
|
4506
|
+
constraints.push(...this.buildConstraints(columnInfo.storeData));
|
|
4507
|
+
if (columnInfo.defaultValue !== undefined) {
|
|
4508
|
+
if (typeof columnInfo.defaultValue === "string") {
|
|
4509
|
+
constraints.push(`DEFAULT '${columnInfo.defaultValue}'`);
|
|
4510
|
+
} else {
|
|
4511
|
+
constraints.push(`DEFAULT ${columnInfo.defaultValue}`);
|
|
4512
|
+
}
|
|
4513
|
+
}
|
|
4514
|
+
return `"${columnInfo.name}" ${type} ${constraints.join(" ")}`.trim();
|
|
4515
|
+
}
|
|
4516
|
+
generateCreateTableSQL(tableName, columns) {
|
|
4517
|
+
const columnDefinitions = columns.map((col) => this.generateColumnSQL(col));
|
|
4518
|
+
const indexes = columns.filter((col) => col.storeData?.hasIndex && !col.storeData?.isPrimaryKey).map((col) => `CREATE INDEX IF NOT EXISTS idx_${tableName}_${col.name} ON "${tableName}"("${col.name}");`);
|
|
4519
|
+
let sql = `CREATE TABLE IF NOT EXISTS "${tableName}" (
|
|
4520
|
+
${columnDefinitions.join(`,
|
|
4521
|
+
`)}
|
|
4522
|
+
)`;
|
|
4523
|
+
if (indexes.length > 0) {
|
|
4524
|
+
sql += `;
|
|
4525
|
+
` + indexes.join(`
|
|
4526
|
+
`);
|
|
4527
|
+
}
|
|
4528
|
+
return sql;
|
|
4529
|
+
}
|
|
4530
|
+
constructor(db, context) {
|
|
4531
|
+
this.db = db;
|
|
4532
|
+
this.context = context;
|
|
4533
|
+
this.context.elements.forEach((element) => {
|
|
4534
|
+
if ("databaseStoreSchema" in element && typeof element.databaseStoreSchema === "function") {
|
|
4535
|
+
const databaseSchema = element.databaseStoreSchema();
|
|
4536
|
+
databaseSchema.tables.forEach((dbTable) => {
|
|
4537
|
+
const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
|
|
4538
|
+
const columns = agnosticSchema.columns.map((columnInfo) => ({
|
|
4539
|
+
name: columnInfo.name,
|
|
4540
|
+
type: this.mapType(columnInfo.type, columnInfo.storeData),
|
|
4541
|
+
constraints: this.buildConstraints(columnInfo.storeData),
|
|
4542
|
+
isNullable: columnInfo.storeData?.isNullable || false,
|
|
4543
|
+
defaultValue: columnInfo.defaultValue,
|
|
4544
|
+
isPrimaryKey: columnInfo.storeData?.isPrimaryKey || false,
|
|
4545
|
+
isAutoIncrement: columnInfo.storeData?.isAutoIncrement || false,
|
|
4546
|
+
isUnique: columnInfo.storeData?.isUnique || false,
|
|
4547
|
+
hasIndex: columnInfo.storeData?.hasIndex || false,
|
|
4548
|
+
foreignKey: columnInfo.storeData?.foreignKey
|
|
4549
|
+
}));
|
|
4550
|
+
this.tableSchemas.set(dbTable.name, columns);
|
|
4551
|
+
const legacyTable = {
|
|
4552
|
+
name: dbTable.name,
|
|
4553
|
+
primaryKey: columns.find((col) => col.isPrimaryKey)?.name || "_id",
|
|
4554
|
+
columns: columns.map((col) => ({
|
|
4555
|
+
name: col.name,
|
|
4556
|
+
type: col.type,
|
|
4557
|
+
isOptional: col.isNullable,
|
|
4558
|
+
default: col.defaultValue
|
|
4559
|
+
}))
|
|
4560
|
+
};
|
|
4561
|
+
this.tables.set(dbTable.name, legacyTable);
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4564
|
+
});
|
|
4565
|
+
}
|
|
4566
|
+
async initialize() {
|
|
4567
|
+
await this.createVersionCounterTable();
|
|
4568
|
+
await this.createTableVersionsTable();
|
|
4569
|
+
const processedSchemas = new Set;
|
|
4570
|
+
const processedTables = new Set;
|
|
4571
|
+
for (const element of this.context.elements) {
|
|
4572
|
+
if ("databaseStoreSchema" in element && typeof element.databaseStoreSchema === "function") {
|
|
4573
|
+
const databaseSchema = element.databaseStoreSchema();
|
|
4574
|
+
if (processedSchemas.has(databaseSchema)) {
|
|
4575
|
+
continue;
|
|
4576
|
+
}
|
|
4577
|
+
processedSchemas.add(databaseSchema);
|
|
4578
|
+
for (const dbTable of databaseSchema.tables) {
|
|
4579
|
+
const tableKey = dbTable.version ? `${dbTable.name}_v${dbTable.version}` : dbTable.name;
|
|
4580
|
+
if (!processedTables.has(tableKey)) {
|
|
4581
|
+
const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
|
|
4582
|
+
let allColumns = [...agnosticSchema.columns];
|
|
4583
|
+
if (dbTable.options?.versioning) {
|
|
4584
|
+
allColumns.push({
|
|
4585
|
+
name: "__version",
|
|
4586
|
+
type: "number",
|
|
4587
|
+
storeData: { isNullable: false, hasIndex: true },
|
|
4588
|
+
defaultValue: 1
|
|
4589
|
+
});
|
|
4590
|
+
}
|
|
4591
|
+
if (dbTable.options?.softDelete) {
|
|
4592
|
+
allColumns.push({
|
|
4593
|
+
name: "deleted",
|
|
4594
|
+
type: "boolean",
|
|
4595
|
+
storeData: { isNullable: false, hasIndex: true },
|
|
4596
|
+
defaultValue: false
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
|
|
4600
|
+
if (dbTable.version && await this.checkVersionedTableExists(dbTable.name, dbTable.version)) {
|
|
4601
|
+
console.log(`Versioned table ${physicalTableName} already exists, skipping creation`);
|
|
4602
|
+
} else {
|
|
4603
|
+
await this.createTableIfNotExistsNew(physicalTableName, allColumns);
|
|
4604
|
+
if (dbTable.version) {
|
|
4605
|
+
await this.registerTableVersion(dbTable.name, dbTable.version, physicalTableName);
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
if (dbTable.version && databaseSchema.reinitTable) {
|
|
4609
|
+
this.pendingReinitTables.push({
|
|
4610
|
+
tableName: physicalTableName,
|
|
4611
|
+
reinitFn: databaseSchema.reinitTable
|
|
4612
|
+
});
|
|
4613
|
+
}
|
|
4614
|
+
const legacyTable = {
|
|
4615
|
+
name: physicalTableName,
|
|
4616
|
+
primaryKey: allColumns.find((col) => col.storeData?.isPrimaryKey)?.name || "_id",
|
|
4617
|
+
columns: allColumns.map((col) => ({
|
|
4618
|
+
name: col.name,
|
|
4619
|
+
type: this.mapType(col.type, col.storeData),
|
|
4620
|
+
isOptional: col.storeData?.isNullable || false,
|
|
4621
|
+
default: col.defaultValue
|
|
4622
|
+
}))
|
|
4623
|
+
};
|
|
4624
|
+
this.tables.set(dbTable.name, legacyTable);
|
|
4625
|
+
processedTables.add(tableKey);
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
async createTableIfNotExistsNew(tableName, columns) {
|
|
4632
|
+
const createTableSQL = this.generateCreateTableSQL(tableName, columns);
|
|
4633
|
+
await this.db.exec(createTableSQL);
|
|
4634
|
+
}
|
|
4635
|
+
async createVersionCounterTable() {
|
|
4636
|
+
const sql = `
|
|
4637
|
+
CREATE TABLE IF NOT EXISTS __arc_version_counters (
|
|
4638
|
+
table_name TEXT PRIMARY KEY,
|
|
4639
|
+
last_version INTEGER NOT NULL DEFAULT 0
|
|
4640
|
+
)
|
|
4641
|
+
`;
|
|
4642
|
+
await this.db.exec(sql);
|
|
4643
|
+
}
|
|
4644
|
+
async createTableVersionsTable() {
|
|
4645
|
+
const sql = `
|
|
4646
|
+
CREATE TABLE IF NOT EXISTS __arc_table_versions (
|
|
4647
|
+
table_name TEXT NOT NULL,
|
|
4648
|
+
version INTEGER NOT NULL,
|
|
4649
|
+
physical_table_name TEXT NOT NULL,
|
|
4650
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
4651
|
+
is_active INTEGER DEFAULT 0,
|
|
4652
|
+
PRIMARY KEY (table_name, version)
|
|
4653
|
+
)
|
|
4654
|
+
`;
|
|
4655
|
+
await this.db.exec(sql);
|
|
4656
|
+
}
|
|
4657
|
+
getPhysicalTableName(logicalName, version) {
|
|
4658
|
+
return version ? `${logicalName}_v${version}` : logicalName;
|
|
4659
|
+
}
|
|
4660
|
+
async checkVersionedTableExists(logicalName, version) {
|
|
4661
|
+
const result = await this.db.exec("SELECT COUNT(*) as count FROM __arc_table_versions WHERE table_name = ? AND version = ?", [logicalName, version]);
|
|
4662
|
+
return result[0]?.count > 0;
|
|
4663
|
+
}
|
|
4664
|
+
async registerTableVersion(logicalName, version, physicalName) {
|
|
4665
|
+
await this.db.exec("INSERT INTO __arc_table_versions (table_name, version, physical_table_name, is_active) VALUES (?, ?, ?, ?)", [logicalName, version, physicalName, 1]);
|
|
4666
|
+
}
|
|
4667
|
+
hasVersioning(tableName) {
|
|
4668
|
+
const table = this.tables.get(tableName);
|
|
4669
|
+
if (!table)
|
|
4670
|
+
return false;
|
|
4671
|
+
return table.columns.some((col) => col.name === "__version");
|
|
4672
|
+
}
|
|
4673
|
+
hasSoftDelete(tableName) {
|
|
4674
|
+
const table = this.tables.get(tableName);
|
|
4675
|
+
if (!table)
|
|
4676
|
+
return false;
|
|
4677
|
+
return table.columns.some((col) => col.name === "deleted");
|
|
4678
|
+
}
|
|
4679
|
+
async executeReinitTables(dataStorage) {
|
|
4680
|
+
for (const { tableName, reinitFn } of this.pendingReinitTables) {
|
|
4681
|
+
await reinitFn(tableName, dataStorage);
|
|
4682
|
+
}
|
|
4683
|
+
this.pendingReinitTables = [];
|
|
4684
|
+
}
|
|
4685
|
+
readWriteTransaction(stores) {
|
|
4686
|
+
return new SQLiteReadWriteTransaction(this.db, this.tables, this);
|
|
4687
|
+
}
|
|
4688
|
+
readTransaction(stores) {
|
|
4689
|
+
return new SQLiteReadTransaction(this.db, this.tables, this);
|
|
4690
|
+
}
|
|
4691
|
+
async destroy() {
|
|
4692
|
+
for (const tableName of this.tables.keys()) {
|
|
4693
|
+
const table = this.tables.get(tableName);
|
|
4694
|
+
if (table) {
|
|
4695
|
+
await this.db.exec(`DROP TABLE IF EXISTS "${table.name}"`);
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
await this.db.exec("DROP TABLE IF EXISTS __arc_version_counters");
|
|
4699
|
+
await this.db.exec("DROP TABLE IF EXISTS __arc_table_versions");
|
|
4700
|
+
this.tables.clear();
|
|
4701
|
+
this.tableSchemas.clear();
|
|
4702
|
+
this.pendingReinitTables = [];
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
var createSQLiteAdapterFactory = (db) => {
|
|
4706
|
+
return async (context) => {
|
|
4707
|
+
const adapter = new SQLiteAdapter(db, context);
|
|
4708
|
+
await adapter.initialize();
|
|
4709
|
+
return adapter;
|
|
4710
|
+
};
|
|
4711
|
+
};
|
|
4712
|
+
|
|
4713
|
+
// src/bun-sqlite.ts
|
|
4714
|
+
class BunSQLiteDatabase {
|
|
4715
|
+
db;
|
|
4716
|
+
constructor(filename = ":memory:") {
|
|
4717
|
+
this.db = new Database(filename);
|
|
4718
|
+
this.db.exec("PRAGMA journal_mode=WAL");
|
|
4719
|
+
}
|
|
4720
|
+
async exec(sql, params) {
|
|
4721
|
+
try {
|
|
4722
|
+
if (params && params.length > 0) {
|
|
4723
|
+
const stmt = this.db.prepare(sql);
|
|
4724
|
+
if (sql.trim().toUpperCase().startsWith("SELECT")) {
|
|
4725
|
+
return stmt.all(...params);
|
|
4726
|
+
} else {
|
|
4727
|
+
stmt.run(...params);
|
|
4728
|
+
return [];
|
|
4729
|
+
}
|
|
4730
|
+
} else {
|
|
4731
|
+
const statements = sql.split(";").filter((s) => s.trim());
|
|
4732
|
+
let result = [];
|
|
4733
|
+
for (const statement of statements) {
|
|
4734
|
+
if (!statement.trim())
|
|
4735
|
+
continue;
|
|
4736
|
+
if (statement.trim().toUpperCase().startsWith("SELECT")) {
|
|
4737
|
+
result = this.db.prepare(statement).all();
|
|
4738
|
+
} else {
|
|
4739
|
+
this.db.exec(statement);
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
return result;
|
|
4743
|
+
}
|
|
4744
|
+
} catch (error) {
|
|
4745
|
+
console.error("SQL Error:", error, `
|
|
4746
|
+
SQL:`, sql, `
|
|
4747
|
+
Params:`, params);
|
|
4748
|
+
throw error;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
async execBatch(queries) {
|
|
4752
|
+
this.db.exec("BEGIN TRANSACTION");
|
|
4753
|
+
try {
|
|
4754
|
+
for (const query of queries) {
|
|
4755
|
+
if (query.params && query.params.length > 0) {
|
|
4756
|
+
this.db.prepare(query.sql).run(...query.params);
|
|
4757
|
+
} else {
|
|
4758
|
+
this.db.exec(query.sql);
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
this.db.exec("COMMIT");
|
|
4762
|
+
} catch (error) {
|
|
4763
|
+
this.db.exec("ROLLBACK");
|
|
4764
|
+
throw error;
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
close() {
|
|
4768
|
+
this.db.close();
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
var createBunSQLiteAdapterFactory = (filename = ":memory:") => {
|
|
4772
|
+
return async (context) => {
|
|
4773
|
+
const db = new BunSQLiteDatabase(filename);
|
|
4774
|
+
return createSQLiteAdapterFactory(db)(context);
|
|
4775
|
+
};
|
|
4776
|
+
};
|
|
4777
|
+
export {
|
|
4778
|
+
createSQLiteAdapterFactory,
|
|
4779
|
+
createBunSQLiteAdapterFactory,
|
|
4780
|
+
SQLiteAdapter,
|
|
4781
|
+
BunSQLiteDatabase
|
|
4782
|
+
};
|