@arcote.tech/arc 0.0.13 → 0.0.15
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-change.d.ts +1 -0
- package/dist/collection/collection.d.ts +35 -26
- package/dist/collection/db.d.ts +11 -12
- package/dist/collection/index.d.ts +1 -1
- package/dist/collection/queries/abstract-collection-query.d.ts +11 -14
- package/dist/collection/queries/abstract-many-items.d.ts +22 -13
- package/dist/collection/queries/all-items.d.ts +4 -3
- package/dist/collection/queries/indexed.d.ts +8 -8
- package/dist/collection/queries/one-item.d.ts +14 -6
- package/dist/collection/queries/util.d.ts +3 -6
- package/dist/collection/query-builders/abstract-many-items.d.ts +6 -6
- package/dist/collection/query-builders/all-items.d.ts +1 -0
- package/dist/collection/query-builders/indexed.d.ts +1 -0
- package/dist/collection/query-builders/one-item.d.ts +1 -0
- package/dist/context/commands.d.ts +3 -1
- package/dist/context/context.d.ts +13 -23
- package/dist/context/element.d.ts +7 -5
- package/dist/context/index.d.ts +4 -0
- package/dist/context/query-builders.d.ts +5 -2
- package/dist/context/query.d.ts +6 -2
- package/dist/data-storage/DataStorage.d.ts +11 -0
- package/dist/data-storage/ForkStoreState.d.ts +32 -0
- package/dist/data-storage/StoreState.d.ts +24 -0
- package/dist/data-storage/data-storage-forked.d.ts +16 -0
- package/dist/data-storage/data-storage-master.d.ts +24 -0
- package/dist/data-storage/data-storage.d.ts +2 -0
- package/dist/data-storage/data-storage.interface.d.ts +37 -0
- package/dist/data-storage/index.d.ts +7 -0
- package/dist/data-storage/master-store-state.d.ts +17 -0
- package/dist/data-storage/store-state-fork.d.ts +15 -0
- package/dist/data-storage/store-state-master.d.ts +14 -0
- package/dist/data-storage/store-state.abstract.d.ts +22 -0
- package/dist/data-storage/store-state.d.ts +24 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/interface.d.ts +17 -0
- package/dist/elements/abstract-primitive.d.ts +1 -0
- package/dist/elements/abstract.d.ts +1 -0
- package/dist/elements/array.d.ts +1 -0
- package/dist/elements/boolean.d.ts +1 -0
- package/dist/elements/branded.d.ts +1 -0
- package/dist/elements/date.d.ts +1 -0
- package/dist/elements/default.d.ts +1 -0
- package/dist/elements/element.d.ts +1 -0
- package/dist/elements/id.d.ts +1 -0
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/number.d.ts +1 -0
- package/dist/elements/object.d.ts +1 -0
- package/dist/elements/optional.d.ts +1 -0
- package/dist/elements/string-enum.d.ts +1 -0
- package/dist/elements/string.d.ts +1 -0
- package/dist/elements/tests/object.test.d.ts +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +452 -397
- package/dist/model/model.d.ts +18 -19
- package/dist/rtc/client.d.ts +2 -1
- package/dist/rtc/deserializeChanges.d.ts +1 -0
- package/dist/rtc/index.d.ts +1 -0
- package/dist/rtc/messages.d.ts +6 -5
- package/dist/rtc/rtc.d.ts +10 -0
- package/dist/state/db.d.ts +14 -0
- package/dist/state/index.d.ts +3 -0
- package/dist/state/query-builder.d.ts +8 -0
- package/dist/state/query.d.ts +22 -0
- package/dist/state/state-change.d.ts +13 -0
- package/dist/state/state.d.ts +28 -0
- package/dist/state/util.d.ts +2 -0
- package/dist/utils.d.ts +17 -14
- package/package.json +2 -2
- package/dist/collection/in-memory-db-proxy.d.ts +0 -10
- package/dist/elements/instanceOf.d.ts +0 -19
- package/dist/elements/or.d.ts +0 -11
- package/dist/elements/recurent.d.ts +0 -20
- package/dist/model/index.d.ts +0 -4
- package/dist/model/query-builder.d.ts +0 -3
- package/dist/model/rtc.d.ts +0 -6
- package/dist/model/submodel.d.ts +0 -23
package/dist/index.js
CHANGED
|
@@ -1,48 +1,34 @@
|
|
|
1
|
-
// collection/collection.ts
|
|
2
|
-
import { apply } from "mutative";
|
|
3
|
-
|
|
4
1
|
// collection/queries/abstract-collection-query.ts
|
|
5
|
-
import { BehaviorSubject, debounceTime, Subject } from "rxjs";
|
|
6
|
-
|
|
7
2
|
class ArcCollectionQuery {
|
|
8
3
|
collection;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
subscriptions = [];
|
|
4
|
+
lastResult;
|
|
5
|
+
listener;
|
|
12
6
|
constructor(collection) {
|
|
13
7
|
this.collection = collection;
|
|
14
8
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const result = await this.fetch(transaction);
|
|
23
|
-
this.resultQueue$ = new BehaviorSubject(result);
|
|
24
|
-
const subscriptionDebounce = this.resultQueue$.pipe(debounceTime(50)).subscribe(this.result$);
|
|
25
|
-
this.subscriptions.push(subscriptionDebounce);
|
|
9
|
+
async run(dataStorage, listener) {
|
|
10
|
+
const store = dataStorage.getStore(this.collection.name);
|
|
11
|
+
const result = await this.fetch(store);
|
|
12
|
+
this.lastResult = result;
|
|
13
|
+
if (listener)
|
|
14
|
+
this.listener = listener;
|
|
15
|
+
return result;
|
|
26
16
|
}
|
|
27
|
-
changeHandler(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
this.nextResult(
|
|
17
|
+
changeHandler(changes) {
|
|
18
|
+
for (const change of changes) {
|
|
19
|
+
const response = this.onChange(change);
|
|
20
|
+
if (response !== false)
|
|
21
|
+
this.lastResult = response;
|
|
22
|
+
}
|
|
23
|
+
if (this.lastResult)
|
|
24
|
+
this.nextResult(this.lastResult);
|
|
35
25
|
}
|
|
36
26
|
unsubscribe() {
|
|
37
|
-
|
|
27
|
+
console.log("unsubscribe");
|
|
38
28
|
}
|
|
39
29
|
nextResult(result) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.resultQueue$.next(result);
|
|
43
|
-
}
|
|
44
|
-
isUnsubscribed() {
|
|
45
|
-
return this.subscriptions.every((sub) => sub.closed);
|
|
30
|
+
this.lastResult = result;
|
|
31
|
+
this.listener?.(result);
|
|
46
32
|
}
|
|
47
33
|
}
|
|
48
34
|
|
|
@@ -72,17 +58,17 @@ class ArcManyItemsQuery extends ArcCollectionQuery {
|
|
|
72
58
|
this.sortFn = sortFn;
|
|
73
59
|
}
|
|
74
60
|
onChange(change) {
|
|
75
|
-
const lastResult = this.
|
|
76
|
-
const lastResultAsArray = lastResult
|
|
77
|
-
const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.
|
|
61
|
+
const lastResult = this.lastResult;
|
|
62
|
+
const lastResultAsArray = lastResult?.toArray() || [];
|
|
63
|
+
const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
|
|
78
64
|
const isInLastResult = index !== -1;
|
|
79
|
-
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.
|
|
65
|
+
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
80
66
|
if (isInLastResult && shouldBeInTheResult)
|
|
81
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.
|
|
67
|
+
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
|
|
82
68
|
else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
|
|
83
69
|
return this.createResult(lastResultAsArray.toSpliced(index, 1));
|
|
84
70
|
else if (!isInLastResult && shouldBeInTheResult)
|
|
85
|
-
return this.createResult(lastResultAsArray.concat(change.
|
|
71
|
+
return this.createResult(lastResultAsArray.concat(change.item));
|
|
86
72
|
return false;
|
|
87
73
|
}
|
|
88
74
|
createResult(result) {
|
|
@@ -104,8 +90,9 @@ class ArcManyItemsQuery extends ArcCollectionQuery {
|
|
|
104
90
|
|
|
105
91
|
// collection/queries/all-items.ts
|
|
106
92
|
class ArcAllItemsQuery extends ArcManyItemsQuery {
|
|
107
|
-
async fetch(
|
|
108
|
-
|
|
93
|
+
async fetch(store) {
|
|
94
|
+
const results = await store.findAll(this.changeHandler.bind(this));
|
|
95
|
+
return this.createFiltredResult(results);
|
|
109
96
|
}
|
|
110
97
|
}
|
|
111
98
|
|
|
@@ -179,8 +166,9 @@ class ArcIndexedItemsQuery extends ArcManyItemsQuery {
|
|
|
179
166
|
return false;
|
|
180
167
|
return true;
|
|
181
168
|
}
|
|
182
|
-
async fetch(
|
|
183
|
-
|
|
169
|
+
async fetch(store) {
|
|
170
|
+
const results = await store.findByIndex(this.index, this.data, this.changeHandler.bind(this));
|
|
171
|
+
return this.createFiltredResult(results);
|
|
184
172
|
}
|
|
185
173
|
}
|
|
186
174
|
|
|
@@ -210,16 +198,15 @@ class ArcOneItemQuery extends ArcCollectionQuery {
|
|
|
210
198
|
this.id = id;
|
|
211
199
|
}
|
|
212
200
|
onChange(change) {
|
|
213
|
-
if (change.type === "delete"
|
|
201
|
+
if (change.type === "delete")
|
|
214
202
|
return;
|
|
215
|
-
if (change.type === "set"
|
|
216
|
-
return change.
|
|
203
|
+
if (change.type === "set")
|
|
204
|
+
return change.item;
|
|
217
205
|
return false;
|
|
218
206
|
}
|
|
219
|
-
fetch(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return transaction.findById(this.collection, this.id);
|
|
207
|
+
async fetch(store) {
|
|
208
|
+
const result = await store.findById(this.id, this.changeHandler.bind(this));
|
|
209
|
+
return result;
|
|
223
210
|
}
|
|
224
211
|
}
|
|
225
212
|
|
|
@@ -271,61 +258,44 @@ class ArcCollection {
|
|
|
271
258
|
one: (id) => new ArcOneItemQueryBuilder(this, id)
|
|
272
259
|
};
|
|
273
260
|
}
|
|
274
|
-
commandContext(
|
|
261
|
+
commandContext(dataStorage) {
|
|
262
|
+
const store = dataStorage.getStore(this.name);
|
|
275
263
|
return {
|
|
276
|
-
add: (data) => {
|
|
264
|
+
add: async (data) => {
|
|
277
265
|
const id = this.id.generate();
|
|
278
266
|
const parsed = this.schema.parse(data);
|
|
279
267
|
const body = {
|
|
280
268
|
_id: id,
|
|
281
269
|
...parsed
|
|
282
270
|
};
|
|
283
|
-
|
|
271
|
+
await store.set(body);
|
|
284
272
|
return { id };
|
|
285
273
|
},
|
|
286
|
-
remove: (id) => {
|
|
287
|
-
|
|
274
|
+
remove: async (id) => {
|
|
275
|
+
await store.remove(id);
|
|
288
276
|
return { success: true };
|
|
289
277
|
},
|
|
290
|
-
set: (id, data) => {
|
|
278
|
+
set: async (id, data) => {
|
|
291
279
|
const parsed = this.schema.parse(data);
|
|
292
280
|
const body = {
|
|
293
281
|
_id: id,
|
|
294
282
|
...parsed
|
|
295
283
|
};
|
|
296
|
-
|
|
284
|
+
await store.set(body);
|
|
297
285
|
return { success: true };
|
|
298
286
|
},
|
|
287
|
+
all: async () => {
|
|
288
|
+
return store.findAll();
|
|
289
|
+
},
|
|
299
290
|
one: async (id) => {
|
|
300
|
-
|
|
301
|
-
|
|
291
|
+
return store.findById(id);
|
|
292
|
+
},
|
|
293
|
+
modify: async (id, data) => {
|
|
294
|
+
const deserialized = this.schema.deserialize(data);
|
|
295
|
+
await store.modify(id, deserialized);
|
|
302
296
|
}
|
|
303
297
|
};
|
|
304
298
|
}
|
|
305
|
-
async applyChange(transaction, change, events) {
|
|
306
|
-
switch (change.type) {
|
|
307
|
-
case "set":
|
|
308
|
-
await transaction.set(this, change.body);
|
|
309
|
-
events.push(change);
|
|
310
|
-
break;
|
|
311
|
-
case "delete":
|
|
312
|
-
await transaction.remove(this, change.id);
|
|
313
|
-
events.push(change);
|
|
314
|
-
break;
|
|
315
|
-
case "mutate":
|
|
316
|
-
const existingObject = await transaction.findById(this, change.id);
|
|
317
|
-
if (existingObject) {
|
|
318
|
-
const updatedObject = apply(existingObject, change.patches);
|
|
319
|
-
await transaction.set(this, updatedObject);
|
|
320
|
-
events.push({
|
|
321
|
-
type: "set",
|
|
322
|
-
collection: change.collection,
|
|
323
|
-
body: updatedObject
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
299
|
indexBy(indexes) {
|
|
330
300
|
return new ArcIndexedCollection(this.name, this.id, this.schema, this.options, indexes);
|
|
331
301
|
}
|
|
@@ -337,6 +307,20 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
337
307
|
super(name, id, schema, options);
|
|
338
308
|
this.indexes = indexes;
|
|
339
309
|
}
|
|
310
|
+
commandContext(dataStorage) {
|
|
311
|
+
const superContext = super.commandContext(dataStorage);
|
|
312
|
+
return new Proxy(superContext, {
|
|
313
|
+
get: (target, name) => {
|
|
314
|
+
if (name in target) {
|
|
315
|
+
return target[name];
|
|
316
|
+
}
|
|
317
|
+
if (!(name in this.indexes)) {
|
|
318
|
+
throw new Error(`Index "${name}" not found in collection "${this.name}"`);
|
|
319
|
+
}
|
|
320
|
+
return (data) => dataStorage.getStore(this.name, this.deserialize.bind(this)).findByIndex(name, data);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
340
324
|
queryBuilder() {
|
|
341
325
|
const superQueryBuilder = super.queryBuilder();
|
|
342
326
|
return new Proxy({}, {
|
|
@@ -352,22 +336,25 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
352
336
|
}
|
|
353
337
|
}
|
|
354
338
|
// context/context.ts
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return new ArcContext(collections);
|
|
339
|
+
function context(elements, commands, version) {
|
|
340
|
+
return new ArcContext(elements, commands, version);
|
|
358
341
|
}
|
|
359
342
|
|
|
360
343
|
class ArcContext {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
344
|
+
elements;
|
|
345
|
+
commands;
|
|
346
|
+
version;
|
|
347
|
+
elementsMap;
|
|
348
|
+
constructor(elements, commands, version) {
|
|
349
|
+
this.elements = elements;
|
|
350
|
+
this.commands = commands;
|
|
351
|
+
this.version = version;
|
|
352
|
+
this.elementsMap = Object.fromEntries(elements.map((collection3) => [collection3.name, collection3]));
|
|
366
353
|
}
|
|
367
354
|
queryBuilder() {
|
|
368
355
|
return new Proxy({}, {
|
|
369
356
|
get: (target, name) => {
|
|
370
|
-
const collection3 = this.
|
|
357
|
+
const collection3 = this.elementsMap[name];
|
|
371
358
|
if (!collection3) {
|
|
372
359
|
throw new Error(`Collection "${name}" not found`);
|
|
373
360
|
}
|
|
@@ -375,57 +362,311 @@ class ArcContext {
|
|
|
375
362
|
}
|
|
376
363
|
});
|
|
377
364
|
}
|
|
378
|
-
commandContext(
|
|
379
|
-
|
|
380
|
-
const collectionDrafts = [];
|
|
381
|
-
function getDraft(collection3, base) {
|
|
382
|
-
const [draft, finalize2] = create(base, { enablePatches: true });
|
|
383
|
-
collectionDrafts.push({
|
|
384
|
-
collection: collection3,
|
|
385
|
-
finalize: finalize2
|
|
386
|
-
});
|
|
387
|
-
return draft;
|
|
388
|
-
}
|
|
389
|
-
const contextProxy = new Proxy({}, {
|
|
365
|
+
commandContext(dataStorage) {
|
|
366
|
+
return new Proxy({}, {
|
|
390
367
|
get: (target, name) => {
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
throw new Error(`Collection "${name}" not found`);
|
|
394
|
-
}
|
|
395
|
-
return collection3.commandContext(transaction, changes, getDraft);
|
|
368
|
+
const element = this.elementsMap[name];
|
|
369
|
+
return element.commandContext(dataStorage);
|
|
396
370
|
}
|
|
397
371
|
});
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// data-storage/store-state.abstract.ts
|
|
375
|
+
class StoreState {
|
|
376
|
+
storeName;
|
|
377
|
+
dataStorage;
|
|
378
|
+
deserialize;
|
|
379
|
+
listeners = new Set;
|
|
380
|
+
constructor(storeName, dataStorage, deserialize) {
|
|
381
|
+
this.storeName = storeName;
|
|
382
|
+
this.dataStorage = dataStorage;
|
|
383
|
+
this.deserialize = deserialize;
|
|
384
|
+
}
|
|
385
|
+
fork() {
|
|
386
|
+
return new ForkedStoreState(this.storeName, this.dataStorage, this, this.deserialize);
|
|
387
|
+
}
|
|
388
|
+
async set(item) {
|
|
389
|
+
const change = {
|
|
390
|
+
type: "set",
|
|
391
|
+
data: item
|
|
392
|
+
};
|
|
393
|
+
await this.applyChanges([change]);
|
|
394
|
+
}
|
|
395
|
+
async remove(id) {
|
|
396
|
+
const change = {
|
|
397
|
+
type: "delete",
|
|
398
|
+
id
|
|
399
|
+
};
|
|
400
|
+
await this.applyChanges([change]);
|
|
401
|
+
}
|
|
402
|
+
async modify(id, data) {
|
|
403
|
+
const change = {
|
|
404
|
+
type: "modify",
|
|
405
|
+
id,
|
|
406
|
+
data
|
|
407
|
+
};
|
|
408
|
+
await this.applyChanges([change]);
|
|
409
|
+
}
|
|
410
|
+
unsubscribe(listener) {
|
|
411
|
+
this.listeners.delete(listener);
|
|
412
|
+
}
|
|
413
|
+
notifyListeners(events) {
|
|
414
|
+
for (const listener of this.listeners) {
|
|
415
|
+
listener.callback(events);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// data-storage/store-state-fork.ts
|
|
421
|
+
class ForkedStoreState extends StoreState {
|
|
422
|
+
master;
|
|
423
|
+
changedItems = new Map;
|
|
424
|
+
changes = [];
|
|
425
|
+
constructor(storeName, dataStorage, master, deserialize) {
|
|
426
|
+
super(storeName, dataStorage, deserialize);
|
|
427
|
+
this.master = master;
|
|
428
|
+
}
|
|
429
|
+
async applyChanges(changes) {
|
|
430
|
+
const events = [];
|
|
431
|
+
for (const change of changes) {
|
|
432
|
+
switch (change.type) {
|
|
433
|
+
case "set":
|
|
434
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
435
|
+
this.changedItems.set(change.data._id, item);
|
|
436
|
+
events.push({
|
|
437
|
+
type: "set",
|
|
438
|
+
item: change.data,
|
|
439
|
+
id: change.data._id
|
|
440
|
+
});
|
|
441
|
+
break;
|
|
442
|
+
case "delete":
|
|
443
|
+
this.changedItems.set(change.id, null);
|
|
444
|
+
events.push({
|
|
445
|
+
type: "delete",
|
|
446
|
+
item: null,
|
|
447
|
+
id: change.id
|
|
448
|
+
});
|
|
449
|
+
break;
|
|
450
|
+
case "modify":
|
|
451
|
+
{
|
|
452
|
+
const existing = await this.findById(change.id);
|
|
453
|
+
const updated = existing ? { ...existing, ...change.data } : change.data;
|
|
454
|
+
const item2 = this.deserialize ? this.deserialize(updated) : updated;
|
|
455
|
+
this.changedItems.set(change.id, item2);
|
|
456
|
+
events.push({
|
|
457
|
+
type: "set",
|
|
458
|
+
item: item2,
|
|
459
|
+
id: change.id
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
407
463
|
}
|
|
408
|
-
|
|
464
|
+
this.changes.push(change);
|
|
409
465
|
}
|
|
410
|
-
|
|
466
|
+
if (events.length > 0) {
|
|
467
|
+
this.notifyListeners(events);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async findById(id, listener) {
|
|
471
|
+
if (listener)
|
|
472
|
+
this.listeners.add({ callback: listener, id });
|
|
473
|
+
if (this.changedItems.has(id))
|
|
474
|
+
return this.changedItems.get(id);
|
|
475
|
+
return await this.master.findById(id);
|
|
476
|
+
}
|
|
477
|
+
async findByIndex(index, data, listener) {
|
|
478
|
+
if (listener)
|
|
479
|
+
this.listeners.add({ callback: listener });
|
|
480
|
+
const parentResult = await this.master.findByIndex(index, data);
|
|
481
|
+
return parentResult.map((item) => {
|
|
482
|
+
const id = item._id;
|
|
483
|
+
if (this.changedItems.has(id))
|
|
484
|
+
return this.changedItems.get(id);
|
|
485
|
+
return item;
|
|
486
|
+
});
|
|
411
487
|
}
|
|
412
|
-
async
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
488
|
+
async findAll(listener) {
|
|
489
|
+
if (listener)
|
|
490
|
+
this.listeners.add({ callback: listener });
|
|
491
|
+
const parentResult = await this.master.findAll();
|
|
492
|
+
return parentResult.map((item) => {
|
|
493
|
+
const id = item._id;
|
|
494
|
+
if (this.changedItems.has(id))
|
|
495
|
+
return this.changedItems.get(id);
|
|
496
|
+
return item;
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// data-storage/data-storage-forked.ts
|
|
502
|
+
class ForkedDataStorage {
|
|
503
|
+
master;
|
|
504
|
+
stores = new Map;
|
|
505
|
+
constructor(master) {
|
|
506
|
+
this.master = master;
|
|
507
|
+
}
|
|
508
|
+
getReadTransaction() {
|
|
509
|
+
return this.master.getReadTransaction();
|
|
510
|
+
}
|
|
511
|
+
getReadWriteTransaction() {
|
|
512
|
+
return this.master.getReadWriteTransaction();
|
|
513
|
+
}
|
|
514
|
+
getStore(storeName, deserialize) {
|
|
515
|
+
if (this.stores.has(storeName))
|
|
516
|
+
return this.stores.get(storeName);
|
|
517
|
+
const masterStorage = this.master.getStore(storeName, deserialize);
|
|
518
|
+
const storage = new ForkedStoreState(storeName, this, masterStorage, deserialize);
|
|
519
|
+
this.stores.set(storeName, storage);
|
|
520
|
+
return storage;
|
|
521
|
+
}
|
|
522
|
+
fork() {
|
|
523
|
+
return new ForkedDataStorage(this);
|
|
524
|
+
}
|
|
525
|
+
async merge() {
|
|
526
|
+
for (const store of this.stores.values()) {
|
|
527
|
+
await store.master?.applyChanges(store.changes);
|
|
416
528
|
}
|
|
417
|
-
return collection3.applyChange(transaction, change, events);
|
|
418
529
|
}
|
|
419
|
-
|
|
420
|
-
|
|
530
|
+
}
|
|
531
|
+
// data-storage/store-state-master.ts
|
|
532
|
+
class MasterStoreState extends StoreState {
|
|
533
|
+
items = new Map;
|
|
534
|
+
isComplete = false;
|
|
535
|
+
constructor(storeName, dataStorage, deserialize) {
|
|
536
|
+
super(storeName, dataStorage, deserialize);
|
|
537
|
+
}
|
|
538
|
+
async applyChanges(changes) {
|
|
539
|
+
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
540
|
+
const events = [];
|
|
541
|
+
for (const change of changes) {
|
|
542
|
+
switch (change.type) {
|
|
543
|
+
case "set":
|
|
544
|
+
await transaction.set(this.storeName, change.data);
|
|
545
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
546
|
+
this.items.set(change.data._id, item);
|
|
547
|
+
events.push({
|
|
548
|
+
type: "set",
|
|
549
|
+
item: change.data,
|
|
550
|
+
id: change.data._id
|
|
551
|
+
});
|
|
552
|
+
break;
|
|
553
|
+
case "delete":
|
|
554
|
+
await transaction.remove(this.storeName, change.id);
|
|
555
|
+
this.items.set(change.id, null);
|
|
556
|
+
events.push({
|
|
557
|
+
type: "delete",
|
|
558
|
+
item: null,
|
|
559
|
+
id: change.id
|
|
560
|
+
});
|
|
561
|
+
break;
|
|
562
|
+
case "modify":
|
|
563
|
+
{
|
|
564
|
+
const existing = await transaction.findById(this.storeName, change.id);
|
|
565
|
+
const updated = existing ? { ...existing, ...change.data } : change.data;
|
|
566
|
+
await transaction.set(this.storeName, updated);
|
|
567
|
+
const item2 = this.deserialize ? this.deserialize(updated) : updated;
|
|
568
|
+
this.items.set(change.id, item2);
|
|
569
|
+
events.push({
|
|
570
|
+
type: "set",
|
|
571
|
+
item: item2,
|
|
572
|
+
id: change.id
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
await transaction.commit();
|
|
579
|
+
if (events.length > 0) {
|
|
580
|
+
this.notifyListeners(events);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
async findById(id, listener) {
|
|
584
|
+
if (listener)
|
|
585
|
+
this.listeners.add({ callback: listener, id });
|
|
586
|
+
if (this.items.has(id))
|
|
587
|
+
return this.items.get(id);
|
|
588
|
+
const transaction = await this.dataStorage.getReadTransaction();
|
|
589
|
+
const result = await transaction.findById(this.storeName, id);
|
|
590
|
+
if (!result)
|
|
591
|
+
return;
|
|
592
|
+
const item = this.deserialize ? this.deserialize(result) : result;
|
|
593
|
+
this.items.set(id, item);
|
|
594
|
+
return item;
|
|
595
|
+
}
|
|
596
|
+
async findByIndex(index, data, listener) {
|
|
597
|
+
if (listener)
|
|
598
|
+
this.listeners.add({ callback: listener });
|
|
599
|
+
if (this.isComplete) {
|
|
600
|
+
const results2 = Array.from(this.items.values()).filter((item) => {
|
|
601
|
+
if (!item)
|
|
602
|
+
return false;
|
|
603
|
+
const notCorrect = Object.entries(data).some(([key, value]) => item[key] !== value);
|
|
604
|
+
return !notCorrect;
|
|
605
|
+
});
|
|
606
|
+
return results2;
|
|
607
|
+
}
|
|
608
|
+
const transaction = await this.dataStorage.getReadTransaction();
|
|
609
|
+
const dbResults = await transaction.findByIndex(this.storeName, index, data);
|
|
610
|
+
const results = dbResults.map((item) => {
|
|
611
|
+
const id = item._id;
|
|
612
|
+
if (this.items.has(id))
|
|
613
|
+
return this.items.get(id);
|
|
614
|
+
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
615
|
+
return processedItem;
|
|
616
|
+
});
|
|
617
|
+
return results;
|
|
618
|
+
}
|
|
619
|
+
async findAll(listener) {
|
|
620
|
+
if (listener)
|
|
621
|
+
this.listeners.add({ callback: listener });
|
|
622
|
+
if (this.isComplete)
|
|
623
|
+
return Array.from(this.items.values()).filter((e) => !!e);
|
|
624
|
+
const transaction = await this.dataStorage.getReadTransaction();
|
|
625
|
+
const dbResults = await transaction.findAll(this.storeName);
|
|
626
|
+
const items = dbResults.map((item) => {
|
|
627
|
+
const id = item._id;
|
|
628
|
+
if (this.items.has(id))
|
|
629
|
+
return this.items.get(id);
|
|
630
|
+
const processedItem = this.deserialize ? this.deserialize(item) : item;
|
|
631
|
+
this.items.set(processedItem._id, processedItem);
|
|
632
|
+
return processedItem;
|
|
633
|
+
});
|
|
634
|
+
this.isComplete = true;
|
|
635
|
+
return items;
|
|
421
636
|
}
|
|
422
637
|
}
|
|
423
638
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
639
|
+
// data-storage/data-storage-master.ts
|
|
640
|
+
class MasterDataStorage {
|
|
641
|
+
dbAdapter;
|
|
642
|
+
arcContext;
|
|
643
|
+
stores = new Map;
|
|
644
|
+
rtcAdapter;
|
|
645
|
+
constructor(dbAdapter, rtcAdapterFactory, arcContext) {
|
|
646
|
+
this.dbAdapter = dbAdapter;
|
|
647
|
+
this.arcContext = arcContext;
|
|
648
|
+
this.rtcAdapter = rtcAdapterFactory(this);
|
|
649
|
+
}
|
|
650
|
+
async getReadTransaction() {
|
|
651
|
+
return (await this.dbAdapter).readTransaction();
|
|
652
|
+
}
|
|
653
|
+
async getReadWriteTransaction() {
|
|
654
|
+
return (await this.dbAdapter).readWriteTransaction();
|
|
655
|
+
}
|
|
656
|
+
getStore(storeName) {
|
|
657
|
+
if (!this.stores.has(storeName)) {
|
|
658
|
+
const contextElement = this.arcContext.elementsMap[storeName];
|
|
659
|
+
if (!contextElement)
|
|
660
|
+
console.log(`Can't find ${storeName} as context element`);
|
|
661
|
+
this.stores.set(storeName, new MasterStoreState(storeName, this, contextElement && contextElement.deserialize ? (a) => contextElement.deserialize(a) : undefined));
|
|
662
|
+
}
|
|
663
|
+
return this.stores.get(storeName);
|
|
664
|
+
}
|
|
665
|
+
fork() {
|
|
666
|
+
return new ForkedDataStorage(this);
|
|
667
|
+
}
|
|
668
|
+
async sync(progressCallback) {
|
|
669
|
+
await this.rtcAdapter.sync(progressCallback);
|
|
429
670
|
}
|
|
430
671
|
}
|
|
431
672
|
// elements/optional.ts
|
|
@@ -689,257 +930,31 @@ class ArcStringEnum extends ArcAbstract {
|
|
|
689
930
|
return this.values;
|
|
690
931
|
}
|
|
691
932
|
}
|
|
692
|
-
// model/model.ts
|
|
693
|
-
import { Subject as Subject2 } from "rxjs";
|
|
694
|
-
function model(context3, dependencies) {
|
|
695
|
-
return new ArcModel(context3, dependencies.dbAdapterFactory, dependencies.rtcAdapterFactory);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
class ArcModel {
|
|
699
|
-
context3;
|
|
700
|
-
rtc;
|
|
701
|
-
dbAdapterPromise;
|
|
702
|
-
changes$ = new Subject2;
|
|
703
|
-
constructor(context3, dbAdapterFactory, rtcAdapterFactory) {
|
|
704
|
-
this.context = context3;
|
|
705
|
-
this.rtc = rtcAdapterFactory(this);
|
|
706
|
-
this.dbAdapterPromise = dbAdapterFactory(this.context);
|
|
707
|
-
}
|
|
708
|
-
query(queryFactory) {
|
|
709
|
-
const queryBuilder = this.context.queryBuilder();
|
|
710
|
-
const query = queryFactory(queryBuilder).toQuery();
|
|
711
|
-
this.runQuery(query);
|
|
712
|
-
return query;
|
|
713
|
-
}
|
|
714
|
-
async runQuery(query) {
|
|
715
|
-
const dbAdapter = await this.dbAdapterPromise;
|
|
716
|
-
query.run(dbAdapter, this.changes$);
|
|
717
|
-
}
|
|
718
|
-
commands() {
|
|
719
|
-
return new Proxy({}, {
|
|
720
|
-
get: (target, name) => {
|
|
721
|
-
if (name in this.context.commands) {
|
|
722
|
-
return async (...args) => {
|
|
723
|
-
const { result } = await this.executeCommand(this.context.commands[name], ...args);
|
|
724
|
-
return result;
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
console.warn(`Command '${name}' not found in the context.`);
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
async executeCommand(command, ...args) {
|
|
733
|
-
const dbAdapter = await this.dbAdapterPromise;
|
|
734
|
-
const transaction = await dbAdapter.readWriteTransaction(this.context.collections);
|
|
735
|
-
const { context: context3, finalize } = await this.context.commandContext(transaction);
|
|
736
|
-
const result = await command(context3, ...args);
|
|
737
|
-
const changes = await finalize();
|
|
738
|
-
await this.applyChangesForTransaction(transaction, changes);
|
|
739
|
-
this.rtc.changesExecuted(changes);
|
|
740
|
-
return { changes, result };
|
|
741
|
-
}
|
|
742
|
-
async applyChangesForTransaction(transaction, changes) {
|
|
743
|
-
const events = [];
|
|
744
|
-
for (const change of changes) {
|
|
745
|
-
await this.context.applyChange(transaction, change, events);
|
|
746
|
-
}
|
|
747
|
-
await transaction.commit();
|
|
748
|
-
events.forEach((change) => this.notifyChange(change));
|
|
749
|
-
}
|
|
750
|
-
notifyChange(change) {
|
|
751
|
-
this.changes$.next(change);
|
|
752
|
-
}
|
|
753
|
-
async applyChanges(changes) {
|
|
754
|
-
const dbAdapter = await this.dbAdapterPromise;
|
|
755
|
-
const transaction = await dbAdapter.readWriteTransaction(this.context.collections);
|
|
756
|
-
await this.applyChangesForTransaction(transaction, changes);
|
|
757
|
-
}
|
|
758
|
-
getCollection(name) {
|
|
759
|
-
return this.context.collectionsMap[name];
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// model/submodel.ts
|
|
763
|
-
import { Subject as Subject3 } from "rxjs";
|
|
764
|
-
|
|
765
|
-
// collection/in-memory-db-proxy.ts
|
|
766
|
-
class InMemoryTransaction {
|
|
767
|
-
parentTransaction;
|
|
768
|
-
local;
|
|
769
|
-
constructor(parentTransaction, local) {
|
|
770
|
-
this.parentTransaction = parentTransaction;
|
|
771
|
-
this.local = local;
|
|
772
|
-
}
|
|
773
|
-
async set(collection3, data) {
|
|
774
|
-
let collectionLocal = this.local.get(collection3.name);
|
|
775
|
-
if (!collectionLocal) {
|
|
776
|
-
collectionLocal = new Map;
|
|
777
|
-
this.local.set(collection3.name, collectionLocal);
|
|
778
|
-
}
|
|
779
|
-
collectionLocal.set(data._id, data);
|
|
780
|
-
}
|
|
781
|
-
async remove(collection3, id3) {
|
|
782
|
-
let collectionLocal = this.local.get(collection3.name);
|
|
783
|
-
if (!collectionLocal) {
|
|
784
|
-
collectionLocal = new Map;
|
|
785
|
-
this.local.set(collection3.name, collectionLocal);
|
|
786
|
-
}
|
|
787
|
-
collectionLocal.set(id3, null);
|
|
788
|
-
}
|
|
789
|
-
async findById(collection3, id3) {
|
|
790
|
-
const localItem = this.local.get(collection3.name)?.get(id3);
|
|
791
|
-
if (localItem !== undefined) {
|
|
792
|
-
return localItem === null ? undefined : localItem;
|
|
793
|
-
}
|
|
794
|
-
return this.parentTransaction.findById(collection3, id3);
|
|
795
|
-
}
|
|
796
|
-
async findByIndex(collection3, index, data) {
|
|
797
|
-
const parentResults = await this.parentTransaction.findByIndex(collection3, index, data);
|
|
798
|
-
const localResults = Array.from(this.local.get(collection3.name)?.values() || []).filter((item) => item !== null && this.matchesIndex(item, index, data));
|
|
799
|
-
return this.mergeResults(parentResults, localResults);
|
|
800
|
-
}
|
|
801
|
-
async findAll(collection3) {
|
|
802
|
-
const parentResults = await this.parentTransaction.findAll(collection3);
|
|
803
|
-
const localResults = Array.from(this.local.get(collection3.name)?.values() || []).filter((item) => item !== null);
|
|
804
|
-
return this.mergeResults(parentResults, localResults);
|
|
805
|
-
}
|
|
806
|
-
async commit() {
|
|
807
|
-
}
|
|
808
|
-
matchesIndex(item, index, data) {
|
|
809
|
-
return Object.entries(data).every(([key, value]) => item[key] === value);
|
|
810
|
-
}
|
|
811
|
-
mergeResults(parentResults, localResults) {
|
|
812
|
-
const mergedMap = new Map;
|
|
813
|
-
parentResults.forEach((item) => mergedMap.set(item._id, item));
|
|
814
|
-
localResults.forEach((item) => mergedMap.set(item._id, item));
|
|
815
|
-
return Array.from(mergedMap.values());
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
class InMemoryDatabaseProxyAdapter {
|
|
820
|
-
parentAdapter;
|
|
821
|
-
local = new Map;
|
|
822
|
-
constructor(parentAdapter) {
|
|
823
|
-
this.parentAdapter = parentAdapter;
|
|
824
|
-
}
|
|
825
|
-
readWriteTransaction(collections) {
|
|
826
|
-
const parentTransaction = this.parentAdapter.readWriteTransaction(collections);
|
|
827
|
-
return new InMemoryTransaction(parentTransaction, this.local);
|
|
828
|
-
}
|
|
829
|
-
readTransaction(collections) {
|
|
830
|
-
const parentTransaction = this.parentAdapter.readTransaction(collections);
|
|
831
|
-
return new InMemoryTransaction(parentTransaction, this.local);
|
|
832
|
-
}
|
|
833
|
-
clearLocal() {
|
|
834
|
-
this.local.clear();
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// model/submodel.ts
|
|
839
|
-
class ArcSubModel {
|
|
840
|
-
parentModel;
|
|
841
|
-
changes$ = new Subject3;
|
|
842
|
-
proxyAdapterPromise;
|
|
843
|
-
uncommitedChanges = [];
|
|
844
|
-
constructor(parentModel) {
|
|
845
|
-
this.parentModel = parentModel;
|
|
846
|
-
this.parentModel.changes$.subscribe(this.changes$);
|
|
847
|
-
this.proxyAdapterPromise = this.parentModel.dbAdapterPromise.then((dbAdapter) => new InMemoryDatabaseProxyAdapter(dbAdapter));
|
|
848
|
-
}
|
|
849
|
-
query(queryFactory) {
|
|
850
|
-
const queryBuilder = this.context.queryBuilder();
|
|
851
|
-
const query = queryFactory(queryBuilder).toQuery();
|
|
852
|
-
this.runQuery(query);
|
|
853
|
-
return query;
|
|
854
|
-
}
|
|
855
|
-
async runQuery(query) {
|
|
856
|
-
const proxyAdapter = await this.proxyAdapterPromise;
|
|
857
|
-
query.run(proxyAdapter, this.changes$);
|
|
858
|
-
}
|
|
859
|
-
commands() {
|
|
860
|
-
return new Proxy({}, {
|
|
861
|
-
get: (target, name) => {
|
|
862
|
-
if (name in this.parentModel.context.commands) {
|
|
863
|
-
return async (...args) => {
|
|
864
|
-
const { result } = await this.executeCommand(this.parentModel.context.commands[name], ...args);
|
|
865
|
-
return result;
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
console.warn(`Command '${name}' not found in the context.`);
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
async executeCommand(command, ...args) {
|
|
874
|
-
const proxyAdapter = await this.proxyAdapterPromise;
|
|
875
|
-
const transaction = proxyAdapter.readWriteTransaction(this.parentModel.context.collections);
|
|
876
|
-
const { context: context3, finalize } = await this.parentModel.context.commandContext(transaction);
|
|
877
|
-
const result = await command(context3, ...args);
|
|
878
|
-
const changes = await finalize();
|
|
879
|
-
const events = [];
|
|
880
|
-
for (const change of changes) {
|
|
881
|
-
await this.context.applyChange(transaction, change, events);
|
|
882
|
-
}
|
|
883
|
-
this.uncommitedChanges.push(...changes);
|
|
884
|
-
events.forEach((change) => this.changes$.next(change));
|
|
885
|
-
return { changes, result };
|
|
886
|
-
}
|
|
887
|
-
getCollection(name) {
|
|
888
|
-
return this.parentModel.getCollection(name);
|
|
889
|
-
}
|
|
890
|
-
async applyChanges(changes) {
|
|
891
|
-
const proxyAdapter = await this.proxyAdapterPromise;
|
|
892
|
-
const transaction = proxyAdapter.readWriteTransaction(this.parentModel.context.collections);
|
|
893
|
-
await this.parentModel.applyChangesForTransaction(transaction, changes);
|
|
894
|
-
}
|
|
895
|
-
get context() {
|
|
896
|
-
return this.parentModel.context;
|
|
897
|
-
}
|
|
898
|
-
async commitChanges() {
|
|
899
|
-
this.parentModel.applyChanges(this.uncommitedChanges);
|
|
900
|
-
this.parentModel.rtc.changesExecuted(this.uncommitedChanges);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
// rtc/deserializeChanges.ts
|
|
904
|
-
function deserializeChanges(changes, getCollection) {
|
|
905
|
-
return changes.map((change) => {
|
|
906
|
-
const c = getCollection(change.collection);
|
|
907
|
-
if (!c) {
|
|
908
|
-
console.error(`Collection ${change.collection} not found`);
|
|
909
|
-
return change;
|
|
910
|
-
}
|
|
911
|
-
switch (change.type) {
|
|
912
|
-
case "set":
|
|
913
|
-
return {
|
|
914
|
-
...change,
|
|
915
|
-
body: c.deserialize(change.body)
|
|
916
|
-
};
|
|
917
|
-
case "mutate":
|
|
918
|
-
return {
|
|
919
|
-
...change,
|
|
920
|
-
patches: change.patches.map((patch) => ({
|
|
921
|
-
...patch,
|
|
922
|
-
value: c.schema.deserializePath(patch.path, patch.value)
|
|
923
|
-
}))
|
|
924
|
-
};
|
|
925
|
-
default:
|
|
926
|
-
return change;
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
|
|
931
933
|
// rtc/client.ts
|
|
932
934
|
class RTCClient {
|
|
933
|
-
|
|
935
|
+
storage;
|
|
934
936
|
_socket;
|
|
935
937
|
openSocket;
|
|
936
938
|
reconnectAttempts = 0;
|
|
937
939
|
maxReconnectAttempts = 5;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
940
|
+
syncProgressCallback;
|
|
941
|
+
syncResolve;
|
|
942
|
+
syncPromise = null;
|
|
943
|
+
pendingStoreChanges = [];
|
|
944
|
+
constructor(storage) {
|
|
945
|
+
this.storage = storage;
|
|
946
|
+
}
|
|
947
|
+
async sync(progressCallback) {
|
|
948
|
+
if (this.syncPromise)
|
|
949
|
+
return this.syncPromise;
|
|
950
|
+
this.syncProgressCallback = progressCallback;
|
|
951
|
+
this.syncPromise = new Promise((resolve) => {
|
|
952
|
+
this.syncResolve = resolve;
|
|
953
|
+
this.connect();
|
|
954
|
+
});
|
|
955
|
+
return this.syncPromise;
|
|
941
956
|
}
|
|
942
|
-
connect() {
|
|
957
|
+
async connect() {
|
|
943
958
|
this._socket = new WebSocket(`wss://${window.location.host}/ws`);
|
|
944
959
|
this.openSocket = new Promise((resolve) => {
|
|
945
960
|
this._socket.addEventListener("open", () => {
|
|
@@ -947,6 +962,7 @@ class RTCClient {
|
|
|
947
962
|
resolve(this._socket);
|
|
948
963
|
});
|
|
949
964
|
});
|
|
965
|
+
const arcState = await this.storage.getStore("state").findById("$arc", (a) => a);
|
|
950
966
|
this._socket.addEventListener("message", (e) => {
|
|
951
967
|
this.onMessage(JSON.parse(e.data));
|
|
952
968
|
});
|
|
@@ -957,7 +973,7 @@ class RTCClient {
|
|
|
957
973
|
});
|
|
958
974
|
this.sendMessage({
|
|
959
975
|
type: "sync",
|
|
960
|
-
lastDate:
|
|
976
|
+
lastDate: arcState?.lastSyncDate || null
|
|
961
977
|
});
|
|
962
978
|
}
|
|
963
979
|
reconnect() {
|
|
@@ -971,7 +987,7 @@ class RTCClient {
|
|
|
971
987
|
console.error("Max reconnect attempts reached. Giving up.");
|
|
972
988
|
}
|
|
973
989
|
}
|
|
974
|
-
|
|
990
|
+
commitChanges(changes) {
|
|
975
991
|
return this.sendMessage({
|
|
976
992
|
type: "changes-executed",
|
|
977
993
|
changes
|
|
@@ -981,35 +997,43 @@ class RTCClient {
|
|
|
981
997
|
switch (message.type) {
|
|
982
998
|
case "sync-result":
|
|
983
999
|
{
|
|
984
|
-
const {
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1000
|
+
const { store, items } = message;
|
|
1001
|
+
this.syncProgressCallback?.({ store, size: items.length });
|
|
1002
|
+
const storeState = this.storage.getStore(store);
|
|
1003
|
+
if (!storeState) {
|
|
1004
|
+
console.error(`Store ${store} not found`);
|
|
988
1005
|
return;
|
|
989
1006
|
}
|
|
990
|
-
const
|
|
1007
|
+
const changes = items.map((item) => {
|
|
991
1008
|
if (item.deleted) {
|
|
992
1009
|
return {
|
|
993
|
-
collection: collection3,
|
|
994
1010
|
type: "delete",
|
|
995
1011
|
id: item._id
|
|
996
1012
|
};
|
|
997
1013
|
}
|
|
998
1014
|
return {
|
|
999
|
-
collection: collection3,
|
|
1000
1015
|
type: "set",
|
|
1001
|
-
|
|
1016
|
+
data: item
|
|
1002
1017
|
};
|
|
1003
1018
|
});
|
|
1004
|
-
this.
|
|
1019
|
+
this.pendingStoreChanges.push(storeState.applyChanges(changes));
|
|
1005
1020
|
}
|
|
1006
1021
|
break;
|
|
1007
1022
|
case "sync-done":
|
|
1008
|
-
|
|
1023
|
+
Promise.all(this.pendingStoreChanges).then(() => {
|
|
1024
|
+
const stateStorage = this.storage.getStore("state");
|
|
1025
|
+
stateStorage.applyChanges([
|
|
1026
|
+
{
|
|
1027
|
+
type: "set",
|
|
1028
|
+
data: { _id: "$arc", lastSyncDate: message.date }
|
|
1029
|
+
}
|
|
1030
|
+
]).then(() => {
|
|
1031
|
+
this.pendingStoreChanges = [];
|
|
1032
|
+
this.syncResolve?.();
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1009
1035
|
break;
|
|
1010
|
-
case "
|
|
1011
|
-
const deserializedChanges = deserializeChanges(message.changes, (name) => this.model.getCollection(name));
|
|
1012
|
-
this.model.applyChanges(deserializedChanges);
|
|
1036
|
+
case "state-changes":
|
|
1013
1037
|
break;
|
|
1014
1038
|
default:
|
|
1015
1039
|
console.warn(`Message unsupported`, message);
|
|
@@ -1020,16 +1044,42 @@ class RTCClient {
|
|
|
1020
1044
|
socket.send(JSON.stringify(message));
|
|
1021
1045
|
}
|
|
1022
1046
|
}
|
|
1023
|
-
var rtcClientFactory = (
|
|
1024
|
-
return new RTCClient(
|
|
1047
|
+
var rtcClientFactory = (storage) => {
|
|
1048
|
+
return new RTCClient(storage);
|
|
1025
1049
|
};
|
|
1050
|
+
// rtc/deserializeChanges.ts
|
|
1051
|
+
function deserializeChanges(changes, getCollection) {
|
|
1052
|
+
return changes.map((change) => {
|
|
1053
|
+
const c = getCollection(change.collection);
|
|
1054
|
+
if (!c) {
|
|
1055
|
+
console.error(`Collection ${change.collection} not found`);
|
|
1056
|
+
return change;
|
|
1057
|
+
}
|
|
1058
|
+
switch (change.type) {
|
|
1059
|
+
case "set":
|
|
1060
|
+
return {
|
|
1061
|
+
...change,
|
|
1062
|
+
body: c.deserialize(change.body)
|
|
1063
|
+
};
|
|
1064
|
+
case "mutate":
|
|
1065
|
+
return {
|
|
1066
|
+
...change,
|
|
1067
|
+
patches: change.patches.map((patch) => ({
|
|
1068
|
+
...patch,
|
|
1069
|
+
value: c.schema.deserializePath(patch.path, patch.value)
|
|
1070
|
+
}))
|
|
1071
|
+
};
|
|
1072
|
+
default:
|
|
1073
|
+
return change;
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1026
1077
|
export {
|
|
1027
1078
|
stringEnum,
|
|
1028
1079
|
string,
|
|
1029
1080
|
rtcClientFactory,
|
|
1030
1081
|
object,
|
|
1031
1082
|
number,
|
|
1032
|
-
model,
|
|
1033
1083
|
id,
|
|
1034
1084
|
deserializeChanges,
|
|
1035
1085
|
date,
|
|
@@ -1037,17 +1087,22 @@ export {
|
|
|
1037
1087
|
collection,
|
|
1038
1088
|
boolean,
|
|
1039
1089
|
array,
|
|
1040
|
-
|
|
1090
|
+
StoreState,
|
|
1091
|
+
MasterStoreState,
|
|
1092
|
+
MasterDataStorage,
|
|
1093
|
+
ForkedStoreState,
|
|
1094
|
+
ForkedDataStorage,
|
|
1041
1095
|
ArcStringEnum,
|
|
1042
1096
|
ArcString,
|
|
1097
|
+
ArcQueryBuilder,
|
|
1043
1098
|
ArcOptional,
|
|
1044
1099
|
ArcObject,
|
|
1045
1100
|
ArcNumber,
|
|
1046
|
-
ArcModel,
|
|
1047
1101
|
ArcIndexedCollection,
|
|
1048
1102
|
ArcId,
|
|
1049
1103
|
ArcDate,
|
|
1050
1104
|
ArcCollectionQuery,
|
|
1105
|
+
ArcCollection,
|
|
1051
1106
|
ArcBranded,
|
|
1052
1107
|
ArcBoolean,
|
|
1053
1108
|
ArcArray
|