@arcote.tech/arc 0.0.27 → 0.1.2
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/collection/collection.d.ts +28 -48
- package/dist/collection/queries/abstract-collection-query.d.ts +4 -6
- package/dist/collection/queries/{abstract-many-items.d.ts → find.d.ts} +8 -8
- package/dist/collection/queries/one-item.d.ts +1 -18
- package/dist/collection/query-builders/find-by-id.d.ts +2 -0
- package/dist/collection/query-builders/find-one.d.ts +2 -0
- package/dist/collection/query-builders/find.d.ts +13 -0
- package/dist/command/command.d.ts +29 -0
- package/dist/command/index.d.ts +2 -0
- package/dist/context/context.d.ts +34 -18
- package/dist/context/element.d.ts +36 -6
- package/dist/context/event.d.ts +39 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/query-builder-context.d.ts +15 -0
- package/dist/context/query-builders.d.ts +11 -2
- package/dist/context/query-cache.d.ts +9 -0
- package/dist/context/query.d.ts +5 -3
- package/dist/context/reactive-query.d.ts +23 -0
- package/dist/context/serializable-query.d.ts +11 -0
- package/dist/data-storage/store-state-fork.d.ts +2 -3
- package/dist/data-storage/store-state-master.d.ts +2 -5
- package/dist/data-storage/store-state.abstract.d.ts +2 -3
- package/dist/data-storage/types.d.ts +28 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/interface.d.ts +2 -17
- package/dist/db/sqliteAdapter.d.ts +39 -0
- package/dist/dist/index.d.ts +513 -0
- package/dist/elements/abstract-primitive.d.ts +0 -1
- package/dist/elements/abstract.d.ts +2 -1
- package/dist/elements/array.d.ts +61 -3
- package/dist/elements/class.d.ts +1 -24
- package/dist/elements/date.d.ts +35 -2
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/number.d.ts +21 -6
- package/dist/elements/object.d.ts +38 -11
- package/dist/elements/or.d.ts +1 -11
- package/dist/elements/record.d.ts +16 -4
- package/dist/elements/string-enum.d.ts +17 -3
- package/dist/elements/string.d.ts +74 -6
- package/dist/elements/utils/type-validator-builder.d.ts +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1481 -752
- package/dist/model/index.d.ts +2 -0
- package/dist/model/model.d.ts +44 -38
- package/dist/state/query-builder.d.ts +1 -8
- package/dist/state/query.d.ts +1 -14
- package/dist/state/state.d.ts +1 -31
- package/dist/tests/context/context.test.d.ts +2 -0
- package/dist/tests/pipe.d.ts +2 -0
- package/dist/tests/query/advance-query.test.d.ts +2 -0
- package/dist/tests/query/collection-all.test.d.ts +2 -0
- package/dist/tests/utils/expect-not-false.d.ts +2 -0
- package/dist/tests/utils/sqlite-adapter.d.ts +3 -0
- package/dist/tests/utils/test-model.d.ts +26 -0
- package/dist/tests/validations/array.test.d.ts +2 -0
- package/dist/tests/validations/date.test.d.ts +2 -0
- package/dist/tests/validations/record.test.d.ts +2 -0
- package/dist/tests/validations/string-enum.test.d.ts +2 -0
- package/dist/utils/arcObjectToStoreSchema.d.ts +4 -0
- package/dist/utils/murmur-hash.d.ts +2 -0
- package/dist/utils.d.ts +4 -4
- package/dist/view/view.d.ts +49 -0
- package/package.json +11 -4
- package/dist/collection/collection-change.d.ts +0 -18
- package/dist/collection/db.d.ts +0 -17
- package/dist/collection/queries/all-items.d.ts +0 -7
- package/dist/collection/queries/indexed.d.ts +0 -13
- package/dist/collection/query-builders/abstract-many-items.d.ts +0 -11
- package/dist/collection/query-builders/all-items.d.ts +0 -9
- package/dist/collection/query-builders/indexed.d.ts +0 -11
- package/dist/collection/query-builders/one-item.d.ts +0 -11
- package/dist/collection/utils/index-query.d.ts +0 -6
- package/dist/data-storage/DataStorage.d.ts +0 -11
- package/dist/data-storage/ForkStoreState.d.ts +0 -32
- package/dist/data-storage/StoreState.d.ts +0 -39
- package/dist/data-storage/data-storage.d.ts +0 -2
- package/dist/data-storage/data-storage.interface.d.ts +0 -46
- package/dist/data-storage/master-store-state.d.ts +0 -30
- package/dist/data-storage/store-state.d.ts +0 -39
- package/dist/elements/object copy.d.ts +0 -29
- package/dist/rtc/deserializeChanges.d.ts +0 -3
- package/dist/state/db.d.ts +0 -15
- package/dist/state/state-change.d.ts +0 -14
- package/dist/state/util.d.ts +0 -3
- package/dist/tests/query/query.test.d.ts +0 -2
- package/dist/tests/query-notification-optimization.test copy.d.ts +0 -2
- package/dist/tests/query-notification-optimization.test.d.ts +0 -2
- package/dist/tests/validation.test.d.ts +0 -2
- package/dist/tests/validations/validation.test.d.ts +0 -2
- package/dist/utils/deep-merge.d.ts +0 -6
- /package/dist/{elements/tests → tests/validations}/object.test.d.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// context/element.ts
|
|
2
2
|
class ArcContextElement {
|
|
3
3
|
$event;
|
|
4
|
+
name;
|
|
5
|
+
commandContext;
|
|
6
|
+
commandClient;
|
|
7
|
+
observer;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class ArcContextElementWithStore extends ArcContextElement {
|
|
4
11
|
}
|
|
5
12
|
|
|
6
13
|
// elements/optional.ts
|
|
@@ -26,7 +33,7 @@ class ArcOptional {
|
|
|
26
33
|
}
|
|
27
34
|
validate(value) {
|
|
28
35
|
if (!value)
|
|
29
|
-
return
|
|
36
|
+
return false;
|
|
30
37
|
return this.parent.validate(value);
|
|
31
38
|
}
|
|
32
39
|
}
|
|
@@ -90,7 +97,10 @@ class ArcDefault {
|
|
|
90
97
|
|
|
91
98
|
// elements/abstract.ts
|
|
92
99
|
class ArcAbstract {
|
|
93
|
-
validations
|
|
100
|
+
validations;
|
|
101
|
+
constructor(validations = []) {
|
|
102
|
+
this.validations = validations;
|
|
103
|
+
}
|
|
94
104
|
default(defaultValueOrCallback) {
|
|
95
105
|
return new ArcDefault(this, defaultValueOrCallback);
|
|
96
106
|
}
|
|
@@ -106,10 +116,18 @@ class ArcAbstract {
|
|
|
106
116
|
return newInstance;
|
|
107
117
|
}
|
|
108
118
|
validate(value) {
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
const errors = this.validations.reduce((acc, { name, validator }) => {
|
|
120
|
+
try {
|
|
121
|
+
acc[name] = validator(value);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
acc[name] = error;
|
|
124
|
+
}
|
|
111
125
|
return acc;
|
|
112
126
|
}, {});
|
|
127
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
128
|
+
return errors;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
113
131
|
}
|
|
114
132
|
pipeValidation(name, validator) {
|
|
115
133
|
const newInstance = this.clone();
|
|
@@ -123,9 +141,6 @@ class ArcAbstract {
|
|
|
123
141
|
|
|
124
142
|
// elements/abstract-primitive.ts
|
|
125
143
|
class ArcPrimitive extends ArcAbstract {
|
|
126
|
-
constructor() {
|
|
127
|
-
super();
|
|
128
|
-
}
|
|
129
144
|
serialize(value) {
|
|
130
145
|
return value;
|
|
131
146
|
}
|
|
@@ -137,8 +152,30 @@ class ArcPrimitive extends ArcAbstract {
|
|
|
137
152
|
}
|
|
138
153
|
}
|
|
139
154
|
|
|
155
|
+
// elements/utils/type-validator-builder.ts
|
|
156
|
+
function primitiveTypeComparatorStrategyFactory(type) {
|
|
157
|
+
return (value) => typeof value === type;
|
|
158
|
+
}
|
|
159
|
+
function typeValidatorBuilder(typeName, comparatorStrategy) {
|
|
160
|
+
const comparator = comparatorStrategy || primitiveTypeComparatorStrategyFactory(typeName);
|
|
161
|
+
return {
|
|
162
|
+
name: "type",
|
|
163
|
+
validator: (value) => {
|
|
164
|
+
const valueType = typeof value;
|
|
165
|
+
if (!comparator(value))
|
|
166
|
+
return { current: valueType, expected: typeName };
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
140
172
|
// elements/string.ts
|
|
173
|
+
var stringValidator = typeValidatorBuilder("string");
|
|
174
|
+
|
|
141
175
|
class ArcString extends ArcPrimitive {
|
|
176
|
+
constructor() {
|
|
177
|
+
super([stringValidator]);
|
|
178
|
+
}
|
|
142
179
|
minLength(min) {
|
|
143
180
|
return this.validation("minLength", (value) => {
|
|
144
181
|
if (value.length < min)
|
|
@@ -157,6 +194,87 @@ class ArcString extends ArcPrimitive {
|
|
|
157
194
|
};
|
|
158
195
|
});
|
|
159
196
|
}
|
|
197
|
+
length(number) {
|
|
198
|
+
return this.validation("length", (value) => {
|
|
199
|
+
if (value.length !== number) {
|
|
200
|
+
return {
|
|
201
|
+
currentLength: value.length,
|
|
202
|
+
length: number
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
includes(str) {
|
|
208
|
+
return this.validation("includes", (value) => {
|
|
209
|
+
if (!value.includes(str)) {
|
|
210
|
+
return {
|
|
211
|
+
currentValue: value,
|
|
212
|
+
includes: str
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
startsWith(str) {
|
|
218
|
+
return this.validation("startsWith", (value) => {
|
|
219
|
+
if (!value.startsWith(str)) {
|
|
220
|
+
return {
|
|
221
|
+
currentValue: value,
|
|
222
|
+
startsWith: str
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
endsWith(str) {
|
|
228
|
+
return this.validation("endsWith", (value) => {
|
|
229
|
+
if (!value.endsWith(str)) {
|
|
230
|
+
return {
|
|
231
|
+
currentValue: value,
|
|
232
|
+
endsWith: str
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
regex(regex) {
|
|
238
|
+
return this.validation("regex", (value) => {
|
|
239
|
+
if (!regex.test(value)) {
|
|
240
|
+
return {
|
|
241
|
+
currentValue: value,
|
|
242
|
+
regex
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
email() {
|
|
248
|
+
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}\])$/;
|
|
249
|
+
return this.validation("email", (value) => {
|
|
250
|
+
if (!regex.test(value)) {
|
|
251
|
+
return {
|
|
252
|
+
currentEmail: value
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
url() {
|
|
258
|
+
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-]*)?$/;
|
|
259
|
+
return this.validation("url", (value) => {
|
|
260
|
+
if (!regex.test(value)) {
|
|
261
|
+
return {
|
|
262
|
+
currentUrl: value
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
ip() {
|
|
268
|
+
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]?)$/;
|
|
269
|
+
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})$/;
|
|
270
|
+
return this.validation("ip", (value) => {
|
|
271
|
+
if (!(IPv4regex.test(value) || IPv6regex.test(value))) {
|
|
272
|
+
return {
|
|
273
|
+
currentIP: value
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
160
278
|
validation(name, validator) {
|
|
161
279
|
const instance = this.pipeValidation(name, validator);
|
|
162
280
|
return instance;
|
|
@@ -196,283 +314,571 @@ function customId(name, createFn) {
|
|
|
196
314
|
return new ArcCustomId(name, createFn);
|
|
197
315
|
}
|
|
198
316
|
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
lastResult;
|
|
202
|
-
listener;
|
|
203
|
-
}
|
|
317
|
+
// elements/object.ts
|
|
318
|
+
var objectValidator = typeValidatorBuilder("object");
|
|
204
319
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
for (const change of changes) {
|
|
226
|
-
const response = this.onChange(change);
|
|
227
|
-
if (response !== false) {
|
|
228
|
-
this.lastResult = response;
|
|
229
|
-
resultChanged = true;
|
|
320
|
+
class ArcObject extends ArcAbstract {
|
|
321
|
+
rawShape;
|
|
322
|
+
constructor(rawShape) {
|
|
323
|
+
super([
|
|
324
|
+
objectValidator,
|
|
325
|
+
{
|
|
326
|
+
name: "schema",
|
|
327
|
+
validator: (value) => {
|
|
328
|
+
const errors = Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
329
|
+
if (!element.validate) {
|
|
330
|
+
return acc;
|
|
331
|
+
}
|
|
332
|
+
acc[key] = element.validate(value[key]);
|
|
333
|
+
return acc;
|
|
334
|
+
}, {});
|
|
335
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
336
|
+
return errors;
|
|
337
|
+
}
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
230
340
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.nextResult(this.lastResult);
|
|
234
|
-
}
|
|
235
|
-
unsubscribe() {
|
|
236
|
-
this.store.unsubscribe(this.bindedChangeHandler);
|
|
341
|
+
]);
|
|
342
|
+
this.rawShape = rawShape;
|
|
237
343
|
}
|
|
238
|
-
|
|
239
|
-
this.
|
|
240
|
-
|
|
344
|
+
parse(value) {
|
|
345
|
+
return Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
346
|
+
acc[key] = element.parse(value[key]);
|
|
347
|
+
return acc;
|
|
348
|
+
}, {});
|
|
241
349
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
350
|
+
serialize(value) {
|
|
351
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
352
|
+
if (!this.rawShape[key]) {
|
|
353
|
+
acc[key] = value2;
|
|
354
|
+
} else {
|
|
355
|
+
acc[key] = this.rawShape[key].serialize(value2);
|
|
356
|
+
}
|
|
357
|
+
return acc;
|
|
358
|
+
}, {});
|
|
249
359
|
}
|
|
250
|
-
|
|
251
|
-
return
|
|
360
|
+
deserialize(value) {
|
|
361
|
+
return Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
362
|
+
key,
|
|
363
|
+
element.deserialize(value[key])
|
|
364
|
+
]));
|
|
252
365
|
}
|
|
253
|
-
|
|
254
|
-
|
|
366
|
+
deserializePath(path, value) {
|
|
367
|
+
if (path.length === 0) {
|
|
368
|
+
return this.deserialize(value);
|
|
369
|
+
}
|
|
370
|
+
const [key, ...restPath] = path;
|
|
371
|
+
const element = this.rawShape[key];
|
|
372
|
+
if (!element) {
|
|
373
|
+
console.warn(`No element found for key: ${key}`);
|
|
374
|
+
return value;
|
|
375
|
+
}
|
|
376
|
+
if (element instanceof ArcObject || element instanceof ArcArray) {
|
|
377
|
+
return element.deserializePath(restPath, value);
|
|
378
|
+
}
|
|
379
|
+
return element.deserialize(value);
|
|
255
380
|
}
|
|
256
|
-
|
|
257
|
-
return
|
|
381
|
+
parsePartial(value) {
|
|
382
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
383
|
+
acc[key] = this.rawShape[key].parse(value2);
|
|
384
|
+
return acc;
|
|
385
|
+
}, {});
|
|
258
386
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
constructor(collection, filterFn, sortFn) {
|
|
265
|
-
super(collection);
|
|
266
|
-
this.filterFn = filterFn;
|
|
267
|
-
this.sortFn = sortFn;
|
|
387
|
+
deserializePartial(value) {
|
|
388
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
389
|
+
acc[key] = this.rawShape[key].deserialize(value2);
|
|
390
|
+
return acc;
|
|
391
|
+
}, {});
|
|
268
392
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
275
|
-
if (isInLastResult && shouldBeInTheResult)
|
|
276
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
|
|
277
|
-
else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
|
|
278
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1));
|
|
279
|
-
else if (!isInLastResult && shouldBeInTheResult)
|
|
280
|
-
return this.createResult(lastResultAsArray.concat(change.item));
|
|
281
|
-
return false;
|
|
393
|
+
serializePartial(value) {
|
|
394
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
395
|
+
acc[key] = this.rawShape[key].serialize(value2);
|
|
396
|
+
return acc;
|
|
397
|
+
}, {});
|
|
282
398
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return new QueryCollectionResult(result.sort(this.sortFn));
|
|
286
|
-
return new QueryCollectionResult(result);
|
|
399
|
+
pick(...keys) {
|
|
400
|
+
return new ArcObject(Object.fromEntries(keys.map((key) => [key, this.rawShape[key]])));
|
|
287
401
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return this.createResult(result.filter(this.filterFn));
|
|
291
|
-
return this.createResult(result);
|
|
402
|
+
omit(...keys) {
|
|
403
|
+
return new ArcObject(Object.fromEntries(Object.entries(this.rawShape).filter(([key]) => !keys.includes(key))));
|
|
292
404
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return this.filterFn(item);
|
|
296
|
-
return true;
|
|
405
|
+
entries() {
|
|
406
|
+
return Object.entries(this.rawShape);
|
|
297
407
|
}
|
|
298
408
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
class ArcAllItemsQuery extends ArcManyItemsQuery {
|
|
302
|
-
async fetch(store) {
|
|
303
|
-
const results = await store.findAll(this.bindedChangeHandler);
|
|
304
|
-
return this.createFiltredResult(results);
|
|
305
|
-
}
|
|
409
|
+
function object(element) {
|
|
410
|
+
return new ArcObject(element);
|
|
306
411
|
}
|
|
307
412
|
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
}
|
|
413
|
+
// elements/array.ts
|
|
414
|
+
var arrayValidator = typeValidatorBuilder("array", Array.isArray);
|
|
311
415
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
416
|
+
class ArcArray extends ArcAbstract {
|
|
417
|
+
parent;
|
|
418
|
+
constructor(parent) {
|
|
419
|
+
super([
|
|
420
|
+
arrayValidator,
|
|
421
|
+
{
|
|
422
|
+
name: "schema",
|
|
423
|
+
validator: (values) => {
|
|
424
|
+
const errors = values.reduce((acc, value, index) => {
|
|
425
|
+
const error = parent.validate(value);
|
|
426
|
+
if (error)
|
|
427
|
+
acc[index] = error;
|
|
428
|
+
return acc;
|
|
429
|
+
}, {});
|
|
430
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
431
|
+
return errors;
|
|
432
|
+
}
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
323
435
|
}
|
|
324
|
-
|
|
325
|
-
|
|
436
|
+
]);
|
|
437
|
+
this.parent = parent;
|
|
326
438
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return this;
|
|
439
|
+
minLength(min) {
|
|
440
|
+
return this.validation("minLength", (value) => {
|
|
441
|
+
if (value.length < min)
|
|
442
|
+
return {
|
|
443
|
+
currentLength: value.length,
|
|
444
|
+
minLength: min
|
|
445
|
+
};
|
|
446
|
+
});
|
|
336
447
|
}
|
|
337
|
-
|
|
338
|
-
this.
|
|
339
|
-
|
|
448
|
+
maxLength(max) {
|
|
449
|
+
return this.validation("maxLength", (value) => {
|
|
450
|
+
if (value.length > max)
|
|
451
|
+
return {
|
|
452
|
+
currentLength: value.length,
|
|
453
|
+
maxLength: max
|
|
454
|
+
};
|
|
455
|
+
});
|
|
340
456
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.sortFn = collection.options.sort;
|
|
457
|
+
length(number) {
|
|
458
|
+
return this.validation("length", (value) => {
|
|
459
|
+
if (value.length !== number) {
|
|
460
|
+
return {
|
|
461
|
+
currentLength: value.length,
|
|
462
|
+
length: number
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
});
|
|
351
466
|
}
|
|
352
|
-
|
|
353
|
-
return
|
|
467
|
+
nonEmpty() {
|
|
468
|
+
return this.validation("nonEmpty", (value) => {
|
|
469
|
+
if (value.length === 0)
|
|
470
|
+
return { msg: "array is empty" };
|
|
471
|
+
});
|
|
354
472
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// db/index-query.ts
|
|
358
|
-
function indexQueryPredicate(item, query) {
|
|
359
|
-
if (!("$gt" in query) && !("$gte" in query) && !("$lt" in query) && !("$lte" in query)) {
|
|
360
|
-
return Object.entries(query).every(([key, value]) => item[key] === value);
|
|
361
|
-
}
|
|
362
|
-
if (query.$gt) {
|
|
363
|
-
const isGreaterThan = Object.entries(query.$gt).every(([key, value]) => item[key] > value);
|
|
364
|
-
if (!isGreaterThan)
|
|
365
|
-
return false;
|
|
473
|
+
parse(value) {
|
|
474
|
+
return value.map((v) => this.parent.parse(v));
|
|
366
475
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (!isGreaterThanOrEqual)
|
|
370
|
-
return false;
|
|
476
|
+
serialize(value) {
|
|
477
|
+
return value.map((v) => this.parent.serialize(v));
|
|
371
478
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
479
|
+
deserialize(value) {
|
|
480
|
+
if (!Array.isArray(value))
|
|
481
|
+
return [];
|
|
482
|
+
return value.map((v) => this.parent.deserialize(v));
|
|
376
483
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
484
|
+
deserializePath(path, value) {
|
|
485
|
+
if (path.length === 0) {
|
|
486
|
+
return this.deserialize(value);
|
|
487
|
+
}
|
|
488
|
+
if (this.parent instanceof ArcObject || this.parent instanceof ArcArray) {
|
|
489
|
+
return this.parent.deserializePath(path, value);
|
|
490
|
+
}
|
|
491
|
+
return this.parent.deserialize(value);
|
|
381
492
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
// collection/queries/indexed.ts
|
|
386
|
-
class ArcIndexedItemsQuery extends ArcManyItemsQuery {
|
|
387
|
-
index;
|
|
388
|
-
data;
|
|
389
|
-
filterFn;
|
|
390
|
-
sortFn;
|
|
391
|
-
constructor(collection, index, data, filterFn, sortFn) {
|
|
392
|
-
super(collection, filterFn, sortFn);
|
|
393
|
-
this.index = index;
|
|
394
|
-
this.data = data;
|
|
395
|
-
this.filterFn = filterFn;
|
|
396
|
-
this.sortFn = sortFn;
|
|
493
|
+
validation(name, validator) {
|
|
494
|
+
const instance = this.pipeValidation(name, validator);
|
|
495
|
+
return instance;
|
|
397
496
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
497
|
+
}
|
|
498
|
+
function array(element) {
|
|
499
|
+
return new ArcArray(element);
|
|
500
|
+
}
|
|
501
|
+
// elements/boolean.ts
|
|
502
|
+
class ArcBoolean extends ArcPrimitive {
|
|
503
|
+
}
|
|
504
|
+
function boolean() {
|
|
505
|
+
return new ArcBoolean;
|
|
506
|
+
}
|
|
507
|
+
// elements/date.ts
|
|
508
|
+
var dateValidator = typeValidatorBuilder("Date", (value) => value instanceof Date);
|
|
509
|
+
|
|
510
|
+
class ArcDate extends ArcAbstract {
|
|
511
|
+
constructor() {
|
|
512
|
+
super([dateValidator]);
|
|
402
513
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
514
|
+
parse(value) {
|
|
515
|
+
return new Date(value);
|
|
516
|
+
}
|
|
517
|
+
serialize(value) {
|
|
518
|
+
return value.getTime();
|
|
519
|
+
}
|
|
520
|
+
deserialize(value) {
|
|
521
|
+
return new Date(value);
|
|
522
|
+
}
|
|
523
|
+
after(after) {
|
|
524
|
+
return this.validation("after", (value) => {
|
|
525
|
+
if (value <= after)
|
|
526
|
+
return { current: value, after };
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
before(before) {
|
|
530
|
+
return this.validation("before", (value) => {
|
|
531
|
+
if (value >= before)
|
|
532
|
+
return { current: value, before };
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
validation(name, validator) {
|
|
536
|
+
const instance = this.pipeValidation(name, validator);
|
|
537
|
+
return instance;
|
|
406
538
|
}
|
|
407
539
|
}
|
|
540
|
+
function date() {
|
|
541
|
+
return new ArcDate;
|
|
542
|
+
}
|
|
543
|
+
// elements/number.ts
|
|
544
|
+
var numberValidator = typeValidatorBuilder("number");
|
|
408
545
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
546
|
+
class ArcNumber extends ArcPrimitive {
|
|
547
|
+
constructor() {
|
|
548
|
+
super([numberValidator]);
|
|
549
|
+
}
|
|
550
|
+
min(min) {
|
|
551
|
+
return this.validation("min", (value) => {
|
|
552
|
+
if (value < min)
|
|
553
|
+
return { current: value, min };
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
max(max) {
|
|
557
|
+
return this.validation("max", (value) => {
|
|
558
|
+
if (value > max)
|
|
559
|
+
return { current: value, max };
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
validation(name, validator) {
|
|
563
|
+
const instance = this.pipeValidation(name, validator);
|
|
564
|
+
return instance;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function number() {
|
|
568
|
+
return new ArcNumber;
|
|
569
|
+
}
|
|
570
|
+
// elements/record.ts
|
|
571
|
+
class ArcRecord extends ArcAbstract {
|
|
572
|
+
key;
|
|
573
|
+
element;
|
|
574
|
+
constructor(key, element) {
|
|
575
|
+
super([
|
|
576
|
+
{
|
|
577
|
+
name: "schema",
|
|
578
|
+
validator: (value) => {
|
|
579
|
+
const errors = Object.entries(value).reduce((acc, [key2, element2]) => {
|
|
580
|
+
const error = this.element.validate(element2);
|
|
581
|
+
if (error) {
|
|
582
|
+
acc[key2] = error;
|
|
583
|
+
}
|
|
584
|
+
return acc;
|
|
585
|
+
}, {});
|
|
586
|
+
if (Object.entries(errors).some(([key2, element2]) => !!(key2 || element2))) {
|
|
587
|
+
return errors;
|
|
588
|
+
}
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
]);
|
|
593
|
+
this.key = key;
|
|
594
|
+
this.element = element;
|
|
595
|
+
}
|
|
596
|
+
parse(value) {
|
|
597
|
+
if (!value)
|
|
598
|
+
return {};
|
|
599
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
600
|
+
acc[key] = this.element.parse(recordValue);
|
|
601
|
+
return acc;
|
|
602
|
+
}, {});
|
|
603
|
+
}
|
|
604
|
+
serialize(value) {
|
|
605
|
+
if (!value)
|
|
606
|
+
return {};
|
|
607
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
608
|
+
acc[key] = this.element.serialize(recordValue);
|
|
609
|
+
return acc;
|
|
610
|
+
}, {});
|
|
611
|
+
}
|
|
612
|
+
deserialize(value) {
|
|
613
|
+
if (!value)
|
|
614
|
+
return {};
|
|
615
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
616
|
+
acc[key] = this.element.deserialize(recordValue);
|
|
617
|
+
return acc;
|
|
618
|
+
}, {});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function record(key, element) {
|
|
622
|
+
return new ArcRecord(key, element);
|
|
623
|
+
}
|
|
624
|
+
// elements/string-enum.ts
|
|
625
|
+
class ArcStringEnum extends ArcAbstract {
|
|
626
|
+
values;
|
|
627
|
+
constructor(values) {
|
|
628
|
+
super([
|
|
629
|
+
{
|
|
630
|
+
name: "schema",
|
|
631
|
+
validator: (value) => {
|
|
632
|
+
if (this.values.includes(value))
|
|
633
|
+
return false;
|
|
634
|
+
return { expect: values, current: value };
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
]);
|
|
638
|
+
this.values = values;
|
|
639
|
+
}
|
|
640
|
+
parse(value) {
|
|
641
|
+
return value;
|
|
642
|
+
}
|
|
643
|
+
serialize(value) {
|
|
644
|
+
return value;
|
|
645
|
+
}
|
|
646
|
+
deserialize(value) {
|
|
647
|
+
return value;
|
|
648
|
+
}
|
|
649
|
+
getEnumerators() {
|
|
650
|
+
return this.values;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function stringEnum(...values) {
|
|
654
|
+
return new ArcStringEnum(values);
|
|
655
|
+
}
|
|
656
|
+
// utils/arcObjectToStoreSchema.ts
|
|
657
|
+
function getSQLiteType(element) {
|
|
658
|
+
if (element instanceof ArcDate) {
|
|
659
|
+
return "TEXT";
|
|
660
|
+
}
|
|
661
|
+
if (element instanceof ArcArray || element instanceof ArcObject) {
|
|
662
|
+
return "TEXT";
|
|
663
|
+
}
|
|
664
|
+
if (element instanceof ArcBoolean) {
|
|
665
|
+
return "INTEGER";
|
|
666
|
+
}
|
|
667
|
+
if (element instanceof ArcNumber) {
|
|
668
|
+
return "NUMBER";
|
|
669
|
+
}
|
|
670
|
+
return "TEXT";
|
|
671
|
+
}
|
|
672
|
+
function arcObjectToStoreSchema(tableName, schema) {
|
|
673
|
+
const columns = schema.entries().map(([name, element]) => ({
|
|
674
|
+
name,
|
|
675
|
+
type: getSQLiteType(element),
|
|
676
|
+
isOptional: element instanceof ArcOptional
|
|
677
|
+
}));
|
|
678
|
+
columns.unshift({
|
|
679
|
+
name: "_id",
|
|
680
|
+
type: "TEXT",
|
|
681
|
+
isOptional: false
|
|
682
|
+
}, {
|
|
683
|
+
name: "deleted",
|
|
684
|
+
type: "INTEGER",
|
|
685
|
+
isOptional: false,
|
|
686
|
+
default: 0
|
|
687
|
+
}, {
|
|
688
|
+
name: "lastUpdate",
|
|
689
|
+
type: "TEXT",
|
|
690
|
+
isOptional: false
|
|
691
|
+
});
|
|
692
|
+
return {
|
|
693
|
+
tables: [
|
|
694
|
+
{
|
|
695
|
+
name: tableName,
|
|
696
|
+
columns,
|
|
697
|
+
primaryKey: "_id"
|
|
698
|
+
}
|
|
699
|
+
]
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// context/query.ts
|
|
704
|
+
class ArcQuery {
|
|
705
|
+
lastResult;
|
|
706
|
+
listeners = new Set;
|
|
707
|
+
subscribe(listener) {
|
|
708
|
+
this.listeners.add(listener);
|
|
709
|
+
}
|
|
710
|
+
unsubscribe(listener) {
|
|
711
|
+
this.listeners.delete(listener);
|
|
712
|
+
}
|
|
713
|
+
nextResult(result) {
|
|
714
|
+
this.lastResult = result;
|
|
715
|
+
this.listeners.forEach((listener) => listener(result));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// context/serializable-query.ts
|
|
720
|
+
class ArcSerializableQuery extends ArcQuery {
|
|
721
|
+
params;
|
|
722
|
+
static key;
|
|
723
|
+
constructor(params) {
|
|
415
724
|
super();
|
|
725
|
+
this.params = params;
|
|
726
|
+
}
|
|
727
|
+
serialize() {
|
|
728
|
+
return {
|
|
729
|
+
key: this.constructor.key,
|
|
730
|
+
params: this.params
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// collection/queries/abstract-collection-query.ts
|
|
736
|
+
class ArcCollectionQuery extends ArcSerializableQuery {
|
|
737
|
+
collection;
|
|
738
|
+
bindedChangeHandler = this.changeHandler.bind(this);
|
|
739
|
+
store;
|
|
740
|
+
constructor(collection, params) {
|
|
741
|
+
super(params);
|
|
416
742
|
this.collection = collection;
|
|
417
|
-
this.index = index;
|
|
418
|
-
this.data = data;
|
|
419
|
-
if (collection.options.sort)
|
|
420
|
-
this.sortFn = collection.options.sort;
|
|
421
743
|
}
|
|
422
|
-
|
|
423
|
-
|
|
744
|
+
async run(dataStorage) {
|
|
745
|
+
const store = dataStorage.getStore(this.collection.name);
|
|
746
|
+
this.store = store;
|
|
747
|
+
const result = await this.fetch(store);
|
|
748
|
+
this.lastResult = result;
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
changeHandler(changes) {
|
|
752
|
+
let resultChanged = false;
|
|
753
|
+
for (const change of changes) {
|
|
754
|
+
const response = this.onChange(change);
|
|
755
|
+
if (response !== false) {
|
|
756
|
+
this.lastResult = response;
|
|
757
|
+
resultChanged = true;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (resultChanged)
|
|
761
|
+
this.nextResult(this.lastResult);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// collection/queries/find.ts
|
|
766
|
+
class QueryCollectionResult {
|
|
767
|
+
result;
|
|
768
|
+
constructor(result) {
|
|
769
|
+
this.result = result;
|
|
770
|
+
}
|
|
771
|
+
get(id3) {
|
|
772
|
+
return id3 ? this.result.find((r) => r._id === id3) : undefined;
|
|
773
|
+
}
|
|
774
|
+
map(callbackfn) {
|
|
775
|
+
return this.result.map(callbackfn);
|
|
776
|
+
}
|
|
777
|
+
toArray() {
|
|
778
|
+
return this.result;
|
|
424
779
|
}
|
|
425
780
|
}
|
|
426
781
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
782
|
+
class ArcFindQuery extends ArcCollectionQuery {
|
|
783
|
+
params;
|
|
784
|
+
constructor(collection, params) {
|
|
785
|
+
super(collection, params);
|
|
786
|
+
this.params = params;
|
|
787
|
+
}
|
|
788
|
+
async fetch(store) {
|
|
789
|
+
const results = await store.find(this.params, this.bindedChangeHandler);
|
|
790
|
+
return this.createResult(results);
|
|
791
|
+
}
|
|
792
|
+
checkItem(item) {
|
|
793
|
+
if (!this.params.where)
|
|
794
|
+
return true;
|
|
795
|
+
return Object.entries(this.params.where).every(([key, condition]) => {
|
|
796
|
+
const value = item[key];
|
|
797
|
+
if (typeof condition !== "object" || condition === null) {
|
|
798
|
+
return value === condition;
|
|
799
|
+
}
|
|
800
|
+
return Object.entries(condition).every(([operator, operatorValue]) => {
|
|
801
|
+
switch (operator) {
|
|
802
|
+
case "$eq":
|
|
803
|
+
return value === operatorValue;
|
|
804
|
+
case "$ne":
|
|
805
|
+
return value !== operatorValue;
|
|
806
|
+
case "$gt":
|
|
807
|
+
return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
|
|
808
|
+
case "$gte":
|
|
809
|
+
return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
|
|
810
|
+
case "$lt":
|
|
811
|
+
return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
|
|
812
|
+
case "$lte":
|
|
813
|
+
return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
|
|
814
|
+
case "$in":
|
|
815
|
+
return Array.isArray(operatorValue) && operatorValue.includes(value);
|
|
816
|
+
case "$nin":
|
|
817
|
+
return Array.isArray(operatorValue) && !operatorValue.includes(value);
|
|
818
|
+
case "$exists":
|
|
819
|
+
return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
|
|
820
|
+
default:
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
});
|
|
433
825
|
}
|
|
434
826
|
onChange(change) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
827
|
+
const lastResult = this.lastResult;
|
|
828
|
+
const lastResultAsArray = lastResult?.toArray() || [];
|
|
829
|
+
const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
|
|
830
|
+
const isInLastResult = index !== -1;
|
|
831
|
+
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
832
|
+
if (isInLastResult && shouldBeInTheResult)
|
|
833
|
+
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
|
|
834
|
+
else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
|
|
835
|
+
return this.createResult(lastResultAsArray.toSpliced(index, 1));
|
|
836
|
+
else if (!isInLastResult && shouldBeInTheResult)
|
|
837
|
+
return this.createResult(lastResultAsArray.concat(change.item));
|
|
441
838
|
return false;
|
|
442
839
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return result;
|
|
840
|
+
createResult(result) {
|
|
841
|
+
return new QueryCollectionResult(result);
|
|
446
842
|
}
|
|
447
843
|
}
|
|
448
844
|
|
|
449
|
-
// collection/query-builders/
|
|
450
|
-
class
|
|
845
|
+
// collection/query-builders/find.ts
|
|
846
|
+
class ArcFindQueryBuilder {
|
|
451
847
|
collection;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
848
|
+
queryContext;
|
|
849
|
+
options;
|
|
850
|
+
constructor(collection, queryContext, options) {
|
|
455
851
|
this.collection = collection;
|
|
456
|
-
this.
|
|
852
|
+
this.queryContext = queryContext;
|
|
853
|
+
this.options = options;
|
|
457
854
|
}
|
|
458
855
|
toQuery() {
|
|
459
|
-
return
|
|
856
|
+
return this.queryContext.cacheQuery(ArcFindQuery, [
|
|
857
|
+
this.collection,
|
|
858
|
+
this.options
|
|
859
|
+
]);
|
|
860
|
+
}
|
|
861
|
+
run() {
|
|
862
|
+
return this.queryContext.runQuery(this.toQuery());
|
|
460
863
|
}
|
|
461
864
|
}
|
|
462
865
|
|
|
463
866
|
// collection/collection.ts
|
|
464
|
-
class ArcCollection extends
|
|
867
|
+
class ArcCollection extends ArcContextElementWithStore {
|
|
465
868
|
name;
|
|
466
|
-
|
|
869
|
+
id;
|
|
467
870
|
schema;
|
|
468
871
|
options;
|
|
469
|
-
constructor(name,
|
|
872
|
+
constructor(name, id3, schema, options) {
|
|
470
873
|
super();
|
|
471
874
|
this.name = name;
|
|
472
|
-
this.id =
|
|
875
|
+
this.id = id3;
|
|
473
876
|
this.schema = schema;
|
|
474
877
|
this.options = options;
|
|
475
878
|
}
|
|
879
|
+
storeSchema() {
|
|
880
|
+
return arcObjectToStoreSchema(this.name, this.schema);
|
|
881
|
+
}
|
|
476
882
|
serialize(data) {
|
|
477
883
|
return {
|
|
478
884
|
_id: this.id.serialize(data._id),
|
|
@@ -485,22 +891,22 @@ class ArcCollection extends ArcContextElement {
|
|
|
485
891
|
...this.schema.deserialize(data)
|
|
486
892
|
};
|
|
487
893
|
}
|
|
488
|
-
queryBuilder() {
|
|
894
|
+
queryBuilder = (context) => {
|
|
489
895
|
return {
|
|
490
|
-
|
|
491
|
-
one: (id2) => new ArcOneItemQueryBuilder(this, id2)
|
|
896
|
+
find: (options) => new ArcFindQueryBuilder(this, context, options || {})
|
|
492
897
|
};
|
|
493
|
-
}
|
|
494
|
-
commandContext(dataStorage, publishEvent) {
|
|
898
|
+
};
|
|
899
|
+
commandContext = (dataStorage, publishEvent) => {
|
|
495
900
|
const store = dataStorage.getStore(this.name);
|
|
496
901
|
return {
|
|
497
902
|
add: async (data) => {
|
|
498
903
|
if (this.id instanceof ArcCustomId)
|
|
499
904
|
throw new Error("Collection with custom id not support 'add' method");
|
|
500
|
-
const
|
|
905
|
+
const id3 = this.id.generate();
|
|
501
906
|
const parsed = this.schema.parse(data);
|
|
502
907
|
const body = {
|
|
503
|
-
_id:
|
|
908
|
+
_id: id3,
|
|
909
|
+
lastUpdate: new Date().toISOString(),
|
|
504
910
|
...parsed
|
|
505
911
|
};
|
|
506
912
|
await store.set(body);
|
|
@@ -508,16 +914,16 @@ class ArcCollection extends ArcContextElement {
|
|
|
508
914
|
type: "set",
|
|
509
915
|
to: body
|
|
510
916
|
});
|
|
511
|
-
return { id:
|
|
917
|
+
return { id: id3 };
|
|
512
918
|
},
|
|
513
|
-
remove: async (
|
|
514
|
-
await store.remove(
|
|
919
|
+
remove: async (id3) => {
|
|
920
|
+
await store.remove(id3);
|
|
515
921
|
return { success: true };
|
|
516
922
|
},
|
|
517
|
-
set: async (
|
|
923
|
+
set: async (id3, data) => {
|
|
518
924
|
const parsed = this.schema.parse(data);
|
|
519
925
|
const body = {
|
|
520
|
-
_id:
|
|
926
|
+
_id: id3,
|
|
521
927
|
...parsed
|
|
522
928
|
};
|
|
523
929
|
await store.set(body);
|
|
@@ -527,15 +933,19 @@ class ArcCollection extends ArcContextElement {
|
|
|
527
933
|
});
|
|
528
934
|
return { success: true };
|
|
529
935
|
},
|
|
530
|
-
|
|
531
|
-
return store.
|
|
936
|
+
find: async (options) => {
|
|
937
|
+
return store.find(options);
|
|
532
938
|
},
|
|
533
|
-
|
|
534
|
-
|
|
939
|
+
findOne: async (where) => {
|
|
940
|
+
const result = await store.find({
|
|
941
|
+
where,
|
|
942
|
+
limit: 1
|
|
943
|
+
});
|
|
944
|
+
return result[0];
|
|
535
945
|
},
|
|
536
|
-
modify: async (
|
|
946
|
+
modify: async (id3, data) => {
|
|
537
947
|
const deserialized = this.schema.serializePartial(data);
|
|
538
|
-
const { from, to } = await store.modify(
|
|
948
|
+
const { from, to } = await store.modify(id3, deserialized);
|
|
539
949
|
await publishEvent({
|
|
540
950
|
type: "modify",
|
|
541
951
|
changes: deserialized,
|
|
@@ -543,8 +953,8 @@ class ArcCollection extends ArcContextElement {
|
|
|
543
953
|
to
|
|
544
954
|
});
|
|
545
955
|
},
|
|
546
|
-
edit: async (
|
|
547
|
-
const { from, to } = await store.mutate(
|
|
956
|
+
edit: async (id3, editCallback) => {
|
|
957
|
+
const { from, to } = await store.mutate(id3, editCallback);
|
|
548
958
|
await publishEvent({
|
|
549
959
|
type: "mutate",
|
|
550
960
|
from,
|
|
@@ -552,118 +962,385 @@ class ArcCollection extends ArcContextElement {
|
|
|
552
962
|
});
|
|
553
963
|
}
|
|
554
964
|
};
|
|
555
|
-
}
|
|
556
|
-
indexBy(indexes) {
|
|
557
|
-
return new ArcIndexedCollection(this.name, this.id, this.schema, this.options, indexes);
|
|
558
|
-
}
|
|
965
|
+
};
|
|
559
966
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
if (!(name in this.indexes)) {
|
|
575
|
-
throw new Error(`Index "${name}" not found in collection "${this.name}"`);
|
|
576
|
-
}
|
|
577
|
-
return (data) => dataStorage.getStore(this.name).findByIndex(name, data);
|
|
578
|
-
}
|
|
579
|
-
});
|
|
967
|
+
function collection(name, id3, schema, options = {}) {
|
|
968
|
+
return new ArcCollection(name, id3, schema, options);
|
|
969
|
+
}
|
|
970
|
+
// command/command.ts
|
|
971
|
+
class ArcCommand extends ArcContextElement {
|
|
972
|
+
name;
|
|
973
|
+
_description;
|
|
974
|
+
_params;
|
|
975
|
+
_result;
|
|
976
|
+
_elements;
|
|
977
|
+
_handler;
|
|
978
|
+
constructor(name) {
|
|
979
|
+
super();
|
|
980
|
+
this.name = name;
|
|
580
981
|
}
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
982
|
+
use(elements) {
|
|
983
|
+
const clone = this.clone();
|
|
984
|
+
clone._elements = elements;
|
|
985
|
+
return clone;
|
|
986
|
+
}
|
|
987
|
+
description(description) {
|
|
988
|
+
const clone = this.clone();
|
|
989
|
+
clone._description = description;
|
|
990
|
+
return clone;
|
|
991
|
+
}
|
|
992
|
+
withParams(schema) {
|
|
993
|
+
const clone = this.clone();
|
|
994
|
+
clone._params = schema instanceof ArcObject ? schema : object(schema);
|
|
995
|
+
return clone;
|
|
996
|
+
}
|
|
997
|
+
withResult(schema) {
|
|
998
|
+
const clone = this.clone();
|
|
999
|
+
clone._result = object(schema);
|
|
1000
|
+
return clone;
|
|
1001
|
+
}
|
|
1002
|
+
handle(handler) {
|
|
1003
|
+
const clone = this.clone();
|
|
1004
|
+
clone._handler = handler;
|
|
1005
|
+
return clone;
|
|
1006
|
+
}
|
|
1007
|
+
commandClient = (ctx) => {
|
|
1008
|
+
return Object.assign(async (params) => {
|
|
1009
|
+
if (this._handler)
|
|
1010
|
+
return this._handler?.(ctx, params);
|
|
1011
|
+
}, { params: this._params });
|
|
1012
|
+
};
|
|
1013
|
+
clone() {
|
|
1014
|
+
const clone = new ArcCommand(this.name);
|
|
1015
|
+
clone._description = this._description;
|
|
1016
|
+
clone._params = this._params;
|
|
1017
|
+
clone._result = this._result;
|
|
1018
|
+
clone._handler = this._handler;
|
|
1019
|
+
clone._elements = this._elements;
|
|
1020
|
+
return clone;
|
|
593
1021
|
}
|
|
594
1022
|
}
|
|
595
|
-
function
|
|
596
|
-
return new
|
|
1023
|
+
function command(name) {
|
|
1024
|
+
return new ArcCommand(name);
|
|
597
1025
|
}
|
|
598
1026
|
// context/context.ts
|
|
599
1027
|
class ArcContext {
|
|
600
|
-
version;
|
|
601
1028
|
elements;
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
elementsMap;
|
|
605
|
-
constructor(version, elements, commands, listeners) {
|
|
606
|
-
this.version = version;
|
|
1029
|
+
eventListeners;
|
|
1030
|
+
constructor(elements) {
|
|
607
1031
|
this.elements = elements;
|
|
608
|
-
this.
|
|
609
|
-
|
|
610
|
-
|
|
1032
|
+
this.eventListeners = new Map;
|
|
1033
|
+
elements.forEach((element) => {
|
|
1034
|
+
if (element.observer) {
|
|
1035
|
+
const handlers = element.observer();
|
|
1036
|
+
Object.entries(handlers).forEach(([alias, handler]) => {
|
|
1037
|
+
const listeners = this.eventListeners.get(alias) || [];
|
|
1038
|
+
listeners.push(handler);
|
|
1039
|
+
this.eventListeners.set(alias, listeners);
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
pipe(newElements) {
|
|
1045
|
+
return new ArcContext([
|
|
1046
|
+
...this.elements,
|
|
1047
|
+
...newElements.filter(Boolean)
|
|
1048
|
+
]);
|
|
611
1049
|
}
|
|
612
|
-
queryBuilder() {
|
|
1050
|
+
queryBuilder(queryContext) {
|
|
613
1051
|
return new Proxy({}, {
|
|
614
1052
|
get: (target, name) => {
|
|
615
|
-
const element = this.
|
|
1053
|
+
const element = this.elements.find((element2) => element2.name === name);
|
|
616
1054
|
if (!element) {
|
|
617
|
-
throw new Error(`
|
|
1055
|
+
throw new Error(`Element "${String(name)}" not found`);
|
|
1056
|
+
}
|
|
1057
|
+
if (!element.queryBuilder) {
|
|
1058
|
+
throw new Error(`Element "${String(name)}" does not have a query builder`);
|
|
618
1059
|
}
|
|
619
|
-
return element.queryBuilder();
|
|
1060
|
+
return element.queryBuilder(queryContext);
|
|
620
1061
|
}
|
|
621
1062
|
});
|
|
622
1063
|
}
|
|
623
|
-
commandContext(client, dataStorage
|
|
1064
|
+
commandContext(client, dataStorage) {
|
|
624
1065
|
return new Proxy({}, {
|
|
625
1066
|
get: (target, name) => {
|
|
626
1067
|
if (name === "$client") {
|
|
627
1068
|
return client;
|
|
628
1069
|
}
|
|
629
|
-
const element = this.
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
1070
|
+
const element = this.elements.find((element2) => element2.name === name);
|
|
1071
|
+
if (!element) {
|
|
1072
|
+
throw new Error(`Element "${String(name)}" not found`);
|
|
1073
|
+
}
|
|
1074
|
+
if (!element.commandContext) {
|
|
1075
|
+
throw new Error(`Element "${String(name)}" does not have a command context`);
|
|
1076
|
+
}
|
|
1077
|
+
return element.commandContext(dataStorage, async (event) => {
|
|
1078
|
+
const listeners = this.eventListeners.get(event.type) || [];
|
|
1079
|
+
await Promise.all(listeners.map((listener) => listener(event, dataStorage)));
|
|
1080
|
+
});
|
|
634
1081
|
}
|
|
635
1082
|
});
|
|
636
1083
|
}
|
|
637
|
-
commandsClient(client, dataStorage, catchErrorCallback) {
|
|
1084
|
+
commandsClient(client, queryContext, dataStorage, catchErrorCallback) {
|
|
638
1085
|
return new Proxy({}, {
|
|
639
1086
|
get: (_, name) => {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1087
|
+
const element = this.elements.find((element2) => element2.name === name);
|
|
1088
|
+
if (!element) {
|
|
1089
|
+
throw new Error(`Element "${String(name)}" not found`);
|
|
1090
|
+
}
|
|
1091
|
+
if (!element.commandClient) {
|
|
1092
|
+
throw new Error(`Element "${String(name)}" does not have a command client`);
|
|
1093
|
+
}
|
|
1094
|
+
return async (arg) => {
|
|
1095
|
+
const forkedDataStorage = dataStorage.fork();
|
|
1096
|
+
const commandContext = this.commandContext(client, forkedDataStorage);
|
|
1097
|
+
try {
|
|
1098
|
+
const result = await element.commandClient(commandContext)(arg);
|
|
1099
|
+
await forkedDataStorage.merge();
|
|
1100
|
+
return result;
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
console.log("error", error);
|
|
1103
|
+
catchErrorCallback(error);
|
|
1104
|
+
return error;
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
async function context(elementsPromise) {
|
|
1112
|
+
const elements = await Promise.all(elementsPromise.map(async (element) => {
|
|
1113
|
+
if (element instanceof Promise) {
|
|
1114
|
+
return (await element).default;
|
|
1115
|
+
}
|
|
1116
|
+
return element;
|
|
1117
|
+
})).then((elements2) => elements2.filter(Boolean));
|
|
1118
|
+
return new ArcContext(elements);
|
|
1119
|
+
}
|
|
1120
|
+
// context/event.ts
|
|
1121
|
+
var eventValidator = typeValidatorBuilder("object");
|
|
1122
|
+
var eventStoreSchema = {
|
|
1123
|
+
tables: [
|
|
1124
|
+
{
|
|
1125
|
+
name: "events",
|
|
1126
|
+
primaryKey: "id",
|
|
1127
|
+
columns: [
|
|
1128
|
+
{
|
|
1129
|
+
name: "id",
|
|
1130
|
+
type: "string"
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
name: "type",
|
|
1134
|
+
type: "string"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
name: "payload",
|
|
1138
|
+
type: "object"
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
name: "createdAt",
|
|
1142
|
+
type: "datetime"
|
|
658
1143
|
}
|
|
659
|
-
|
|
1144
|
+
]
|
|
1145
|
+
}
|
|
1146
|
+
]
|
|
1147
|
+
};
|
|
1148
|
+
var eventId = id("event");
|
|
1149
|
+
|
|
1150
|
+
class ArcEvent extends ArcContextElementWithStore {
|
|
1151
|
+
name;
|
|
1152
|
+
payload;
|
|
1153
|
+
storeSchema = () => eventStoreSchema;
|
|
1154
|
+
constructor(name, payload) {
|
|
1155
|
+
super();
|
|
1156
|
+
this.name = name;
|
|
1157
|
+
this.payload = payload;
|
|
1158
|
+
}
|
|
1159
|
+
commandContext = (dataStorage, publishEvent) => {
|
|
1160
|
+
return {
|
|
1161
|
+
emit: async (payload) => {
|
|
1162
|
+
const event = {
|
|
1163
|
+
id: eventId.generate(),
|
|
1164
|
+
type: this.name,
|
|
1165
|
+
payload,
|
|
1166
|
+
createdAt: new Date().toISOString()
|
|
1167
|
+
};
|
|
1168
|
+
await dataStorage.getStore("events").set(event);
|
|
1169
|
+
await publishEvent(event);
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function event(name, payload) {
|
|
1175
|
+
return new ArcEvent(name, payload ? new ArcObject(payload) : undefined);
|
|
1176
|
+
}
|
|
1177
|
+
// context/query-builder-context.ts
|
|
1178
|
+
class QueryBuilderContext {
|
|
1179
|
+
queryCache;
|
|
1180
|
+
dataStorage;
|
|
1181
|
+
usedQueries = new Set;
|
|
1182
|
+
constructor(queryCache, dataStorage) {
|
|
1183
|
+
this.queryCache = queryCache;
|
|
1184
|
+
this.dataStorage = dataStorage;
|
|
1185
|
+
}
|
|
1186
|
+
cacheQuery(QueryConstructor, args) {
|
|
1187
|
+
return new QueryConstructor(...args);
|
|
1188
|
+
}
|
|
1189
|
+
runQuery(query) {
|
|
1190
|
+
this.usedQueries.add(query);
|
|
1191
|
+
return query.run(this.dataStorage);
|
|
1192
|
+
}
|
|
1193
|
+
getUsedQueries() {
|
|
1194
|
+
const queries = Array.from(this.usedQueries);
|
|
1195
|
+
this.usedQueries.clear();
|
|
1196
|
+
return queries;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
// context/query-builders.ts
|
|
1200
|
+
class ArcQueryBuilder {
|
|
1201
|
+
queryContext;
|
|
1202
|
+
constructor(queryContext) {
|
|
1203
|
+
this.queryContext = queryContext;
|
|
1204
|
+
}
|
|
1205
|
+
run() {
|
|
1206
|
+
const query = this.toQuery();
|
|
1207
|
+
return this.queryContext.runQuery(query);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
// utils/murmur-hash.ts
|
|
1211
|
+
function murmurHash(key, seed = 0) {
|
|
1212
|
+
var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i;
|
|
1213
|
+
remainder = key.length & 3;
|
|
1214
|
+
bytes = key.length - remainder;
|
|
1215
|
+
h1 = seed;
|
|
1216
|
+
c1 = 3432918353;
|
|
1217
|
+
c2 = 461845907;
|
|
1218
|
+
i = 0;
|
|
1219
|
+
while (i < bytes) {
|
|
1220
|
+
k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
|
|
1221
|
+
++i;
|
|
1222
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
1223
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
1224
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
1225
|
+
h1 ^= k1;
|
|
1226
|
+
h1 = h1 << 13 | h1 >>> 19;
|
|
1227
|
+
h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
|
|
1228
|
+
h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
|
|
1229
|
+
}
|
|
1230
|
+
k1 = 0;
|
|
1231
|
+
switch (remainder) {
|
|
1232
|
+
case 3:
|
|
1233
|
+
k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
|
|
1234
|
+
case 2:
|
|
1235
|
+
k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
|
|
1236
|
+
case 1:
|
|
1237
|
+
k1 ^= key.charCodeAt(i) & 255;
|
|
1238
|
+
k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
|
|
1239
|
+
k1 = k1 << 15 | k1 >>> 17;
|
|
1240
|
+
k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
|
|
1241
|
+
h1 ^= k1;
|
|
1242
|
+
}
|
|
1243
|
+
h1 ^= key.length;
|
|
1244
|
+
h1 ^= h1 >>> 16;
|
|
1245
|
+
h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
|
|
1246
|
+
h1 ^= h1 >>> 13;
|
|
1247
|
+
h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
|
|
1248
|
+
h1 ^= h1 >>> 16;
|
|
1249
|
+
return h1 >>> 0;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// context/query-cache.ts
|
|
1253
|
+
class QueryCache {
|
|
1254
|
+
cache = new Map;
|
|
1255
|
+
getIndexHash(value) {
|
|
1256
|
+
if (typeof value === "number" || typeof value === "string" || typeof value === "function" || value === null || value === undefined) {
|
|
1257
|
+
return value;
|
|
1258
|
+
}
|
|
1259
|
+
return murmurHash(value);
|
|
1260
|
+
}
|
|
1261
|
+
set(index, query) {
|
|
1262
|
+
let current = this.cache;
|
|
1263
|
+
for (let i = 0;i < index.length - 1; i++) {
|
|
1264
|
+
const hashedIndex = this.getIndexHash(index[i]);
|
|
1265
|
+
if (!current.has(hashedIndex)) {
|
|
1266
|
+
current.set(hashedIndex, new Map);
|
|
1267
|
+
}
|
|
1268
|
+
current = current.get(hashedIndex);
|
|
1269
|
+
}
|
|
1270
|
+
const lastHashedIndex = this.getIndexHash(index[index.length - 1]);
|
|
1271
|
+
current.set(lastHashedIndex, query);
|
|
1272
|
+
}
|
|
1273
|
+
get(index) {
|
|
1274
|
+
let current = this.cache;
|
|
1275
|
+
for (let i = 0;i < index.length - 1; i++) {
|
|
1276
|
+
const hashedIndex = this.getIndexHash(index[i]);
|
|
1277
|
+
if (!current.has(hashedIndex)) {
|
|
660
1278
|
return;
|
|
661
1279
|
}
|
|
1280
|
+
current = current.get(hashedIndex);
|
|
1281
|
+
}
|
|
1282
|
+
const lastHashedIndex = this.getIndexHash(index[index.length - 1]);
|
|
1283
|
+
return current.get(lastHashedIndex);
|
|
1284
|
+
}
|
|
1285
|
+
debug() {
|
|
1286
|
+
return this.cache;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
// context/reactive-query.ts
|
|
1290
|
+
class ReactiveQuery extends ArcQuery {
|
|
1291
|
+
queryContext;
|
|
1292
|
+
queryBuilder;
|
|
1293
|
+
fn;
|
|
1294
|
+
queries = [];
|
|
1295
|
+
constructor(queryContext, queryBuilder, fn) {
|
|
1296
|
+
super();
|
|
1297
|
+
this.queryContext = queryContext;
|
|
1298
|
+
this.queryBuilder = queryBuilder;
|
|
1299
|
+
this.fn = fn;
|
|
1300
|
+
}
|
|
1301
|
+
run() {
|
|
1302
|
+
const promise = new Promise(async (resolve) => {
|
|
1303
|
+
const value = await this.runQuery();
|
|
1304
|
+
resolve(value);
|
|
1305
|
+
});
|
|
1306
|
+
return promise;
|
|
1307
|
+
}
|
|
1308
|
+
onChangeHandler = this.onChange.bind(this);
|
|
1309
|
+
async onChange() {
|
|
1310
|
+
const value = await this.runQuery();
|
|
1311
|
+
await this.nextResult(value);
|
|
1312
|
+
}
|
|
1313
|
+
async runQuery() {
|
|
1314
|
+
const value = await this.fn(this.queryBuilder);
|
|
1315
|
+
const queries = this.queryContext.getUsedQueries();
|
|
1316
|
+
this.queries.forEach((query) => {
|
|
1317
|
+
if (!queries.includes(query)) {
|
|
1318
|
+
query.unsubscribe(this.onChangeHandler);
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
queries.forEach((query) => {
|
|
1322
|
+
if (!this.queries.includes(query)) {
|
|
1323
|
+
query.subscribe(this.onChangeHandler);
|
|
1324
|
+
}
|
|
662
1325
|
});
|
|
1326
|
+
this.queries = queries;
|
|
1327
|
+
return value;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
class ReactiveQueryBuilder {
|
|
1332
|
+
queryBuilder;
|
|
1333
|
+
fn;
|
|
1334
|
+
constructor(queryBuilder, fn) {
|
|
1335
|
+
this.queryBuilder = queryBuilder;
|
|
1336
|
+
this.fn = fn;
|
|
1337
|
+
}
|
|
1338
|
+
toQuery(context2) {
|
|
1339
|
+
return new ReactiveQuery(context2, this.queryBuilder, this.fn);
|
|
663
1340
|
}
|
|
664
1341
|
}
|
|
665
|
-
function
|
|
666
|
-
return new
|
|
1342
|
+
function reactive(fn) {
|
|
1343
|
+
return (context2) => new ReactiveQueryBuilder(context2, fn);
|
|
667
1344
|
}
|
|
668
1345
|
// data-storage/data-storage.abstract.ts
|
|
669
1346
|
class DataStorage {
|
|
@@ -717,30 +1394,31 @@ class StoreState {
|
|
|
717
1394
|
};
|
|
718
1395
|
await this.applyChanges([change]);
|
|
719
1396
|
}
|
|
720
|
-
async remove(
|
|
1397
|
+
async remove(id3) {
|
|
721
1398
|
const change = {
|
|
722
1399
|
type: "delete",
|
|
723
|
-
id:
|
|
1400
|
+
id: id3
|
|
724
1401
|
};
|
|
725
1402
|
await this.applyChanges([change]);
|
|
726
1403
|
}
|
|
727
|
-
async modify(
|
|
1404
|
+
async modify(id3, data) {
|
|
728
1405
|
const change = {
|
|
729
1406
|
type: "modify",
|
|
730
|
-
id:
|
|
1407
|
+
id: id3,
|
|
731
1408
|
data
|
|
732
1409
|
};
|
|
733
1410
|
const { from, to } = await this.applyChange(change);
|
|
734
1411
|
return { from, to };
|
|
735
1412
|
}
|
|
736
|
-
async mutate(
|
|
737
|
-
const
|
|
738
|
-
const
|
|
1413
|
+
async mutate(id3, editCallback) {
|
|
1414
|
+
const result = await this.find({ where: { _id: id3 } });
|
|
1415
|
+
const object3 = result[0];
|
|
1416
|
+
const [draft, finalize] = create(object3 || {}, { enablePatches: true });
|
|
739
1417
|
await editCallback(draft);
|
|
740
1418
|
const [_, patches] = finalize();
|
|
741
1419
|
const change = {
|
|
742
1420
|
type: "mutate",
|
|
743
|
-
id:
|
|
1421
|
+
id: id3,
|
|
744
1422
|
patches
|
|
745
1423
|
};
|
|
746
1424
|
const { from, to } = await this.applyChange(change);
|
|
@@ -784,10 +1462,10 @@ class ForkedStoreState extends StoreState {
|
|
|
784
1462
|
};
|
|
785
1463
|
}
|
|
786
1464
|
if (change.type === "delete") {
|
|
787
|
-
const from = await this.
|
|
1465
|
+
const from = await this.find({ where: { _id: change.id } }).then((results) => results[0] || null);
|
|
788
1466
|
this.changedItems.set(change.id, null);
|
|
789
1467
|
return {
|
|
790
|
-
from
|
|
1468
|
+
from,
|
|
791
1469
|
to: null,
|
|
792
1470
|
event: {
|
|
793
1471
|
type: "delete",
|
|
@@ -797,7 +1475,9 @@ class ForkedStoreState extends StoreState {
|
|
|
797
1475
|
};
|
|
798
1476
|
}
|
|
799
1477
|
if (change.type === "modify") {
|
|
800
|
-
const existing = await this.
|
|
1478
|
+
const existing = await this.find({
|
|
1479
|
+
where: { _id: change.id }
|
|
1480
|
+
}).then((results) => results[0]);
|
|
801
1481
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
802
1482
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
803
1483
|
this.changedItems.set(change.id, item);
|
|
@@ -812,7 +1492,9 @@ class ForkedStoreState extends StoreState {
|
|
|
812
1492
|
};
|
|
813
1493
|
}
|
|
814
1494
|
if (change.type === "mutate") {
|
|
815
|
-
const existing = await this.
|
|
1495
|
+
const existing = await this.find({
|
|
1496
|
+
where: { _id: change.id }
|
|
1497
|
+
}).then((results) => results[0]);
|
|
816
1498
|
const updated = apply(existing || {}, change.patches);
|
|
817
1499
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
818
1500
|
this.changedItems.set(change.id, item);
|
|
@@ -829,59 +1511,42 @@ class ForkedStoreState extends StoreState {
|
|
|
829
1511
|
throw new Error("Unknown change type");
|
|
830
1512
|
}
|
|
831
1513
|
async applyChange(change) {
|
|
832
|
-
const { event, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
833
|
-
this.notifyListeners([
|
|
1514
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
1515
|
+
this.notifyListeners([event3]);
|
|
834
1516
|
return { from, to };
|
|
835
1517
|
}
|
|
836
1518
|
async applyChanges(changes) {
|
|
837
1519
|
const events = [];
|
|
838
1520
|
for (const change of changes) {
|
|
839
|
-
const { event } = await this.applyChangeAndReturnEvent(change);
|
|
840
|
-
if (
|
|
841
|
-
events.push(
|
|
1521
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(change);
|
|
1522
|
+
if (event3)
|
|
1523
|
+
events.push(event3);
|
|
842
1524
|
}
|
|
843
1525
|
if (events.length > 0) {
|
|
844
1526
|
this.notifyListeners(events);
|
|
845
1527
|
}
|
|
846
1528
|
}
|
|
847
|
-
async
|
|
848
|
-
if (listener)
|
|
849
|
-
this.listeners.set(listener, { callback: listener, id: id2 });
|
|
850
|
-
if (this.changedItems.has(id2))
|
|
851
|
-
return this.changedItems.get(id2);
|
|
852
|
-
return await this.master.findById(id2);
|
|
853
|
-
}
|
|
854
|
-
async findByIndex(index, data, listener) {
|
|
855
|
-
if (listener)
|
|
1529
|
+
async find(options, listener) {
|
|
1530
|
+
if (listener) {
|
|
856
1531
|
this.listeners.set(listener, { callback: listener });
|
|
857
|
-
|
|
1532
|
+
}
|
|
1533
|
+
const parentResults = await this.master.find(options);
|
|
858
1534
|
const results = new Map;
|
|
859
|
-
|
|
860
|
-
for (const [
|
|
1535
|
+
parentResults.forEach((item) => results.set(item._id, item));
|
|
1536
|
+
for (const [id3, changedItem] of this.changedItems) {
|
|
861
1537
|
if (changedItem === null) {
|
|
862
|
-
results.delete(
|
|
1538
|
+
results.delete(id3);
|
|
863
1539
|
continue;
|
|
864
1540
|
}
|
|
865
|
-
const matches = Object.entries(
|
|
1541
|
+
const matches = !options.where || Object.entries(options.where).every(([key, value]) => changedItem[key] === value);
|
|
866
1542
|
if (matches) {
|
|
867
|
-
results.set(
|
|
1543
|
+
results.set(id3, changedItem);
|
|
868
1544
|
} else {
|
|
869
|
-
results.delete(
|
|
1545
|
+
results.delete(id3);
|
|
870
1546
|
}
|
|
871
1547
|
}
|
|
872
1548
|
return Array.from(results.values());
|
|
873
1549
|
}
|
|
874
|
-
async findAll(listener) {
|
|
875
|
-
if (listener)
|
|
876
|
-
this.listeners.set(listener, { callback: listener });
|
|
877
|
-
const parentResult = await this.master.findAll();
|
|
878
|
-
return parentResult.map((item) => {
|
|
879
|
-
const id2 = item._id;
|
|
880
|
-
if (this.changedItems.has(id2))
|
|
881
|
-
return this.changedItems.get(id2);
|
|
882
|
-
return item;
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
1550
|
}
|
|
886
1551
|
|
|
887
1552
|
// data-storage/data-storage-forked.ts
|
|
@@ -920,8 +1585,6 @@ class ForkedDataStorage extends DataStorage {
|
|
|
920
1585
|
// data-storage/store-state-master.ts
|
|
921
1586
|
import { apply as apply2 } from "mutative";
|
|
922
1587
|
class MasterStoreState extends StoreState {
|
|
923
|
-
items = new Map;
|
|
924
|
-
isComplete = false;
|
|
925
1588
|
constructor(storeName, dataStorage, deserialize) {
|
|
926
1589
|
super(storeName, dataStorage, deserialize);
|
|
927
1590
|
}
|
|
@@ -929,7 +1592,6 @@ class MasterStoreState extends StoreState {
|
|
|
929
1592
|
if (change.type === "set") {
|
|
930
1593
|
await transaction.set(this.storeName, change.data);
|
|
931
1594
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
932
|
-
this.items.set(change.data._id, item);
|
|
933
1595
|
return {
|
|
934
1596
|
from: null,
|
|
935
1597
|
to: item,
|
|
@@ -942,7 +1604,6 @@ class MasterStoreState extends StoreState {
|
|
|
942
1604
|
}
|
|
943
1605
|
if (change.type === "delete") {
|
|
944
1606
|
await transaction.remove(this.storeName, change.id);
|
|
945
|
-
this.items.set(change.id, null);
|
|
946
1607
|
return {
|
|
947
1608
|
from: null,
|
|
948
1609
|
to: null,
|
|
@@ -954,11 +1615,10 @@ class MasterStoreState extends StoreState {
|
|
|
954
1615
|
};
|
|
955
1616
|
}
|
|
956
1617
|
if (change.type === "modify") {
|
|
957
|
-
const existing = await transaction.
|
|
1618
|
+
const existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
958
1619
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
959
1620
|
await transaction.set(this.storeName, updated);
|
|
960
1621
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
961
|
-
this.items.set(change.id, item);
|
|
962
1622
|
return {
|
|
963
1623
|
from: null,
|
|
964
1624
|
to: item,
|
|
@@ -970,11 +1630,10 @@ class MasterStoreState extends StoreState {
|
|
|
970
1630
|
};
|
|
971
1631
|
}
|
|
972
1632
|
if (change.type === "mutate") {
|
|
973
|
-
const existing = await transaction.
|
|
974
|
-
const updated = apply2(existing, change.patches);
|
|
1633
|
+
const existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
1634
|
+
const updated = apply2(existing || {}, change.patches);
|
|
975
1635
|
await transaction.set(this.storeName, updated);
|
|
976
1636
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
977
|
-
this.items.set(change.id, item);
|
|
978
1637
|
return {
|
|
979
1638
|
from: null,
|
|
980
1639
|
to: item,
|
|
@@ -989,77 +1648,31 @@ class MasterStoreState extends StoreState {
|
|
|
989
1648
|
}
|
|
990
1649
|
async applyChange(change) {
|
|
991
1650
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
992
|
-
const { event, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
993
|
-
|
|
1651
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
1652
|
+
await transaction.commit();
|
|
1653
|
+
this.notifyListeners([event3]);
|
|
994
1654
|
return { from, to };
|
|
995
1655
|
}
|
|
996
1656
|
async applyChanges(changes) {
|
|
997
1657
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
998
1658
|
const events = [];
|
|
999
1659
|
for (const change of changes) {
|
|
1000
|
-
const { event } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
1001
|
-
if (
|
|
1002
|
-
events.push(
|
|
1660
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
1661
|
+
if (event3)
|
|
1662
|
+
events.push(event3);
|
|
1003
1663
|
}
|
|
1004
1664
|
await transaction.commit();
|
|
1005
1665
|
if (events.length > 0) {
|
|
1006
1666
|
this.notifyListeners(events);
|
|
1007
1667
|
}
|
|
1008
1668
|
}
|
|
1009
|
-
async
|
|
1010
|
-
if (
|
|
1011
|
-
return;
|
|
1012
|
-
if (listener)
|
|
1013
|
-
this.listeners.set(listener, { callback: listener, id: id2 });
|
|
1014
|
-
if (this.items.has(id2))
|
|
1015
|
-
return this.items.get(id2);
|
|
1016
|
-
const transaction = await this.dataStorage.getReadTransaction();
|
|
1017
|
-
const result = await transaction.findById(this.storeName, id2);
|
|
1018
|
-
const item = result && this.deserialize ? this.deserialize(result) : result;
|
|
1019
|
-
if (!item)
|
|
1020
|
-
return;
|
|
1021
|
-
this.items.set(id2, item);
|
|
1022
|
-
return item;
|
|
1023
|
-
}
|
|
1024
|
-
async findByIndex(index, data, listener) {
|
|
1025
|
-
if (listener)
|
|
1669
|
+
async find(options, listener) {
|
|
1670
|
+
if (listener) {
|
|
1026
1671
|
this.listeners.set(listener, { callback: listener });
|
|
1027
|
-
if (this.isComplete) {
|
|
1028
|
-
const results2 = Array.from(this.items.values()).filter((item) => {
|
|
1029
|
-
if (!item)
|
|
1030
|
-
return false;
|
|
1031
|
-
return indexQueryPredicate(item, data);
|
|
1032
|
-
});
|
|
1033
|
-
return results2;
|
|
1034
1672
|
}
|
|
1035
1673
|
const transaction = await this.dataStorage.getReadTransaction();
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
const id2 = item._id;
|
|
1039
|
-
if (this.items.has(id2))
|
|
1040
|
-
return this.items.get(id2);
|
|
1041
|
-
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
1042
|
-
return processedItem;
|
|
1043
|
-
});
|
|
1044
|
-
return results;
|
|
1045
|
-
}
|
|
1046
|
-
async findAll(listener) {
|
|
1047
|
-
if (listener)
|
|
1048
|
-
this.listeners.set(listener, { callback: listener });
|
|
1049
|
-
if (this.isComplete)
|
|
1050
|
-
return Array.from(this.items.values()).filter((e) => !!e);
|
|
1051
|
-
const transaction = await this.dataStorage.getReadTransaction();
|
|
1052
|
-
const dbResults = await transaction.findAll(this.storeName);
|
|
1053
|
-
const items = dbResults.map((item) => {
|
|
1054
|
-
const id2 = item._id;
|
|
1055
|
-
if (this.items.has(id2))
|
|
1056
|
-
return this.items.get(id2);
|
|
1057
|
-
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
1058
|
-
this.items.set(processedItem._id, processedItem);
|
|
1059
|
-
return processedItem;
|
|
1060
|
-
});
|
|
1061
|
-
this.isComplete = true;
|
|
1062
|
-
return items;
|
|
1674
|
+
const results = await transaction.find(this.storeName, options);
|
|
1675
|
+
return results.map((item) => this.deserialize ? this.deserialize(item) : item);
|
|
1063
1676
|
}
|
|
1064
1677
|
}
|
|
1065
1678
|
|
|
@@ -1083,250 +1696,376 @@ class MasterDataStorage extends DataStorage {
|
|
|
1083
1696
|
}
|
|
1084
1697
|
getStore(storeName) {
|
|
1085
1698
|
if (!this.stores.has(storeName)) {
|
|
1086
|
-
const contextElement = this.arcContext.
|
|
1699
|
+
const contextElement = this.arcContext.elements.find((element) => element.name === storeName);
|
|
1087
1700
|
if (!contextElement)
|
|
1088
1701
|
console.log(`Can't find ${storeName} as context element`);
|
|
1089
|
-
this.stores.set(storeName, new MasterStoreState(storeName, this, contextElement && contextElement.deserialize ? (a) => contextElement.deserialize(a) : undefined));
|
|
1702
|
+
this.stores.set(storeName, new MasterStoreState(storeName, this, contextElement && "deserialize" in contextElement && typeof contextElement.deserialize === "function" ? (a) => contextElement.deserialize(a) : undefined));
|
|
1090
1703
|
}
|
|
1091
1704
|
return this.stores.get(storeName);
|
|
1092
1705
|
}
|
|
1093
1706
|
async applyChanges(changes) {
|
|
1094
1707
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
1095
1708
|
}
|
|
1096
|
-
applySerializedChanges(changes) {
|
|
1097
|
-
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
1098
|
-
}
|
|
1099
|
-
async commitChanges(changes) {
|
|
1100
|
-
await Promise.all([
|
|
1101
|
-
this.applyChanges(changes),
|
|
1102
|
-
this.rtcAdapter.commitChanges(changes)
|
|
1103
|
-
]);
|
|
1104
|
-
}
|
|
1105
|
-
fork() {
|
|
1106
|
-
return new ForkedDataStorage(this);
|
|
1107
|
-
}
|
|
1108
|
-
async sync(progressCallback) {
|
|
1109
|
-
await this.rtcAdapter.sync(progressCallback);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
// elements/object.ts
|
|
1113
|
-
class ArcObject extends ArcAbstract {
|
|
1114
|
-
rawShape;
|
|
1115
|
-
constructor(rawShape) {
|
|
1116
|
-
super();
|
|
1117
|
-
this.rawShape = rawShape;
|
|
1118
|
-
}
|
|
1119
|
-
parse(value) {
|
|
1120
|
-
return Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
1121
|
-
acc[key] = element.parse(value[key]);
|
|
1122
|
-
return acc;
|
|
1123
|
-
}, {});
|
|
1124
|
-
}
|
|
1125
|
-
serialize(value) {
|
|
1126
|
-
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1127
|
-
if (!this.rawShape[key]) {
|
|
1128
|
-
acc[key] = value2;
|
|
1129
|
-
} else {
|
|
1130
|
-
acc[key] = this.rawShape[key].serialize(value2);
|
|
1131
|
-
}
|
|
1132
|
-
return acc;
|
|
1133
|
-
}, {});
|
|
1134
|
-
}
|
|
1135
|
-
deserialize(value) {
|
|
1136
|
-
return Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
1137
|
-
key,
|
|
1138
|
-
element.deserialize(value[key])
|
|
1139
|
-
]));
|
|
1140
|
-
}
|
|
1141
|
-
deserializePath(path, value) {
|
|
1142
|
-
if (path.length === 0) {
|
|
1143
|
-
return this.deserialize(value);
|
|
1144
|
-
}
|
|
1145
|
-
const [key, ...restPath] = path;
|
|
1146
|
-
const element = this.rawShape[key];
|
|
1147
|
-
if (!element) {
|
|
1148
|
-
console.warn(`No element found for key: ${key}`);
|
|
1149
|
-
return value;
|
|
1150
|
-
}
|
|
1151
|
-
if (element instanceof ArcObject || element instanceof ArcArray) {
|
|
1152
|
-
return element.deserializePath(restPath, value);
|
|
1153
|
-
}
|
|
1154
|
-
return element.deserialize(value);
|
|
1155
|
-
}
|
|
1156
|
-
parsePartial(value) {
|
|
1157
|
-
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1158
|
-
acc[key] = this.rawShape[key].parse(value2);
|
|
1159
|
-
return acc;
|
|
1160
|
-
}, {});
|
|
1709
|
+
applySerializedChanges(changes) {
|
|
1710
|
+
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
1161
1711
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1712
|
+
async commitChanges(changes) {
|
|
1713
|
+
await Promise.all([
|
|
1714
|
+
this.applyChanges(changes),
|
|
1715
|
+
this.rtcAdapter.commitChanges(changes)
|
|
1716
|
+
]);
|
|
1167
1717
|
}
|
|
1168
|
-
|
|
1169
|
-
return
|
|
1170
|
-
acc[key] = this.rawShape[key].serialize(value2);
|
|
1171
|
-
return acc;
|
|
1172
|
-
}, {});
|
|
1718
|
+
fork() {
|
|
1719
|
+
return new ForkedDataStorage(this);
|
|
1173
1720
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1721
|
+
async sync(progressCallback) {
|
|
1722
|
+
await this.rtcAdapter.sync(progressCallback);
|
|
1176
1723
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1724
|
+
}
|
|
1725
|
+
// db/sqliteAdapter.ts
|
|
1726
|
+
class SQLiteReadTransaction {
|
|
1727
|
+
db;
|
|
1728
|
+
tables;
|
|
1729
|
+
constructor(db, tables) {
|
|
1730
|
+
this.db = db;
|
|
1731
|
+
this.tables = tables;
|
|
1732
|
+
}
|
|
1733
|
+
getId(store, id3) {
|
|
1734
|
+
return id3;
|
|
1735
|
+
}
|
|
1736
|
+
buildWhereClause(where) {
|
|
1737
|
+
if (!where) {
|
|
1738
|
+
return { sql: "deleted != 1", params: [] };
|
|
1739
|
+
}
|
|
1740
|
+
const conditions = ["deleted != 1"];
|
|
1741
|
+
const params = [];
|
|
1742
|
+
Object.entries(where).forEach(([key, value]) => {
|
|
1743
|
+
if (typeof value === "object" && value !== null) {
|
|
1744
|
+
Object.entries(value).forEach(([operator, operand]) => {
|
|
1745
|
+
switch (operator) {
|
|
1746
|
+
case "$eq":
|
|
1747
|
+
case "$ne":
|
|
1748
|
+
case "$gt":
|
|
1749
|
+
case "$gte":
|
|
1750
|
+
case "$lt":
|
|
1751
|
+
case "$lte":
|
|
1752
|
+
conditions.push(`${key} ${this.getOperatorSymbol(operator)} ?`);
|
|
1753
|
+
params.push(operand);
|
|
1754
|
+
break;
|
|
1755
|
+
case "$in":
|
|
1756
|
+
case "$nin":
|
|
1757
|
+
if (Array.isArray(operand)) {
|
|
1758
|
+
conditions.push(`${key} ${operator === "$in" ? "IN" : "NOT IN"} (${operand.map(() => "?").join(", ")})`);
|
|
1759
|
+
params.push(...operand);
|
|
1760
|
+
}
|
|
1761
|
+
break;
|
|
1762
|
+
case "$exists":
|
|
1763
|
+
if (typeof operand === "boolean") {
|
|
1764
|
+
conditions.push(operand ? `${key} IS NOT NULL` : `${key} IS NULL`);
|
|
1765
|
+
}
|
|
1766
|
+
break;
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
} else {
|
|
1770
|
+
conditions.push(`${key} = ?`);
|
|
1771
|
+
params.push(value);
|
|
1182
1772
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1773
|
+
});
|
|
1774
|
+
return {
|
|
1775
|
+
sql: conditions.join(" AND "),
|
|
1776
|
+
params
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
getOperatorSymbol(operator) {
|
|
1780
|
+
const operators = {
|
|
1781
|
+
$eq: "=",
|
|
1782
|
+
$ne: "!=",
|
|
1783
|
+
$gt: ">",
|
|
1784
|
+
$gte: ">=",
|
|
1785
|
+
$lt: "<",
|
|
1786
|
+
$lte: "<="
|
|
1787
|
+
};
|
|
1788
|
+
return operators[operator] || "=";
|
|
1789
|
+
}
|
|
1790
|
+
buildOrderByClause(orderBy) {
|
|
1791
|
+
if (!orderBy)
|
|
1792
|
+
return "";
|
|
1793
|
+
const orderClauses = Object.entries(orderBy).map(([key, direction]) => `${key} ${direction.toUpperCase()}`).join(", ");
|
|
1794
|
+
return orderClauses ? `ORDER BY ${orderClauses}` : "";
|
|
1795
|
+
}
|
|
1796
|
+
async find(store, options) {
|
|
1797
|
+
const { where, limit, offset, orderBy } = options || {};
|
|
1798
|
+
const whereClause = this.buildWhereClause(where);
|
|
1799
|
+
const orderByClause = this.buildOrderByClause(orderBy);
|
|
1800
|
+
const query2 = `
|
|
1801
|
+
SELECT *
|
|
1802
|
+
FROM ${store}
|
|
1803
|
+
WHERE ${whereClause.sql}
|
|
1804
|
+
${orderByClause}
|
|
1805
|
+
${limit ? `LIMIT ${limit}` : ""}
|
|
1806
|
+
${offset ? `OFFSET ${offset}` : ""}
|
|
1807
|
+
`;
|
|
1808
|
+
return await this.db.exec(query2, whereClause.params);
|
|
1187
1809
|
}
|
|
1188
|
-
}
|
|
1189
|
-
function object(element) {
|
|
1190
|
-
return new ArcObject(element);
|
|
1191
1810
|
}
|
|
1192
1811
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
return value.map((v) => this.parent.parse(v));
|
|
1202
|
-
}
|
|
1203
|
-
serialize(value) {
|
|
1204
|
-
return value.map((v) => this.parent.serialize(v));
|
|
1205
|
-
}
|
|
1206
|
-
deserialize(value) {
|
|
1207
|
-
if (!Array.isArray(value))
|
|
1208
|
-
return [];
|
|
1209
|
-
return value.map((v) => this.parent.deserialize(v));
|
|
1812
|
+
class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
|
|
1813
|
+
async remove(store, id3) {
|
|
1814
|
+
const table = this.tables.get(store);
|
|
1815
|
+
if (!table) {
|
|
1816
|
+
throw new Error(`Store ${store} not found`);
|
|
1817
|
+
}
|
|
1818
|
+
const query2 = `UPDATE ${store} SET deleted = 1, lastUpdate = ? WHERE ${table.primaryKey} = ?`;
|
|
1819
|
+
await this.db.exec(query2, [new Date().toISOString(), id3]);
|
|
1210
1820
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1821
|
+
async set(store, item) {
|
|
1822
|
+
const schema = this.tables.get(store);
|
|
1823
|
+
if (!schema) {
|
|
1824
|
+
throw new Error(`Store ${store} not found`);
|
|
1214
1825
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1826
|
+
const now = new Date().toISOString();
|
|
1827
|
+
const columnNames = schema.columns.map((col) => col.name);
|
|
1828
|
+
const values = schema.columns.map((column) => {
|
|
1829
|
+
let value = item[column.name];
|
|
1830
|
+
if (value === undefined && column.default !== undefined) {
|
|
1831
|
+
value = column.default;
|
|
1832
|
+
}
|
|
1833
|
+
return this.serializeValue(value);
|
|
1834
|
+
});
|
|
1835
|
+
const placeholders = columnNames.map(() => "?").join(", ");
|
|
1836
|
+
const sql = `
|
|
1837
|
+
INSERT OR REPLACE INTO ${schema.name}
|
|
1838
|
+
(${columnNames.join(", ")})
|
|
1839
|
+
VALUES (${placeholders})
|
|
1840
|
+
`;
|
|
1841
|
+
await this.db.exec(sql, values);
|
|
1842
|
+
}
|
|
1843
|
+
async commit() {
|
|
1844
|
+
return Promise.resolve();
|
|
1845
|
+
}
|
|
1846
|
+
serializeValue(value) {
|
|
1847
|
+
if (value === null || value === undefined)
|
|
1848
|
+
return null;
|
|
1849
|
+
if (value instanceof Date) {
|
|
1850
|
+
return value.toISOString();
|
|
1217
1851
|
}
|
|
1218
|
-
|
|
1852
|
+
if (Array.isArray(value) || typeof value === "object") {
|
|
1853
|
+
return JSON.stringify(value);
|
|
1854
|
+
}
|
|
1855
|
+
return value;
|
|
1219
1856
|
}
|
|
1220
1857
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
return new Date(value);
|
|
1237
|
-
}
|
|
1238
|
-
serialize(value) {
|
|
1239
|
-
return value.getTime();
|
|
1858
|
+
|
|
1859
|
+
class SQLiteAdapter {
|
|
1860
|
+
db;
|
|
1861
|
+
context;
|
|
1862
|
+
tables = new Map;
|
|
1863
|
+
constructor(db, context3) {
|
|
1864
|
+
this.db = db;
|
|
1865
|
+
this.context = context3;
|
|
1866
|
+
this.context.elements.forEach((element) => {
|
|
1867
|
+
if ("storeSchema" in element && typeof element.storeSchema === "function") {
|
|
1868
|
+
element.storeSchema().tables.forEach((table) => {
|
|
1869
|
+
this.tables.set(table.name, table);
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1240
1873
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1874
|
+
async initialize() {
|
|
1875
|
+
const stores = new Set;
|
|
1876
|
+
for (const element of this.context.elements) {
|
|
1877
|
+
if ("storeSchema" in element && typeof element.storeSchema === "function") {
|
|
1878
|
+
const schema = element.storeSchema();
|
|
1879
|
+
stores.add(schema);
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
for (const schema of stores) {
|
|
1883
|
+
for (const table of schema.tables) {
|
|
1884
|
+
await this.createTableIfNotExists(table);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1243
1887
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1888
|
+
async createTableIfNotExists(table) {
|
|
1889
|
+
const columns = table.columns.map((column) => {
|
|
1890
|
+
const constraints = [];
|
|
1891
|
+
if (!column.isOptional) {
|
|
1892
|
+
constraints.push("NOT NULL");
|
|
1893
|
+
}
|
|
1894
|
+
if (column.default !== undefined) {
|
|
1895
|
+
constraints.push(`DEFAULT ${JSON.stringify(column.default)}`);
|
|
1896
|
+
}
|
|
1897
|
+
if (column.name === table.primaryKey) {
|
|
1898
|
+
constraints.push("PRIMARY KEY");
|
|
1899
|
+
}
|
|
1900
|
+
return `"${column.name}" ${column.type} ${constraints.join(" ")}`.trim();
|
|
1254
1901
|
});
|
|
1902
|
+
const createTableSQL = `
|
|
1903
|
+
CREATE TABLE IF NOT EXISTS ${table.name} (
|
|
1904
|
+
${columns.join(`,
|
|
1905
|
+
`)}
|
|
1906
|
+
)
|
|
1907
|
+
`;
|
|
1908
|
+
await this.db.exec(createTableSQL);
|
|
1255
1909
|
}
|
|
1256
|
-
|
|
1257
|
-
return this.
|
|
1258
|
-
if (value > max)
|
|
1259
|
-
return { current: value, max };
|
|
1260
|
-
});
|
|
1910
|
+
readWriteTransaction(stores) {
|
|
1911
|
+
return new SQLiteReadWriteTransaction(this.db, this.tables);
|
|
1261
1912
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
return instance;
|
|
1913
|
+
readTransaction(stores) {
|
|
1914
|
+
return new SQLiteReadTransaction(this.db, this.tables);
|
|
1265
1915
|
}
|
|
1266
1916
|
}
|
|
1267
|
-
|
|
1268
|
-
return
|
|
1917
|
+
var createSQLiteAdapterFactory = (db) => {
|
|
1918
|
+
return async (context3) => {
|
|
1919
|
+
const adapter = new SQLiteAdapter(db, context3);
|
|
1920
|
+
await adapter.initialize();
|
|
1921
|
+
return adapter;
|
|
1922
|
+
};
|
|
1923
|
+
};
|
|
1924
|
+
// model/model.ts
|
|
1925
|
+
class ModelBase {
|
|
1269
1926
|
}
|
|
1270
|
-
|
|
1271
|
-
class
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1927
|
+
|
|
1928
|
+
class Model extends ModelBase {
|
|
1929
|
+
context;
|
|
1930
|
+
dataStorage;
|
|
1931
|
+
client;
|
|
1932
|
+
catchErrorCallback;
|
|
1933
|
+
queryCache = new QueryCache;
|
|
1934
|
+
constructor(context3, dataStorage, client, catchErrorCallback) {
|
|
1275
1935
|
super();
|
|
1276
|
-
this.
|
|
1277
|
-
this.
|
|
1936
|
+
this.context = context3;
|
|
1937
|
+
this.dataStorage = dataStorage;
|
|
1938
|
+
this.client = client;
|
|
1939
|
+
this.catchErrorCallback = catchErrorCallback;
|
|
1940
|
+
}
|
|
1941
|
+
async query(queryBuilderFn) {
|
|
1942
|
+
const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
|
|
1943
|
+
const queryBuilder = this.context.queryBuilder(queryContext);
|
|
1944
|
+
const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
|
|
1945
|
+
return query2.run(this.dataStorage);
|
|
1946
|
+
}
|
|
1947
|
+
subscribe(queryBuilderFn, callback) {
|
|
1948
|
+
const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
|
|
1949
|
+
const queryBuilder = this.context.queryBuilder(queryContext);
|
|
1950
|
+
const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
|
|
1951
|
+
query2.subscribe(callback);
|
|
1952
|
+
const runPromise = query2.run(this.dataStorage);
|
|
1953
|
+
runPromise.then((result) => {
|
|
1954
|
+
callback(result);
|
|
1955
|
+
});
|
|
1956
|
+
return {
|
|
1957
|
+
unsubscribe: () => {
|
|
1958
|
+
query2.unsubscribe(callback);
|
|
1959
|
+
},
|
|
1960
|
+
result: runPromise
|
|
1961
|
+
};
|
|
1278
1962
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1283
|
-
acc[key] = this.element.parse(recordValue);
|
|
1284
|
-
return acc;
|
|
1285
|
-
}, {});
|
|
1963
|
+
commands() {
|
|
1964
|
+
const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
|
|
1965
|
+
return this.context.commandsClient(this.client, queryContext, this.dataStorage, this.catchErrorCallback);
|
|
1286
1966
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
return {};
|
|
1290
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1291
|
-
acc[key] = this.element.serialize(recordValue);
|
|
1292
|
-
return acc;
|
|
1293
|
-
}, {});
|
|
1967
|
+
fork() {
|
|
1968
|
+
return new ForkedModel(this.context, this.dataStorage.fork(), this.client, this.catchErrorCallback);
|
|
1294
1969
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
return {};
|
|
1298
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1299
|
-
acc[key] = this.element.deserialize(recordValue);
|
|
1300
|
-
return acc;
|
|
1301
|
-
}, {});
|
|
1970
|
+
get $debug() {
|
|
1971
|
+
return {};
|
|
1302
1972
|
}
|
|
1303
1973
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1974
|
+
|
|
1975
|
+
class ForkedModel extends Model {
|
|
1976
|
+
constructor(context3, dataStorage, client, catchErrorCallback) {
|
|
1977
|
+
super(context3, dataStorage, client, catchErrorCallback);
|
|
1978
|
+
}
|
|
1979
|
+
merge() {}
|
|
1306
1980
|
}
|
|
1307
|
-
|
|
1308
|
-
class
|
|
1309
|
-
|
|
1310
|
-
|
|
1981
|
+
|
|
1982
|
+
class RemoteModelClient extends ModelBase {
|
|
1983
|
+
context;
|
|
1984
|
+
apiBaseUrl;
|
|
1985
|
+
client;
|
|
1986
|
+
catchErrorCallback;
|
|
1987
|
+
constructor(context3, apiBaseUrl, client, catchErrorCallback) {
|
|
1311
1988
|
super();
|
|
1312
|
-
this.
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1989
|
+
this.context = context3;
|
|
1990
|
+
this.apiBaseUrl = apiBaseUrl;
|
|
1991
|
+
this.client = client;
|
|
1992
|
+
this.catchErrorCallback = catchErrorCallback;
|
|
1993
|
+
}
|
|
1994
|
+
async query(queryBuilderFn) {
|
|
1995
|
+
const queryTracking = {
|
|
1996
|
+
element: null,
|
|
1997
|
+
queryType: null,
|
|
1998
|
+
params: null
|
|
1999
|
+
};
|
|
2000
|
+
const queryContext = new QueryBuilderContext(new QueryCache, null);
|
|
2001
|
+
const originalQueryBuilder = this.context.queryBuilder(queryContext);
|
|
2002
|
+
const queryBuilderProxy = new Proxy(originalQueryBuilder, {
|
|
2003
|
+
get: (target, prop) => {
|
|
2004
|
+
queryTracking.element = prop;
|
|
2005
|
+
const originalElement = Reflect.get(target, prop);
|
|
2006
|
+
return new Proxy(originalElement, {
|
|
2007
|
+
get: (elementTarget, methodProp) => {
|
|
2008
|
+
queryTracking.queryType = methodProp;
|
|
2009
|
+
const originalMethod = Reflect.get(elementTarget, methodProp);
|
|
2010
|
+
return (...args) => {
|
|
2011
|
+
queryTracking.params = args;
|
|
2012
|
+
return originalMethod(...args);
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
});
|
|
2018
|
+
const query2 = queryBuilderFn(queryBuilderProxy).toQuery(queryContext);
|
|
2019
|
+
const response = await fetch(`${this.apiBaseUrl}/query`, {
|
|
2020
|
+
method: "POST",
|
|
2021
|
+
headers: { "Content-Type": "application/json" },
|
|
2022
|
+
body: JSON.stringify({
|
|
2023
|
+
query: {
|
|
2024
|
+
element: queryTracking.element,
|
|
2025
|
+
queryType: queryTracking.queryType,
|
|
2026
|
+
params: queryTracking.params
|
|
2027
|
+
}
|
|
2028
|
+
})
|
|
2029
|
+
});
|
|
2030
|
+
if (!response.ok) {
|
|
2031
|
+
throw new Error(`Query failed: ${response.statusText}`);
|
|
2032
|
+
}
|
|
2033
|
+
const { result } = await response.json();
|
|
2034
|
+
return result;
|
|
1319
2035
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
2036
|
+
subscribe(queryBuilderFn, callback) {
|
|
2037
|
+
const result = this.query(queryBuilderFn);
|
|
2038
|
+
result.then((initialResult) => {
|
|
2039
|
+
callback(initialResult);
|
|
2040
|
+
}).catch(this.catchErrorCallback);
|
|
2041
|
+
return {
|
|
2042
|
+
unsubscribe: () => {},
|
|
2043
|
+
result
|
|
2044
|
+
};
|
|
1322
2045
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
2046
|
+
commands() {
|
|
2047
|
+
const commandsProxy = new Proxy({}, {
|
|
2048
|
+
get: (target, prop) => {
|
|
2049
|
+
const element = this.context.elements.find((e) => e.name === prop);
|
|
2050
|
+
if (!element) {
|
|
2051
|
+
throw new Error(`Command ${String(prop)} not found`);
|
|
2052
|
+
}
|
|
2053
|
+
return Object.assign(async (argument) => {
|
|
2054
|
+
const response = await fetch(`${this.apiBaseUrl}/command/${String(prop)}`, {
|
|
2055
|
+
method: "POST",
|
|
2056
|
+
headers: { "Content-Type": "application/json" },
|
|
2057
|
+
body: JSON.stringify(argument)
|
|
2058
|
+
});
|
|
2059
|
+
if (!response.ok) {
|
|
2060
|
+
throw new Error(`Command ${String(prop)} failed: ${response.statusText}`);
|
|
2061
|
+
}
|
|
2062
|
+
return response.json();
|
|
2063
|
+
}, { params: element._params });
|
|
2064
|
+
}
|
|
2065
|
+
});
|
|
2066
|
+
return commandsProxy;
|
|
1325
2067
|
}
|
|
1326
2068
|
}
|
|
1327
|
-
function stringEnum(...values) {
|
|
1328
|
-
return new ArcStringEnum(values);
|
|
1329
|
-
}
|
|
1330
2069
|
// rtc/client.ts
|
|
1331
2070
|
class RTCClient {
|
|
1332
2071
|
storage;
|
|
@@ -1350,41 +2089,9 @@ class RTCClient {
|
|
|
1350
2089
|
}
|
|
1351
2090
|
async performSync() {
|
|
1352
2091
|
this.connectWebSocket();
|
|
1353
|
-
const arcState = await this.storage.getStore("state").findById("$arc", (a) => a);
|
|
1354
|
-
const response = await fetch(`/ws/sync?lastSync=${arcState?.lastSyncDate || ""}`, {
|
|
1355
|
-
method: "GET",
|
|
1356
|
-
headers: {
|
|
1357
|
-
"Content-Type": "application/json",
|
|
1358
|
-
Authorization: `Bearer ${this.token}`
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
|
-
if (!response.ok) {
|
|
1362
|
-
throw new Error("Sync failed");
|
|
1363
|
-
}
|
|
1364
|
-
const { results, syncDate } = await response.json();
|
|
1365
|
-
const pendingStoreChanges = [];
|
|
1366
|
-
const transaction = await this.storage.getReadWriteTransaction();
|
|
1367
|
-
for (const { store, items } of results) {
|
|
1368
|
-
this.syncProgressCallback?.({ store, size: items.length });
|
|
1369
|
-
for (const item of items) {
|
|
1370
|
-
if (item.deleted) {
|
|
1371
|
-
await transaction.remove(store, item._id);
|
|
1372
|
-
} else {
|
|
1373
|
-
await transaction.set(store, item);
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
await transaction.commit();
|
|
1378
|
-
const stateStorage = this.storage.getStore("state");
|
|
1379
|
-
await stateStorage.applyChanges([
|
|
1380
|
-
{
|
|
1381
|
-
type: "set",
|
|
1382
|
-
data: { _id: "$arc", lastSyncDate: syncDate }
|
|
1383
|
-
}
|
|
1384
|
-
]);
|
|
1385
2092
|
}
|
|
1386
2093
|
async connectWebSocket() {
|
|
1387
|
-
this._socket = new WebSocket(`wss://${window.location.host}/ws?token=${this.token}`);
|
|
2094
|
+
this._socket = new WebSocket(`wss://${window.location.host}/api/ws?token=${this.token}`);
|
|
1388
2095
|
this.openSocket = new Promise((resolve) => {
|
|
1389
2096
|
this._socket.addEventListener("open", () => {
|
|
1390
2097
|
this.reconnectAttempts = 0;
|
|
@@ -1434,138 +2141,160 @@ class RTCClient {
|
|
|
1434
2141
|
var rtcClientFactory = (token) => (storage) => {
|
|
1435
2142
|
return new RTCClient(storage, token);
|
|
1436
2143
|
};
|
|
1437
|
-
//
|
|
1438
|
-
class
|
|
1439
|
-
state;
|
|
1440
|
-
bindedChangeHandler = this.changeHandler.bind(this);
|
|
1441
|
-
store;
|
|
1442
|
-
constructor(state) {
|
|
1443
|
-
super();
|
|
1444
|
-
this.state = state;
|
|
1445
|
-
}
|
|
1446
|
-
async run(dataStorage, listener) {
|
|
1447
|
-
this.store = dataStorage.getStore("state");
|
|
1448
|
-
const result = await this.store.findById(this.state.name, this.bindedChangeHandler);
|
|
1449
|
-
this.lastResult = this.state.deserialize(result);
|
|
1450
|
-
if (listener)
|
|
1451
|
-
this.listener = listener;
|
|
1452
|
-
return this.lastResult;
|
|
1453
|
-
}
|
|
1454
|
-
onChange(change) {
|
|
1455
|
-
if (change.type === "set")
|
|
1456
|
-
return change.item;
|
|
1457
|
-
return false;
|
|
1458
|
-
}
|
|
1459
|
-
changeHandler(changes) {
|
|
1460
|
-
for (const change of changes) {
|
|
1461
|
-
const response = this.onChange(change);
|
|
1462
|
-
if (response !== false)
|
|
1463
|
-
this.lastResult = this.state.deserialize(response);
|
|
1464
|
-
}
|
|
1465
|
-
if (this.lastResult)
|
|
1466
|
-
this.nextResult(this.lastResult);
|
|
1467
|
-
}
|
|
1468
|
-
unsubscribe() {
|
|
1469
|
-
this.store.unsubscribe(this.bindedChangeHandler);
|
|
1470
|
-
}
|
|
1471
|
-
nextResult(result) {
|
|
1472
|
-
this.lastResult = result;
|
|
1473
|
-
this.listener?.(result);
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
// state/query-builder.ts
|
|
1478
|
-
class ArcStateQueryBuilder extends ArcQueryBuilder {
|
|
1479
|
-
state;
|
|
1480
|
-
constructor(state) {
|
|
1481
|
-
super();
|
|
1482
|
-
this.state = state;
|
|
1483
|
-
}
|
|
1484
|
-
toQuery() {
|
|
1485
|
-
return new ArcStateQuery(this.state);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// state/state.ts
|
|
1490
|
-
class ArcState extends ArcContextElement {
|
|
2144
|
+
// view/view.ts
|
|
2145
|
+
class ArcView extends ArcContextElementWithStore {
|
|
1491
2146
|
name;
|
|
2147
|
+
id;
|
|
1492
2148
|
schema;
|
|
1493
|
-
|
|
2149
|
+
_description;
|
|
2150
|
+
_elements;
|
|
2151
|
+
_handler;
|
|
2152
|
+
constructor(name, id3, schema) {
|
|
1494
2153
|
super();
|
|
1495
2154
|
this.name = name;
|
|
2155
|
+
this.id = id3;
|
|
1496
2156
|
this.schema = schema;
|
|
1497
2157
|
}
|
|
1498
|
-
|
|
2158
|
+
storeSchema = () => {
|
|
2159
|
+
return arcObjectToStoreSchema(this.name, this.schema);
|
|
2160
|
+
};
|
|
2161
|
+
use(elements) {
|
|
2162
|
+
const clone = this.clone();
|
|
2163
|
+
clone._elements = elements;
|
|
2164
|
+
return clone;
|
|
2165
|
+
}
|
|
2166
|
+
description(description) {
|
|
2167
|
+
const clone = this.clone();
|
|
2168
|
+
clone._description = description;
|
|
2169
|
+
return clone;
|
|
2170
|
+
}
|
|
2171
|
+
handle(handler) {
|
|
2172
|
+
const clone = this.clone();
|
|
2173
|
+
clone._handler = handler;
|
|
2174
|
+
return clone;
|
|
2175
|
+
}
|
|
2176
|
+
queryBuilder = (context3) => {
|
|
1499
2177
|
return {
|
|
1500
|
-
|
|
1501
|
-
|
|
2178
|
+
find: (options) => {
|
|
2179
|
+
throw new Error("Not implemented");
|
|
2180
|
+
}
|
|
1502
2181
|
};
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1507
|
-
queryBuilder() {
|
|
1508
|
-
return new ArcStateQueryBuilder(this);
|
|
1509
|
-
}
|
|
1510
|
-
commandContext(dataStorage, publishEvent) {
|
|
1511
|
-
const store = dataStorage.getStore("state");
|
|
2182
|
+
};
|
|
2183
|
+
commandContext = (dataStorage, publishEvent) => {
|
|
2184
|
+
const store = dataStorage.getStore(this.name);
|
|
1512
2185
|
return {
|
|
1513
|
-
|
|
1514
|
-
return store.
|
|
2186
|
+
find: async (options) => {
|
|
2187
|
+
return store.find(options);
|
|
1515
2188
|
},
|
|
1516
|
-
|
|
1517
|
-
const
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
type: "modify",
|
|
1521
|
-
changes: data,
|
|
1522
|
-
from,
|
|
1523
|
-
to
|
|
2189
|
+
findOne: async (where) => {
|
|
2190
|
+
const result = await store.find({
|
|
2191
|
+
where,
|
|
2192
|
+
limit: 1
|
|
1524
2193
|
});
|
|
1525
|
-
|
|
1526
|
-
edit: async (editCallback) => {
|
|
1527
|
-
const { from, to } = await store.mutate(this.name, editCallback);
|
|
2194
|
+
return result[0];
|
|
1528
2195
|
}
|
|
1529
2196
|
};
|
|
2197
|
+
};
|
|
2198
|
+
observer = () => {
|
|
2199
|
+
return Object.entries(this._handler ?? {}).reduce((acc, [key, value]) => {
|
|
2200
|
+
acc[key] = (event3, dataStorage) => {
|
|
2201
|
+
const store = dataStorage.getStore(this.name);
|
|
2202
|
+
const ctx = {
|
|
2203
|
+
remove: async (id3) => {
|
|
2204
|
+
await store.remove(id3);
|
|
2205
|
+
return { success: true };
|
|
2206
|
+
},
|
|
2207
|
+
set: async (id3, data) => {
|
|
2208
|
+
const parsed = this.schema.parse(data);
|
|
2209
|
+
const body = {
|
|
2210
|
+
_id: id3,
|
|
2211
|
+
lastUpdate: new Date().toISOString(),
|
|
2212
|
+
...parsed
|
|
2213
|
+
};
|
|
2214
|
+
await store.set(body);
|
|
2215
|
+
return { success: true };
|
|
2216
|
+
},
|
|
2217
|
+
find: async (options) => {
|
|
2218
|
+
return store.find(options);
|
|
2219
|
+
},
|
|
2220
|
+
findOne: async (where) => {
|
|
2221
|
+
const result = await store.find({
|
|
2222
|
+
where,
|
|
2223
|
+
limit: 1
|
|
2224
|
+
});
|
|
2225
|
+
return result[0];
|
|
2226
|
+
},
|
|
2227
|
+
modify: async (id3, data) => {
|
|
2228
|
+
const deserialized = this.schema.serializePartial(data);
|
|
2229
|
+
const { from, to } = await store.modify(id3, {
|
|
2230
|
+
...deserialized,
|
|
2231
|
+
lastUpdate: new Date().toISOString()
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
value(ctx, event3);
|
|
2236
|
+
};
|
|
2237
|
+
return acc;
|
|
2238
|
+
}, {});
|
|
2239
|
+
};
|
|
2240
|
+
clone() {
|
|
2241
|
+
const clone = new ArcView(this.name, this.id, this.schema);
|
|
2242
|
+
clone._description = this._description;
|
|
2243
|
+
clone._elements = this._elements;
|
|
2244
|
+
clone._handler = this._handler;
|
|
2245
|
+
return clone;
|
|
1530
2246
|
}
|
|
1531
2247
|
}
|
|
1532
|
-
function
|
|
1533
|
-
return new
|
|
2248
|
+
function view(name, id3, schema) {
|
|
2249
|
+
return new ArcView(name, id3, schema);
|
|
1534
2250
|
}
|
|
1535
2251
|
export {
|
|
2252
|
+
view,
|
|
1536
2253
|
stringEnum,
|
|
1537
2254
|
string,
|
|
1538
|
-
state,
|
|
1539
2255
|
rtcClientFactory,
|
|
1540
2256
|
record,
|
|
2257
|
+
reactive,
|
|
1541
2258
|
object,
|
|
1542
2259
|
number,
|
|
1543
2260
|
id,
|
|
2261
|
+
event,
|
|
1544
2262
|
date,
|
|
1545
2263
|
customId,
|
|
2264
|
+
createSQLiteAdapterFactory,
|
|
1546
2265
|
context,
|
|
2266
|
+
command,
|
|
1547
2267
|
collection,
|
|
1548
2268
|
boolean,
|
|
1549
2269
|
array,
|
|
1550
2270
|
StoreState,
|
|
2271
|
+
SQLiteAdapter,
|
|
2272
|
+
RemoteModelClient,
|
|
2273
|
+
QueryCache,
|
|
2274
|
+
QueryBuilderContext,
|
|
2275
|
+
ModelBase,
|
|
2276
|
+
Model,
|
|
1551
2277
|
MasterStoreState,
|
|
1552
2278
|
MasterDataStorage,
|
|
1553
2279
|
ForkedStoreState,
|
|
2280
|
+
ForkedModel,
|
|
1554
2281
|
ForkedDataStorage,
|
|
1555
2282
|
DataStorage,
|
|
2283
|
+
ArcView,
|
|
1556
2284
|
ArcStringEnum,
|
|
1557
2285
|
ArcString,
|
|
1558
|
-
ArcState,
|
|
1559
2286
|
ArcRecord,
|
|
1560
2287
|
ArcQueryBuilder,
|
|
1561
2288
|
ArcQuery,
|
|
1562
2289
|
ArcOptional,
|
|
1563
2290
|
ArcObject,
|
|
1564
2291
|
ArcNumber,
|
|
1565
|
-
ArcIndexedCollection,
|
|
1566
2292
|
ArcId,
|
|
2293
|
+
ArcEvent,
|
|
1567
2294
|
ArcDate,
|
|
1568
2295
|
ArcCustomId,
|
|
2296
|
+
ArcContext,
|
|
2297
|
+
ArcCommand,
|
|
1569
2298
|
ArcCollectionQuery,
|
|
1570
2299
|
ArcCollection,
|
|
1571
2300
|
ArcBranded,
|