@arcote.tech/arc 0.0.26 → 0.1.0
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/data-storage-master.d.ts +9 -1
- 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 +6 -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 +2 -3
- package/dist/elements/abstract.d.ts +18 -1
- package/dist/elements/array.d.ts +61 -3
- package/dist/elements/boolean.d.ts +1 -1
- package/dist/elements/branded.d.ts +4 -3
- package/dist/elements/class.d.ts +2 -0
- package/dist/elements/date.d.ts +35 -2
- package/dist/elements/default.d.ts +1 -0
- package/dist/elements/element.d.ts +1 -0
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/number.d.ts +36 -2
- package/dist/elements/object.d.ts +38 -5
- package/dist/elements/optional.d.ts +3 -2
- package/dist/elements/or.d.ts +2 -0
- package/dist/elements/record.d.ts +16 -4
- package/dist/elements/string-enum.d.ts +17 -3
- package/dist/elements/string.d.ts +89 -2
- package/dist/elements/tests/example.d.ts +6 -0
- package/dist/elements/tests/test.d.ts +2 -0
- package/dist/elements/utils/type-validator-builder.d.ts +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1528 -708
- 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/number.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/tests/validations/string.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 +1 -1
- 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-notification-optimization.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
|
|
@@ -24,6 +31,11 @@ class ArcOptional {
|
|
|
24
31
|
return null;
|
|
25
32
|
return this.parent.deserialize(value);
|
|
26
33
|
}
|
|
34
|
+
validate(value) {
|
|
35
|
+
if (!value)
|
|
36
|
+
return false;
|
|
37
|
+
return this.parent.validate(value);
|
|
38
|
+
}
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
// elements/branded.ts
|
|
@@ -46,6 +58,9 @@ class ArcBranded {
|
|
|
46
58
|
optional() {
|
|
47
59
|
return new ArcOptional(this);
|
|
48
60
|
}
|
|
61
|
+
validate(value) {
|
|
62
|
+
return this.parent.validate(value);
|
|
63
|
+
}
|
|
49
64
|
}
|
|
50
65
|
|
|
51
66
|
// elements/default.ts
|
|
@@ -56,6 +71,9 @@ class ArcDefault {
|
|
|
56
71
|
this.parent = parent;
|
|
57
72
|
this.defaultValueOrCallback = defaultValueOrCallback;
|
|
58
73
|
}
|
|
74
|
+
validate(value) {
|
|
75
|
+
throw new Error("Method not implemented.");
|
|
76
|
+
}
|
|
59
77
|
parse(value) {
|
|
60
78
|
if (value)
|
|
61
79
|
return this.parent.parse(value);
|
|
@@ -79,6 +97,10 @@ class ArcDefault {
|
|
|
79
97
|
|
|
80
98
|
// elements/abstract.ts
|
|
81
99
|
class ArcAbstract {
|
|
100
|
+
validations;
|
|
101
|
+
constructor(validations = []) {
|
|
102
|
+
this.validations = validations;
|
|
103
|
+
}
|
|
82
104
|
default(defaultValueOrCallback) {
|
|
83
105
|
return new ArcDefault(this, defaultValueOrCallback);
|
|
84
106
|
}
|
|
@@ -88,13 +110,37 @@ class ArcAbstract {
|
|
|
88
110
|
branded(name) {
|
|
89
111
|
return new ArcBranded(this, name);
|
|
90
112
|
}
|
|
113
|
+
clone() {
|
|
114
|
+
const Constructor = this.constructor;
|
|
115
|
+
const newInstance = Object.assign(new Constructor, this);
|
|
116
|
+
return newInstance;
|
|
117
|
+
}
|
|
118
|
+
validate(value) {
|
|
119
|
+
const errors = this.validations.reduce((acc, { name, validator }) => {
|
|
120
|
+
try {
|
|
121
|
+
acc[name] = validator(value);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
acc[name] = error;
|
|
124
|
+
}
|
|
125
|
+
return acc;
|
|
126
|
+
}, {});
|
|
127
|
+
if (Object.values(errors).some((result) => !!result)) {
|
|
128
|
+
return errors;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
pipeValidation(name, validator) {
|
|
133
|
+
const newInstance = this.clone();
|
|
134
|
+
newInstance.validations = [...this.validations, { name, validator }];
|
|
135
|
+
return newInstance;
|
|
136
|
+
}
|
|
137
|
+
getValidations() {
|
|
138
|
+
return this.validations;
|
|
139
|
+
}
|
|
91
140
|
}
|
|
92
141
|
|
|
93
142
|
// elements/abstract-primitive.ts
|
|
94
143
|
class ArcPrimitive extends ArcAbstract {
|
|
95
|
-
constructor() {
|
|
96
|
-
super();
|
|
97
|
-
}
|
|
98
144
|
serialize(value) {
|
|
99
145
|
return value;
|
|
100
146
|
}
|
|
@@ -106,8 +152,133 @@ class ArcPrimitive extends ArcAbstract {
|
|
|
106
152
|
}
|
|
107
153
|
}
|
|
108
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
|
+
|
|
109
172
|
// elements/string.ts
|
|
173
|
+
var stringValidator = typeValidatorBuilder("string");
|
|
174
|
+
|
|
110
175
|
class ArcString extends ArcPrimitive {
|
|
176
|
+
constructor() {
|
|
177
|
+
super([stringValidator]);
|
|
178
|
+
}
|
|
179
|
+
minLength(min) {
|
|
180
|
+
return this.validation("minLength", (value) => {
|
|
181
|
+
if (value.length < min)
|
|
182
|
+
return {
|
|
183
|
+
currentLength: value.length,
|
|
184
|
+
minLength: min
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
maxLength(max) {
|
|
189
|
+
return this.validation("maxLength", (value) => {
|
|
190
|
+
if (value.length > max)
|
|
191
|
+
return {
|
|
192
|
+
currentLength: value.length,
|
|
193
|
+
maxLength: max
|
|
194
|
+
};
|
|
195
|
+
});
|
|
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
|
+
}
|
|
278
|
+
validation(name, validator) {
|
|
279
|
+
const instance = this.pipeValidation(name, validator);
|
|
280
|
+
return instance;
|
|
281
|
+
}
|
|
111
282
|
}
|
|
112
283
|
function string() {
|
|
113
284
|
return new ArcString;
|
|
@@ -143,283 +314,571 @@ function customId(name, createFn) {
|
|
|
143
314
|
return new ArcCustomId(name, createFn);
|
|
144
315
|
}
|
|
145
316
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
lastResult;
|
|
149
|
-
listener;
|
|
150
|
-
}
|
|
317
|
+
// elements/object.ts
|
|
318
|
+
var objectValidator = typeValidatorBuilder("object");
|
|
151
319
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
for (const change of changes) {
|
|
173
|
-
const response = this.onChange(change);
|
|
174
|
-
if (response !== false) {
|
|
175
|
-
this.lastResult = response;
|
|
176
|
-
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
|
+
}
|
|
177
340
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.nextResult(this.lastResult);
|
|
181
|
-
}
|
|
182
|
-
unsubscribe() {
|
|
183
|
-
this.store.unsubscribe(this.bindedChangeHandler);
|
|
341
|
+
]);
|
|
342
|
+
this.rawShape = rawShape;
|
|
184
343
|
}
|
|
185
|
-
|
|
186
|
-
this.
|
|
187
|
-
|
|
344
|
+
parse(value) {
|
|
345
|
+
return Object.entries(this.rawShape).reduce((acc, [key, element]) => {
|
|
346
|
+
acc[key] = element.parse(value[key]);
|
|
347
|
+
return acc;
|
|
348
|
+
}, {});
|
|
188
349
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
+
}, {});
|
|
196
359
|
}
|
|
197
|
-
|
|
198
|
-
return
|
|
360
|
+
deserialize(value) {
|
|
361
|
+
return Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
362
|
+
key,
|
|
363
|
+
element.deserialize(value[key])
|
|
364
|
+
]));
|
|
199
365
|
}
|
|
200
|
-
|
|
201
|
-
|
|
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);
|
|
202
380
|
}
|
|
203
|
-
|
|
204
|
-
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
|
+
}, {});
|
|
205
386
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
constructor(collection, filterFn, sortFn) {
|
|
212
|
-
super(collection);
|
|
213
|
-
this.filterFn = filterFn;
|
|
214
|
-
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
|
+
}, {});
|
|
215
392
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
222
|
-
if (isInLastResult && shouldBeInTheResult)
|
|
223
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
|
|
224
|
-
else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
|
|
225
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1));
|
|
226
|
-
else if (!isInLastResult && shouldBeInTheResult)
|
|
227
|
-
return this.createResult(lastResultAsArray.concat(change.item));
|
|
228
|
-
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
|
+
}, {});
|
|
229
398
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return new QueryCollectionResult(result.sort(this.sortFn));
|
|
233
|
-
return new QueryCollectionResult(result);
|
|
399
|
+
pick(...keys) {
|
|
400
|
+
return new ArcObject(Object.fromEntries(keys.map((key) => [key, this.rawShape[key]])));
|
|
234
401
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return this.createResult(result.filter(this.filterFn));
|
|
238
|
-
return this.createResult(result);
|
|
402
|
+
omit(...keys) {
|
|
403
|
+
return new ArcObject(Object.fromEntries(Object.entries(this.rawShape).filter(([key]) => !keys.includes(key))));
|
|
239
404
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return this.filterFn(item);
|
|
243
|
-
return true;
|
|
405
|
+
entries() {
|
|
406
|
+
return Object.entries(this.rawShape);
|
|
244
407
|
}
|
|
245
408
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
class ArcAllItemsQuery extends ArcManyItemsQuery {
|
|
249
|
-
async fetch(store) {
|
|
250
|
-
const results = await store.findAll(this.bindedChangeHandler);
|
|
251
|
-
return this.createFiltredResult(results);
|
|
252
|
-
}
|
|
409
|
+
function object(element) {
|
|
410
|
+
return new ArcObject(element);
|
|
253
411
|
}
|
|
254
412
|
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
}
|
|
413
|
+
// elements/array.ts
|
|
414
|
+
var arrayValidator = typeValidatorBuilder("array", Array.isArray);
|
|
258
415
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
}
|
|
270
435
|
}
|
|
271
|
-
|
|
272
|
-
|
|
436
|
+
]);
|
|
437
|
+
this.parent = parent;
|
|
273
438
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
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
|
+
});
|
|
283
447
|
}
|
|
284
|
-
|
|
285
|
-
this.
|
|
286
|
-
|
|
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
|
+
});
|
|
287
456
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
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
|
+
});
|
|
298
466
|
}
|
|
299
|
-
|
|
300
|
-
return
|
|
467
|
+
nonEmpty() {
|
|
468
|
+
return this.validation("nonEmpty", (value) => {
|
|
469
|
+
if (value.length === 0)
|
|
470
|
+
return { msg: "array is empty" };
|
|
471
|
+
});
|
|
301
472
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
// db/index-query.ts
|
|
305
|
-
function indexQueryPredicate(item, query) {
|
|
306
|
-
if (!("$gt" in query) && !("$gte" in query) && !("$lt" in query) && !("$lte" in query)) {
|
|
307
|
-
return Object.entries(query).every(([key, value]) => item[key] === value);
|
|
308
|
-
}
|
|
309
|
-
if (query.$gt) {
|
|
310
|
-
const isGreaterThan = Object.entries(query.$gt).every(([key, value]) => item[key] > value);
|
|
311
|
-
if (!isGreaterThan)
|
|
312
|
-
return false;
|
|
473
|
+
parse(value) {
|
|
474
|
+
return value.map((v) => this.parent.parse(v));
|
|
313
475
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (!isGreaterThanOrEqual)
|
|
317
|
-
return false;
|
|
476
|
+
serialize(value) {
|
|
477
|
+
return value.map((v) => this.parent.serialize(v));
|
|
318
478
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
479
|
+
deserialize(value) {
|
|
480
|
+
if (!Array.isArray(value))
|
|
481
|
+
return [];
|
|
482
|
+
return value.map((v) => this.parent.deserialize(v));
|
|
323
483
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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);
|
|
328
492
|
}
|
|
329
|
-
|
|
493
|
+
validation(name, validator) {
|
|
494
|
+
const instance = this.pipeValidation(name, validator);
|
|
495
|
+
return instance;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function array(element) {
|
|
499
|
+
return new ArcArray(element);
|
|
330
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);
|
|
331
509
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
data;
|
|
336
|
-
filterFn;
|
|
337
|
-
sortFn;
|
|
338
|
-
constructor(collection, index, data, filterFn, sortFn) {
|
|
339
|
-
super(collection, filterFn, sortFn);
|
|
340
|
-
this.index = index;
|
|
341
|
-
this.data = data;
|
|
342
|
-
this.filterFn = filterFn;
|
|
343
|
-
this.sortFn = sortFn;
|
|
510
|
+
class ArcDate extends ArcAbstract {
|
|
511
|
+
constructor() {
|
|
512
|
+
super([dateValidator]);
|
|
344
513
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return false;
|
|
348
|
-
return indexQueryPredicate(item, this.data);
|
|
514
|
+
parse(value) {
|
|
515
|
+
return new Date(value);
|
|
349
516
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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;
|
|
353
538
|
}
|
|
354
539
|
}
|
|
540
|
+
function date() {
|
|
541
|
+
return new ArcDate;
|
|
542
|
+
}
|
|
543
|
+
// elements/number.ts
|
|
544
|
+
var numberValidator = typeValidatorBuilder("number");
|
|
355
545
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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) {
|
|
362
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);
|
|
363
742
|
this.collection = collection;
|
|
364
|
-
this.index = index;
|
|
365
|
-
this.data = data;
|
|
366
|
-
if (collection.options.sort)
|
|
367
|
-
this.sortFn = collection.options.sort;
|
|
368
743
|
}
|
|
369
|
-
|
|
370
|
-
|
|
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;
|
|
371
779
|
}
|
|
372
780
|
}
|
|
373
781
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
+
});
|
|
380
825
|
}
|
|
381
826
|
onChange(change) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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));
|
|
388
838
|
return false;
|
|
389
839
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return result;
|
|
840
|
+
createResult(result) {
|
|
841
|
+
return new QueryCollectionResult(result);
|
|
393
842
|
}
|
|
394
843
|
}
|
|
395
844
|
|
|
396
|
-
// collection/query-builders/
|
|
397
|
-
class
|
|
845
|
+
// collection/query-builders/find.ts
|
|
846
|
+
class ArcFindQueryBuilder {
|
|
398
847
|
collection;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
848
|
+
queryContext;
|
|
849
|
+
options;
|
|
850
|
+
constructor(collection, queryContext, options) {
|
|
402
851
|
this.collection = collection;
|
|
403
|
-
this.
|
|
852
|
+
this.queryContext = queryContext;
|
|
853
|
+
this.options = options;
|
|
404
854
|
}
|
|
405
855
|
toQuery() {
|
|
406
|
-
return
|
|
856
|
+
return this.queryContext.cacheQuery(ArcFindQuery, [
|
|
857
|
+
this.collection,
|
|
858
|
+
this.options
|
|
859
|
+
]);
|
|
860
|
+
}
|
|
861
|
+
run() {
|
|
862
|
+
return this.queryContext.runQuery(this.toQuery());
|
|
407
863
|
}
|
|
408
864
|
}
|
|
409
865
|
|
|
410
866
|
// collection/collection.ts
|
|
411
|
-
class ArcCollection extends
|
|
867
|
+
class ArcCollection extends ArcContextElementWithStore {
|
|
412
868
|
name;
|
|
413
|
-
|
|
869
|
+
id;
|
|
414
870
|
schema;
|
|
415
871
|
options;
|
|
416
|
-
constructor(name,
|
|
872
|
+
constructor(name, id3, schema, options) {
|
|
417
873
|
super();
|
|
418
874
|
this.name = name;
|
|
419
|
-
this.id =
|
|
875
|
+
this.id = id3;
|
|
420
876
|
this.schema = schema;
|
|
421
877
|
this.options = options;
|
|
422
878
|
}
|
|
879
|
+
storeSchema() {
|
|
880
|
+
return arcObjectToStoreSchema(this.name, this.schema);
|
|
881
|
+
}
|
|
423
882
|
serialize(data) {
|
|
424
883
|
return {
|
|
425
884
|
_id: this.id.serialize(data._id),
|
|
@@ -432,22 +891,22 @@ class ArcCollection extends ArcContextElement {
|
|
|
432
891
|
...this.schema.deserialize(data)
|
|
433
892
|
};
|
|
434
893
|
}
|
|
435
|
-
queryBuilder() {
|
|
894
|
+
queryBuilder = (context) => {
|
|
436
895
|
return {
|
|
437
|
-
|
|
438
|
-
one: (id2) => new ArcOneItemQueryBuilder(this, id2)
|
|
896
|
+
find: (options) => new ArcFindQueryBuilder(this, context, options || {})
|
|
439
897
|
};
|
|
440
|
-
}
|
|
441
|
-
commandContext(dataStorage, publishEvent) {
|
|
898
|
+
};
|
|
899
|
+
commandContext = (dataStorage, publishEvent) => {
|
|
442
900
|
const store = dataStorage.getStore(this.name);
|
|
443
901
|
return {
|
|
444
902
|
add: async (data) => {
|
|
445
903
|
if (this.id instanceof ArcCustomId)
|
|
446
904
|
throw new Error("Collection with custom id not support 'add' method");
|
|
447
|
-
const
|
|
905
|
+
const id3 = this.id.generate();
|
|
448
906
|
const parsed = this.schema.parse(data);
|
|
449
907
|
const body = {
|
|
450
|
-
_id:
|
|
908
|
+
_id: id3,
|
|
909
|
+
lastUpdate: new Date().toISOString(),
|
|
451
910
|
...parsed
|
|
452
911
|
};
|
|
453
912
|
await store.set(body);
|
|
@@ -455,16 +914,16 @@ class ArcCollection extends ArcContextElement {
|
|
|
455
914
|
type: "set",
|
|
456
915
|
to: body
|
|
457
916
|
});
|
|
458
|
-
return { id:
|
|
917
|
+
return { id: id3 };
|
|
459
918
|
},
|
|
460
|
-
remove: async (
|
|
461
|
-
await store.remove(
|
|
919
|
+
remove: async (id3) => {
|
|
920
|
+
await store.remove(id3);
|
|
462
921
|
return { success: true };
|
|
463
922
|
},
|
|
464
|
-
set: async (
|
|
923
|
+
set: async (id3, data) => {
|
|
465
924
|
const parsed = this.schema.parse(data);
|
|
466
925
|
const body = {
|
|
467
|
-
_id:
|
|
926
|
+
_id: id3,
|
|
468
927
|
...parsed
|
|
469
928
|
};
|
|
470
929
|
await store.set(body);
|
|
@@ -474,15 +933,19 @@ class ArcCollection extends ArcContextElement {
|
|
|
474
933
|
});
|
|
475
934
|
return { success: true };
|
|
476
935
|
},
|
|
477
|
-
|
|
478
|
-
return store.
|
|
936
|
+
find: async (options) => {
|
|
937
|
+
return store.find(options);
|
|
479
938
|
},
|
|
480
|
-
|
|
481
|
-
|
|
939
|
+
findOne: async (where) => {
|
|
940
|
+
const result = await store.find({
|
|
941
|
+
where,
|
|
942
|
+
limit: 1
|
|
943
|
+
});
|
|
944
|
+
return result[0];
|
|
482
945
|
},
|
|
483
|
-
modify: async (
|
|
946
|
+
modify: async (id3, data) => {
|
|
484
947
|
const deserialized = this.schema.serializePartial(data);
|
|
485
|
-
const { from, to } = await store.modify(
|
|
948
|
+
const { from, to } = await store.modify(id3, deserialized);
|
|
486
949
|
await publishEvent({
|
|
487
950
|
type: "modify",
|
|
488
951
|
changes: deserialized,
|
|
@@ -490,8 +953,8 @@ class ArcCollection extends ArcContextElement {
|
|
|
490
953
|
to
|
|
491
954
|
});
|
|
492
955
|
},
|
|
493
|
-
edit: async (
|
|
494
|
-
const { from, to } = await store.mutate(
|
|
956
|
+
edit: async (id3, editCallback) => {
|
|
957
|
+
const { from, to } = await store.mutate(id3, editCallback);
|
|
495
958
|
await publishEvent({
|
|
496
959
|
type: "mutate",
|
|
497
960
|
from,
|
|
@@ -499,129 +962,396 @@ class ArcCollection extends ArcContextElement {
|
|
|
499
962
|
});
|
|
500
963
|
}
|
|
501
964
|
};
|
|
502
|
-
}
|
|
503
|
-
indexBy(indexes) {
|
|
504
|
-
return new ArcIndexedCollection(this.name, this.id, this.schema, this.options, indexes);
|
|
505
|
-
}
|
|
965
|
+
};
|
|
506
966
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if (!(name in this.indexes)) {
|
|
522
|
-
throw new Error(`Index "${name}" not found in collection "${this.name}"`);
|
|
523
|
-
}
|
|
524
|
-
return (data) => dataStorage.getStore(this.name).findByIndex(name, data);
|
|
525
|
-
}
|
|
526
|
-
});
|
|
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;
|
|
527
981
|
}
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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;
|
|
540
1021
|
}
|
|
541
1022
|
}
|
|
542
|
-
function
|
|
543
|
-
return new
|
|
1023
|
+
function command(name) {
|
|
1024
|
+
return new ArcCommand(name);
|
|
544
1025
|
}
|
|
545
1026
|
// context/context.ts
|
|
546
1027
|
class ArcContext {
|
|
547
|
-
version;
|
|
548
1028
|
elements;
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
elementsMap;
|
|
552
|
-
constructor(version, elements, commands, listeners) {
|
|
553
|
-
this.version = version;
|
|
1029
|
+
eventListeners;
|
|
1030
|
+
constructor(elements) {
|
|
554
1031
|
this.elements = elements;
|
|
555
|
-
this.
|
|
556
|
-
|
|
557
|
-
|
|
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
|
+
]);
|
|
558
1049
|
}
|
|
559
|
-
queryBuilder() {
|
|
1050
|
+
queryBuilder(queryContext) {
|
|
560
1051
|
return new Proxy({}, {
|
|
561
1052
|
get: (target, name) => {
|
|
562
|
-
const element = this.
|
|
1053
|
+
const element = this.elements.find((element2) => element2.name === name);
|
|
563
1054
|
if (!element) {
|
|
564
|
-
throw new Error(`
|
|
1055
|
+
throw new Error(`Element "${String(name)}" not found`);
|
|
565
1056
|
}
|
|
566
|
-
|
|
1057
|
+
if (!element.queryBuilder) {
|
|
1058
|
+
throw new Error(`Element "${String(name)}" does not have a query builder`);
|
|
1059
|
+
}
|
|
1060
|
+
return element.queryBuilder(queryContext);
|
|
567
1061
|
}
|
|
568
1062
|
});
|
|
569
1063
|
}
|
|
570
|
-
commandContext(client, dataStorage
|
|
1064
|
+
commandContext(client, dataStorage) {
|
|
571
1065
|
return new Proxy({}, {
|
|
572
1066
|
get: (target, name) => {
|
|
573
1067
|
if (name === "$client") {
|
|
574
1068
|
return client;
|
|
575
1069
|
}
|
|
576
|
-
const element = this.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
+
});
|
|
581
1081
|
}
|
|
582
1082
|
});
|
|
583
1083
|
}
|
|
584
|
-
commandsClient(client, dataStorage, catchErrorCallback) {
|
|
1084
|
+
commandsClient(client, queryContext, dataStorage, catchErrorCallback) {
|
|
585
1085
|
return new Proxy({}, {
|
|
586
1086
|
get: (_, name) => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const commandContext = this.commandContext(client, forkedDataStorage, async (data) => {
|
|
591
|
-
if (!this.listeners)
|
|
592
|
-
return;
|
|
593
|
-
await Promise.all(this.listeners?.map((listener) => listener(data, commandContext)));
|
|
594
|
-
});
|
|
595
|
-
try {
|
|
596
|
-
const result = await this.commands[name](commandContext, ...args);
|
|
597
|
-
await forkedDataStorage.merge();
|
|
598
|
-
return result;
|
|
599
|
-
} catch (error) {
|
|
600
|
-
console.log("error", error);
|
|
601
|
-
catchErrorCallback(error);
|
|
602
|
-
return error;
|
|
603
|
-
}
|
|
604
|
-
};
|
|
1087
|
+
const element = this.elements.find((element2) => element2.name === name);
|
|
1088
|
+
if (!element) {
|
|
1089
|
+
throw new Error(`Element "${String(name)}" not found`);
|
|
605
1090
|
}
|
|
606
|
-
|
|
607
|
-
|
|
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
|
+
};
|
|
608
1107
|
}
|
|
609
1108
|
});
|
|
610
1109
|
}
|
|
611
1110
|
}
|
|
612
|
-
function context(
|
|
613
|
-
|
|
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"
|
|
1143
|
+
}
|
|
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);
|
|
614
1176
|
}
|
|
615
|
-
//
|
|
616
|
-
class
|
|
617
|
-
|
|
618
|
-
|
|
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;
|
|
619
1197
|
}
|
|
620
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)) {
|
|
1278
|
+
return;
|
|
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
|
+
}
|
|
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);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
function reactive(fn) {
|
|
1343
|
+
return (context2) => new ReactiveQueryBuilder(context2, fn);
|
|
1344
|
+
}
|
|
1345
|
+
// data-storage/data-storage.abstract.ts
|
|
1346
|
+
class DataStorage {
|
|
1347
|
+
async commitChanges(changes) {
|
|
1348
|
+
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// data-storage/store-state-fork.ts
|
|
1353
|
+
import { apply } from "mutative";
|
|
621
1354
|
|
|
622
|
-
// data-storage/store-state-fork.ts
|
|
623
|
-
import { apply } from "mutative";
|
|
624
|
-
|
|
625
1355
|
// data-storage/deep-merge.ts
|
|
626
1356
|
function deepMerge(target, source) {
|
|
627
1357
|
const output = { ...target };
|
|
@@ -664,35 +1394,39 @@ class StoreState {
|
|
|
664
1394
|
};
|
|
665
1395
|
await this.applyChanges([change]);
|
|
666
1396
|
}
|
|
667
|
-
async remove(
|
|
1397
|
+
async remove(id3) {
|
|
668
1398
|
const change = {
|
|
669
1399
|
type: "delete",
|
|
670
|
-
id:
|
|
1400
|
+
id: id3
|
|
671
1401
|
};
|
|
672
1402
|
await this.applyChanges([change]);
|
|
673
1403
|
}
|
|
674
|
-
async modify(
|
|
1404
|
+
async modify(id3, data) {
|
|
675
1405
|
const change = {
|
|
676
1406
|
type: "modify",
|
|
677
|
-
id:
|
|
1407
|
+
id: id3,
|
|
678
1408
|
data
|
|
679
1409
|
};
|
|
680
1410
|
const { from, to } = await this.applyChange(change);
|
|
681
1411
|
return { from, to };
|
|
682
1412
|
}
|
|
683
|
-
async mutate(
|
|
684
|
-
const
|
|
685
|
-
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 });
|
|
686
1417
|
await editCallback(draft);
|
|
687
1418
|
const [_, patches] = finalize();
|
|
688
1419
|
const change = {
|
|
689
1420
|
type: "mutate",
|
|
690
|
-
id:
|
|
1421
|
+
id: id3,
|
|
691
1422
|
patches
|
|
692
1423
|
};
|
|
693
1424
|
const { from, to } = await this.applyChange(change);
|
|
694
1425
|
return { from, to };
|
|
695
1426
|
}
|
|
1427
|
+
applySerializedChanges(changes) {
|
|
1428
|
+
return Promise.all(changes.map((change) => this.applyChange(change)));
|
|
1429
|
+
}
|
|
696
1430
|
unsubscribe(listener) {
|
|
697
1431
|
this.listeners.delete(listener);
|
|
698
1432
|
}
|
|
@@ -728,10 +1462,10 @@ class ForkedStoreState extends StoreState {
|
|
|
728
1462
|
};
|
|
729
1463
|
}
|
|
730
1464
|
if (change.type === "delete") {
|
|
731
|
-
const from = await this.
|
|
1465
|
+
const from = await this.find({ where: { _id: change.id } }).then((results) => results[0] || null);
|
|
732
1466
|
this.changedItems.set(change.id, null);
|
|
733
1467
|
return {
|
|
734
|
-
from
|
|
1468
|
+
from,
|
|
735
1469
|
to: null,
|
|
736
1470
|
event: {
|
|
737
1471
|
type: "delete",
|
|
@@ -741,7 +1475,9 @@ class ForkedStoreState extends StoreState {
|
|
|
741
1475
|
};
|
|
742
1476
|
}
|
|
743
1477
|
if (change.type === "modify") {
|
|
744
|
-
const existing = await this.
|
|
1478
|
+
const existing = await this.find({
|
|
1479
|
+
where: { _id: change.id }
|
|
1480
|
+
}).then((results) => results[0]);
|
|
745
1481
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
746
1482
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
747
1483
|
this.changedItems.set(change.id, item);
|
|
@@ -756,7 +1492,9 @@ class ForkedStoreState extends StoreState {
|
|
|
756
1492
|
};
|
|
757
1493
|
}
|
|
758
1494
|
if (change.type === "mutate") {
|
|
759
|
-
const existing = await this.
|
|
1495
|
+
const existing = await this.find({
|
|
1496
|
+
where: { _id: change.id }
|
|
1497
|
+
}).then((results) => results[0]);
|
|
760
1498
|
const updated = apply(existing || {}, change.patches);
|
|
761
1499
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
762
1500
|
this.changedItems.set(change.id, item);
|
|
@@ -773,59 +1511,42 @@ class ForkedStoreState extends StoreState {
|
|
|
773
1511
|
throw new Error("Unknown change type");
|
|
774
1512
|
}
|
|
775
1513
|
async applyChange(change) {
|
|
776
|
-
const { event, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
777
|
-
this.notifyListeners([
|
|
1514
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
1515
|
+
this.notifyListeners([event3]);
|
|
778
1516
|
return { from, to };
|
|
779
1517
|
}
|
|
780
1518
|
async applyChanges(changes) {
|
|
781
1519
|
const events = [];
|
|
782
1520
|
for (const change of changes) {
|
|
783
|
-
const { event } = await this.applyChangeAndReturnEvent(change);
|
|
784
|
-
if (
|
|
785
|
-
events.push(
|
|
1521
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(change);
|
|
1522
|
+
if (event3)
|
|
1523
|
+
events.push(event3);
|
|
786
1524
|
}
|
|
787
1525
|
if (events.length > 0) {
|
|
788
1526
|
this.notifyListeners(events);
|
|
789
1527
|
}
|
|
790
1528
|
}
|
|
791
|
-
async
|
|
792
|
-
if (listener)
|
|
793
|
-
this.listeners.set(listener, { callback: listener, id: id2 });
|
|
794
|
-
if (this.changedItems.has(id2))
|
|
795
|
-
return this.changedItems.get(id2);
|
|
796
|
-
return await this.master.findById(id2);
|
|
797
|
-
}
|
|
798
|
-
async findByIndex(index, data, listener) {
|
|
799
|
-
if (listener)
|
|
1529
|
+
async find(options, listener) {
|
|
1530
|
+
if (listener) {
|
|
800
1531
|
this.listeners.set(listener, { callback: listener });
|
|
801
|
-
|
|
1532
|
+
}
|
|
1533
|
+
const parentResults = await this.master.find(options);
|
|
802
1534
|
const results = new Map;
|
|
803
|
-
|
|
804
|
-
for (const [
|
|
1535
|
+
parentResults.forEach((item) => results.set(item._id, item));
|
|
1536
|
+
for (const [id3, changedItem] of this.changedItems) {
|
|
805
1537
|
if (changedItem === null) {
|
|
806
|
-
results.delete(
|
|
1538
|
+
results.delete(id3);
|
|
807
1539
|
continue;
|
|
808
1540
|
}
|
|
809
|
-
const matches = Object.entries(
|
|
1541
|
+
const matches = !options.where || Object.entries(options.where).every(([key, value]) => changedItem[key] === value);
|
|
810
1542
|
if (matches) {
|
|
811
|
-
results.set(
|
|
1543
|
+
results.set(id3, changedItem);
|
|
812
1544
|
} else {
|
|
813
|
-
results.delete(
|
|
1545
|
+
results.delete(id3);
|
|
814
1546
|
}
|
|
815
1547
|
}
|
|
816
1548
|
return Array.from(results.values());
|
|
817
1549
|
}
|
|
818
|
-
async findAll(listener) {
|
|
819
|
-
if (listener)
|
|
820
|
-
this.listeners.set(listener, { callback: listener });
|
|
821
|
-
const parentResult = await this.master.findAll();
|
|
822
|
-
return parentResult.map((item) => {
|
|
823
|
-
const id2 = item._id;
|
|
824
|
-
if (this.changedItems.has(id2))
|
|
825
|
-
return this.changedItems.get(id2);
|
|
826
|
-
return item;
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
1550
|
}
|
|
830
1551
|
|
|
831
1552
|
// data-storage/data-storage-forked.ts
|
|
@@ -864,8 +1585,6 @@ class ForkedDataStorage extends DataStorage {
|
|
|
864
1585
|
// data-storage/store-state-master.ts
|
|
865
1586
|
import { apply as apply2 } from "mutative";
|
|
866
1587
|
class MasterStoreState extends StoreState {
|
|
867
|
-
items = new Map;
|
|
868
|
-
isComplete = false;
|
|
869
1588
|
constructor(storeName, dataStorage, deserialize) {
|
|
870
1589
|
super(storeName, dataStorage, deserialize);
|
|
871
1590
|
}
|
|
@@ -873,7 +1592,6 @@ class MasterStoreState extends StoreState {
|
|
|
873
1592
|
if (change.type === "set") {
|
|
874
1593
|
await transaction.set(this.storeName, change.data);
|
|
875
1594
|
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
876
|
-
this.items.set(change.data._id, item);
|
|
877
1595
|
return {
|
|
878
1596
|
from: null,
|
|
879
1597
|
to: item,
|
|
@@ -886,7 +1604,6 @@ class MasterStoreState extends StoreState {
|
|
|
886
1604
|
}
|
|
887
1605
|
if (change.type === "delete") {
|
|
888
1606
|
await transaction.remove(this.storeName, change.id);
|
|
889
|
-
this.items.set(change.id, null);
|
|
890
1607
|
return {
|
|
891
1608
|
from: null,
|
|
892
1609
|
to: null,
|
|
@@ -898,11 +1615,10 @@ class MasterStoreState extends StoreState {
|
|
|
898
1615
|
};
|
|
899
1616
|
}
|
|
900
1617
|
if (change.type === "modify") {
|
|
901
|
-
const existing = await transaction.
|
|
1618
|
+
const existing = await transaction.find(this.storeName, { where: { _id: change.id } }).then((results) => results[0]);
|
|
902
1619
|
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
903
1620
|
await transaction.set(this.storeName, updated);
|
|
904
1621
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
905
|
-
this.items.set(change.id, item);
|
|
906
1622
|
return {
|
|
907
1623
|
from: null,
|
|
908
1624
|
to: item,
|
|
@@ -914,11 +1630,10 @@ class MasterStoreState extends StoreState {
|
|
|
914
1630
|
};
|
|
915
1631
|
}
|
|
916
1632
|
if (change.type === "mutate") {
|
|
917
|
-
const existing = await transaction.
|
|
918
|
-
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);
|
|
919
1635
|
await transaction.set(this.storeName, updated);
|
|
920
1636
|
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
921
|
-
this.items.set(change.id, item);
|
|
922
1637
|
return {
|
|
923
1638
|
from: null,
|
|
924
1639
|
to: item,
|
|
@@ -933,77 +1648,31 @@ class MasterStoreState extends StoreState {
|
|
|
933
1648
|
}
|
|
934
1649
|
async applyChange(change) {
|
|
935
1650
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
936
|
-
const { event, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
937
|
-
|
|
1651
|
+
const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
1652
|
+
await transaction.commit();
|
|
1653
|
+
this.notifyListeners([event3]);
|
|
938
1654
|
return { from, to };
|
|
939
1655
|
}
|
|
940
1656
|
async applyChanges(changes) {
|
|
941
1657
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
942
1658
|
const events = [];
|
|
943
1659
|
for (const change of changes) {
|
|
944
|
-
const { event } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
945
|
-
if (
|
|
946
|
-
events.push(
|
|
1660
|
+
const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
1661
|
+
if (event3)
|
|
1662
|
+
events.push(event3);
|
|
947
1663
|
}
|
|
948
1664
|
await transaction.commit();
|
|
949
1665
|
if (events.length > 0) {
|
|
950
1666
|
this.notifyListeners(events);
|
|
951
1667
|
}
|
|
952
1668
|
}
|
|
953
|
-
async
|
|
954
|
-
if (
|
|
955
|
-
return;
|
|
956
|
-
if (listener)
|
|
957
|
-
this.listeners.set(listener, { callback: listener, id: id2 });
|
|
958
|
-
if (this.items.has(id2))
|
|
959
|
-
return this.items.get(id2);
|
|
960
|
-
const transaction = await this.dataStorage.getReadTransaction();
|
|
961
|
-
const result = await transaction.findById(this.storeName, id2);
|
|
962
|
-
const item = result && this.deserialize ? this.deserialize(result) : result;
|
|
963
|
-
if (!item)
|
|
964
|
-
return;
|
|
965
|
-
this.items.set(id2, item);
|
|
966
|
-
return item;
|
|
967
|
-
}
|
|
968
|
-
async findByIndex(index, data, listener) {
|
|
969
|
-
if (listener)
|
|
1669
|
+
async find(options, listener) {
|
|
1670
|
+
if (listener) {
|
|
970
1671
|
this.listeners.set(listener, { callback: listener });
|
|
971
|
-
if (this.isComplete) {
|
|
972
|
-
const results2 = Array.from(this.items.values()).filter((item) => {
|
|
973
|
-
if (!item)
|
|
974
|
-
return false;
|
|
975
|
-
return indexQueryPredicate(item, data);
|
|
976
|
-
});
|
|
977
|
-
return results2;
|
|
978
1672
|
}
|
|
979
1673
|
const transaction = await this.dataStorage.getReadTransaction();
|
|
980
|
-
const
|
|
981
|
-
|
|
982
|
-
const id2 = item._id;
|
|
983
|
-
if (this.items.has(id2))
|
|
984
|
-
return this.items.get(id2);
|
|
985
|
-
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
986
|
-
return processedItem;
|
|
987
|
-
});
|
|
988
|
-
return results;
|
|
989
|
-
}
|
|
990
|
-
async findAll(listener) {
|
|
991
|
-
if (listener)
|
|
992
|
-
this.listeners.set(listener, { callback: listener });
|
|
993
|
-
if (this.isComplete)
|
|
994
|
-
return Array.from(this.items.values()).filter((e) => !!e);
|
|
995
|
-
const transaction = await this.dataStorage.getReadTransaction();
|
|
996
|
-
const dbResults = await transaction.findAll(this.storeName);
|
|
997
|
-
const items = dbResults.map((item) => {
|
|
998
|
-
const id2 = item._id;
|
|
999
|
-
if (this.items.has(id2))
|
|
1000
|
-
return this.items.get(id2);
|
|
1001
|
-
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
1002
|
-
this.items.set(processedItem._id, processedItem);
|
|
1003
|
-
return processedItem;
|
|
1004
|
-
});
|
|
1005
|
-
this.isComplete = true;
|
|
1006
|
-
return items;
|
|
1674
|
+
const results = await transaction.find(this.storeName, options);
|
|
1675
|
+
return results.map((item) => this.deserialize ? this.deserialize(item) : item);
|
|
1007
1676
|
}
|
|
1008
1677
|
}
|
|
1009
1678
|
|
|
@@ -1027,16 +1696,19 @@ class MasterDataStorage extends DataStorage {
|
|
|
1027
1696
|
}
|
|
1028
1697
|
getStore(storeName) {
|
|
1029
1698
|
if (!this.stores.has(storeName)) {
|
|
1030
|
-
const contextElement = this.arcContext.
|
|
1699
|
+
const contextElement = this.arcContext.elements.find((element) => element.name === storeName);
|
|
1031
1700
|
if (!contextElement)
|
|
1032
1701
|
console.log(`Can't find ${storeName} as context element`);
|
|
1033
|
-
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));
|
|
1034
1703
|
}
|
|
1035
1704
|
return this.stores.get(storeName);
|
|
1036
1705
|
}
|
|
1037
1706
|
async applyChanges(changes) {
|
|
1038
1707
|
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
1039
1708
|
}
|
|
1709
|
+
applySerializedChanges(changes) {
|
|
1710
|
+
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
|
|
1711
|
+
}
|
|
1040
1712
|
async commitChanges(changes) {
|
|
1041
1713
|
await Promise.all([
|
|
1042
1714
|
this.applyChanges(changes),
|
|
@@ -1050,192 +1722,350 @@ class MasterDataStorage extends DataStorage {
|
|
|
1050
1722
|
await this.rtcAdapter.sync(progressCallback);
|
|
1051
1723
|
}
|
|
1052
1724
|
}
|
|
1053
|
-
//
|
|
1054
|
-
class
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
this.
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
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
|
+
});
|
|
1070
1769
|
} else {
|
|
1071
|
-
|
|
1770
|
+
conditions.push(`${key} = ?`);
|
|
1771
|
+
params.push(value);
|
|
1072
1772
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1773
|
+
});
|
|
1774
|
+
return {
|
|
1775
|
+
sql: conditions.join(" AND "),
|
|
1776
|
+
params
|
|
1777
|
+
};
|
|
1075
1778
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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);
|
|
1081
1809
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1810
|
+
}
|
|
1811
|
+
|
|
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`);
|
|
1085
1817
|
}
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1818
|
+
const query2 = `UPDATE ${store} SET deleted = 1, lastUpdate = ? WHERE ${table.primaryKey} = ?`;
|
|
1819
|
+
await this.db.exec(query2, [new Date().toISOString(), id3]);
|
|
1820
|
+
}
|
|
1821
|
+
async set(store, item) {
|
|
1822
|
+
const schema = this.tables.get(store);
|
|
1823
|
+
if (!schema) {
|
|
1824
|
+
throw new Error(`Store ${store} not found`);
|
|
1091
1825
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
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();
|
|
1094
1851
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
return
|
|
1099
|
-
acc[key] = this.rawShape[key].parse(value2);
|
|
1100
|
-
return acc;
|
|
1101
|
-
}, {});
|
|
1102
|
-
}
|
|
1103
|
-
deserializePartial(value) {
|
|
1104
|
-
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1105
|
-
acc[key] = this.rawShape[key].deserialize(value2);
|
|
1106
|
-
return acc;
|
|
1107
|
-
}, {});
|
|
1108
|
-
}
|
|
1109
|
-
serializePartial(value) {
|
|
1110
|
-
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1111
|
-
acc[key] = this.rawShape[key].serialize(value2);
|
|
1112
|
-
return acc;
|
|
1113
|
-
}, {});
|
|
1852
|
+
if (Array.isArray(value) || typeof value === "object") {
|
|
1853
|
+
return JSON.stringify(value);
|
|
1854
|
+
}
|
|
1855
|
+
return value;
|
|
1114
1856
|
}
|
|
1115
1857
|
}
|
|
1116
|
-
function object(element) {
|
|
1117
|
-
return new ArcObject(element);
|
|
1118
|
-
}
|
|
1119
1858
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
this.
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
return value.map((v) => this.parent.deserialize(v));
|
|
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
|
+
});
|
|
1135
1873
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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
|
+
}
|
|
1139
1881
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1882
|
+
for (const schema of stores) {
|
|
1883
|
+
for (const table of schema.tables) {
|
|
1884
|
+
await this.createTableIfNotExists(table);
|
|
1885
|
+
}
|
|
1142
1886
|
}
|
|
1143
|
-
return this.parent.deserialize(value);
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
function array(element) {
|
|
1147
|
-
return new ArcArray(element);
|
|
1148
|
-
}
|
|
1149
|
-
// elements/boolean.ts
|
|
1150
|
-
class ArcBoolean extends ArcPrimitive {
|
|
1151
|
-
}
|
|
1152
|
-
function boolean() {
|
|
1153
|
-
return new ArcBoolean;
|
|
1154
|
-
}
|
|
1155
|
-
// elements/date.ts
|
|
1156
|
-
class ArcDate extends ArcAbstract {
|
|
1157
|
-
constructor() {
|
|
1158
|
-
super();
|
|
1159
1887
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
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();
|
|
1901
|
+
});
|
|
1902
|
+
const createTableSQL = `
|
|
1903
|
+
CREATE TABLE IF NOT EXISTS ${table.name} (
|
|
1904
|
+
${columns.join(`,
|
|
1905
|
+
`)}
|
|
1906
|
+
)
|
|
1907
|
+
`;
|
|
1908
|
+
await this.db.exec(createTableSQL);
|
|
1162
1909
|
}
|
|
1163
|
-
|
|
1164
|
-
return
|
|
1910
|
+
readWriteTransaction(stores) {
|
|
1911
|
+
return new SQLiteReadWriteTransaction(this.db, this.tables);
|
|
1165
1912
|
}
|
|
1166
|
-
|
|
1167
|
-
return new
|
|
1913
|
+
readTransaction(stores) {
|
|
1914
|
+
return new SQLiteReadTransaction(this.db, this.tables);
|
|
1168
1915
|
}
|
|
1169
1916
|
}
|
|
1170
|
-
|
|
1171
|
-
return
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
|
|
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 {
|
|
1178
1926
|
}
|
|
1179
|
-
|
|
1180
|
-
class
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1927
|
+
|
|
1928
|
+
class Model extends ModelBase {
|
|
1929
|
+
context;
|
|
1930
|
+
dataStorage;
|
|
1931
|
+
client;
|
|
1932
|
+
catchErrorCallback;
|
|
1933
|
+
queryCache = new QueryCache;
|
|
1934
|
+
constructor(context3, dataStorage, client, catchErrorCallback) {
|
|
1184
1935
|
super();
|
|
1185
|
-
this.
|
|
1186
|
-
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
|
+
};
|
|
1187
1962
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1192
|
-
acc[key] = this.element.parse(recordValue);
|
|
1193
|
-
return acc;
|
|
1194
|
-
}, {});
|
|
1963
|
+
commands() {
|
|
1964
|
+
const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
|
|
1965
|
+
return this.context.commandsClient(this.client, queryContext, this.dataStorage, this.catchErrorCallback);
|
|
1195
1966
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
return {};
|
|
1199
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1200
|
-
acc[key] = this.element.serialize(recordValue);
|
|
1201
|
-
return acc;
|
|
1202
|
-
}, {});
|
|
1967
|
+
fork() {
|
|
1968
|
+
return new ForkedModel(this.context, this.dataStorage.fork(), this.client, this.catchErrorCallback);
|
|
1203
1969
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
return {};
|
|
1207
|
-
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1208
|
-
acc[key] = this.element.deserialize(recordValue);
|
|
1209
|
-
return acc;
|
|
1210
|
-
}, {});
|
|
1970
|
+
get $debug() {
|
|
1971
|
+
return {};
|
|
1211
1972
|
}
|
|
1212
1973
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1974
|
+
|
|
1975
|
+
class ForkedModel extends Model {
|
|
1976
|
+
constructor(context3, dataStorage, client, catchErrorCallback) {
|
|
1977
|
+
super(context3, dataStorage, client, catchErrorCallback);
|
|
1978
|
+
}
|
|
1979
|
+
merge() {}
|
|
1215
1980
|
}
|
|
1216
|
-
|
|
1217
|
-
class
|
|
1218
|
-
|
|
1219
|
-
|
|
1981
|
+
|
|
1982
|
+
class RemoteModelClient extends ModelBase {
|
|
1983
|
+
context;
|
|
1984
|
+
apiBaseUrl;
|
|
1985
|
+
client;
|
|
1986
|
+
catchErrorCallback;
|
|
1987
|
+
constructor(context3, apiBaseUrl, client, catchErrorCallback) {
|
|
1220
1988
|
super();
|
|
1221
|
-
this.
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
|
|
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;
|
|
1228
2035
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
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
|
+
};
|
|
1231
2045
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
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;
|
|
1234
2067
|
}
|
|
1235
2068
|
}
|
|
1236
|
-
function stringEnum(...values) {
|
|
1237
|
-
return new ArcStringEnum(values);
|
|
1238
|
-
}
|
|
1239
2069
|
// rtc/client.ts
|
|
1240
2070
|
class RTCClient {
|
|
1241
2071
|
storage;
|
|
@@ -1259,41 +2089,9 @@ class RTCClient {
|
|
|
1259
2089
|
}
|
|
1260
2090
|
async performSync() {
|
|
1261
2091
|
this.connectWebSocket();
|
|
1262
|
-
const arcState = await this.storage.getStore("state").findById("$arc", (a) => a);
|
|
1263
|
-
const response = await fetch(`/ws/sync?lastSync=${arcState?.lastSyncDate || ""}`, {
|
|
1264
|
-
method: "GET",
|
|
1265
|
-
headers: {
|
|
1266
|
-
"Content-Type": "application/json",
|
|
1267
|
-
Authorization: `Bearer ${this.token}`
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
if (!response.ok) {
|
|
1271
|
-
throw new Error("Sync failed");
|
|
1272
|
-
}
|
|
1273
|
-
const { results, syncDate } = await response.json();
|
|
1274
|
-
const pendingStoreChanges = [];
|
|
1275
|
-
const transaction = await this.storage.getReadWriteTransaction();
|
|
1276
|
-
for (const { store, items } of results) {
|
|
1277
|
-
this.syncProgressCallback?.({ store, size: items.length });
|
|
1278
|
-
for (const item of items) {
|
|
1279
|
-
if (item.deleted) {
|
|
1280
|
-
await transaction.remove(store, item._id);
|
|
1281
|
-
} else {
|
|
1282
|
-
await transaction.set(store, item);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
await transaction.commit();
|
|
1287
|
-
const stateStorage = this.storage.getStore("state");
|
|
1288
|
-
await stateStorage.applyChanges([
|
|
1289
|
-
{
|
|
1290
|
-
type: "set",
|
|
1291
|
-
data: { _id: "$arc", lastSyncDate: syncDate }
|
|
1292
|
-
}
|
|
1293
|
-
]);
|
|
1294
2092
|
}
|
|
1295
2093
|
async connectWebSocket() {
|
|
1296
|
-
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}`);
|
|
1297
2095
|
this.openSocket = new Promise((resolve) => {
|
|
1298
2096
|
this._socket.addEventListener("open", () => {
|
|
1299
2097
|
this.reconnectAttempts = 0;
|
|
@@ -1343,138 +2141,160 @@ class RTCClient {
|
|
|
1343
2141
|
var rtcClientFactory = (token) => (storage) => {
|
|
1344
2142
|
return new RTCClient(storage, token);
|
|
1345
2143
|
};
|
|
1346
|
-
//
|
|
1347
|
-
class
|
|
1348
|
-
state;
|
|
1349
|
-
bindedChangeHandler = this.changeHandler.bind(this);
|
|
1350
|
-
store;
|
|
1351
|
-
constructor(state) {
|
|
1352
|
-
super();
|
|
1353
|
-
this.state = state;
|
|
1354
|
-
}
|
|
1355
|
-
async run(dataStorage, listener) {
|
|
1356
|
-
this.store = dataStorage.getStore("state");
|
|
1357
|
-
const result = await this.store.findById(this.state.name, this.bindedChangeHandler);
|
|
1358
|
-
this.lastResult = this.state.deserialize(result);
|
|
1359
|
-
if (listener)
|
|
1360
|
-
this.listener = listener;
|
|
1361
|
-
return this.lastResult;
|
|
1362
|
-
}
|
|
1363
|
-
onChange(change) {
|
|
1364
|
-
if (change.type === "set")
|
|
1365
|
-
return change.item;
|
|
1366
|
-
return false;
|
|
1367
|
-
}
|
|
1368
|
-
changeHandler(changes) {
|
|
1369
|
-
for (const change of changes) {
|
|
1370
|
-
const response = this.onChange(change);
|
|
1371
|
-
if (response !== false)
|
|
1372
|
-
this.lastResult = this.state.deserialize(response);
|
|
1373
|
-
}
|
|
1374
|
-
if (this.lastResult)
|
|
1375
|
-
this.nextResult(this.lastResult);
|
|
1376
|
-
}
|
|
1377
|
-
unsubscribe() {
|
|
1378
|
-
this.store.unsubscribe(this.bindedChangeHandler);
|
|
1379
|
-
}
|
|
1380
|
-
nextResult(result) {
|
|
1381
|
-
this.lastResult = result;
|
|
1382
|
-
this.listener?.(result);
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
// state/query-builder.ts
|
|
1387
|
-
class ArcStateQueryBuilder extends ArcQueryBuilder {
|
|
1388
|
-
state;
|
|
1389
|
-
constructor(state) {
|
|
1390
|
-
super();
|
|
1391
|
-
this.state = state;
|
|
1392
|
-
}
|
|
1393
|
-
toQuery() {
|
|
1394
|
-
return new ArcStateQuery(this.state);
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
// state/state.ts
|
|
1399
|
-
class ArcState extends ArcContextElement {
|
|
2144
|
+
// view/view.ts
|
|
2145
|
+
class ArcView extends ArcContextElementWithStore {
|
|
1400
2146
|
name;
|
|
2147
|
+
id;
|
|
1401
2148
|
schema;
|
|
1402
|
-
|
|
2149
|
+
_description;
|
|
2150
|
+
_elements;
|
|
2151
|
+
_handler;
|
|
2152
|
+
constructor(name, id3, schema) {
|
|
1403
2153
|
super();
|
|
1404
2154
|
this.name = name;
|
|
2155
|
+
this.id = id3;
|
|
1405
2156
|
this.schema = schema;
|
|
1406
2157
|
}
|
|
1407
|
-
|
|
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) => {
|
|
1408
2177
|
return {
|
|
1409
|
-
|
|
1410
|
-
|
|
2178
|
+
find: (options) => {
|
|
2179
|
+
throw new Error("Not implemented");
|
|
2180
|
+
}
|
|
1411
2181
|
};
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
queryBuilder() {
|
|
1417
|
-
return new ArcStateQueryBuilder(this);
|
|
1418
|
-
}
|
|
1419
|
-
commandContext(dataStorage, publishEvent) {
|
|
1420
|
-
const store = dataStorage.getStore("state");
|
|
2182
|
+
};
|
|
2183
|
+
commandContext = (dataStorage, publishEvent) => {
|
|
2184
|
+
const store = dataStorage.getStore(this.name);
|
|
1421
2185
|
return {
|
|
1422
|
-
|
|
1423
|
-
return store.
|
|
2186
|
+
find: async (options) => {
|
|
2187
|
+
return store.find(options);
|
|
1424
2188
|
},
|
|
1425
|
-
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
type: "modify",
|
|
1430
|
-
changes: data,
|
|
1431
|
-
from,
|
|
1432
|
-
to
|
|
2189
|
+
findOne: async (where) => {
|
|
2190
|
+
const result = await store.find({
|
|
2191
|
+
where,
|
|
2192
|
+
limit: 1
|
|
1433
2193
|
});
|
|
1434
|
-
|
|
1435
|
-
edit: async (editCallback) => {
|
|
1436
|
-
const { from, to } = await store.mutate(this.name, editCallback);
|
|
2194
|
+
return result[0];
|
|
1437
2195
|
}
|
|
1438
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;
|
|
1439
2246
|
}
|
|
1440
2247
|
}
|
|
1441
|
-
function
|
|
1442
|
-
return new
|
|
2248
|
+
function view(name, id3, schema) {
|
|
2249
|
+
return new ArcView(name, id3, schema);
|
|
1443
2250
|
}
|
|
1444
2251
|
export {
|
|
2252
|
+
view,
|
|
1445
2253
|
stringEnum,
|
|
1446
2254
|
string,
|
|
1447
|
-
state,
|
|
1448
2255
|
rtcClientFactory,
|
|
1449
2256
|
record,
|
|
2257
|
+
reactive,
|
|
1450
2258
|
object,
|
|
1451
2259
|
number,
|
|
1452
2260
|
id,
|
|
2261
|
+
event,
|
|
1453
2262
|
date,
|
|
1454
2263
|
customId,
|
|
2264
|
+
createSQLiteAdapterFactory,
|
|
1455
2265
|
context,
|
|
2266
|
+
command,
|
|
1456
2267
|
collection,
|
|
1457
2268
|
boolean,
|
|
1458
2269
|
array,
|
|
1459
2270
|
StoreState,
|
|
2271
|
+
SQLiteAdapter,
|
|
2272
|
+
RemoteModelClient,
|
|
2273
|
+
QueryCache,
|
|
2274
|
+
QueryBuilderContext,
|
|
2275
|
+
ModelBase,
|
|
2276
|
+
Model,
|
|
1460
2277
|
MasterStoreState,
|
|
1461
2278
|
MasterDataStorage,
|
|
1462
2279
|
ForkedStoreState,
|
|
2280
|
+
ForkedModel,
|
|
1463
2281
|
ForkedDataStorage,
|
|
1464
2282
|
DataStorage,
|
|
2283
|
+
ArcView,
|
|
1465
2284
|
ArcStringEnum,
|
|
1466
2285
|
ArcString,
|
|
1467
|
-
ArcState,
|
|
1468
2286
|
ArcRecord,
|
|
1469
2287
|
ArcQueryBuilder,
|
|
1470
2288
|
ArcQuery,
|
|
1471
2289
|
ArcOptional,
|
|
1472
2290
|
ArcObject,
|
|
1473
2291
|
ArcNumber,
|
|
1474
|
-
ArcIndexedCollection,
|
|
1475
2292
|
ArcId,
|
|
2293
|
+
ArcEvent,
|
|
1476
2294
|
ArcDate,
|
|
1477
2295
|
ArcCustomId,
|
|
2296
|
+
ArcContext,
|
|
2297
|
+
ArcCommand,
|
|
1478
2298
|
ArcCollectionQuery,
|
|
1479
2299
|
ArcCollection,
|
|
1480
2300
|
ArcBranded,
|