@arcote.tech/arc 0.0.15 → 0.0.17
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 +27 -13
- package/dist/collection/index.d.ts +0 -1
- package/dist/collection/queries/abstract-collection-query.d.ts +7 -8
- package/dist/collection/queries/one-item.d.ts +1 -1
- package/dist/collection/queries/util.d.ts +2 -2
- package/dist/context/context.d.ts +21 -10
- package/dist/context/element.d.ts +7 -7
- package/dist/context/query.d.ts +6 -3
- package/dist/data-storage/ForkStoreState.d.ts +1 -1
- package/dist/data-storage/StoreState.d.ts +37 -22
- package/dist/data-storage/data-storage-forked.d.ts +2 -2
- package/dist/data-storage/data-storage-master.d.ts +5 -3
- package/dist/data-storage/data-storage.abstract.d.ts +47 -0
- package/dist/data-storage/data-storage.interface.d.ts +14 -5
- package/dist/data-storage/deep-merge.d.ts +6 -0
- package/dist/data-storage/index.d.ts +1 -1
- package/dist/data-storage/master-store-state.d.ts +26 -13
- package/dist/data-storage/store-state-fork.d.ts +10 -1
- package/dist/data-storage/store-state-master.d.ts +11 -1
- package/dist/data-storage/store-state.abstract.d.ts +15 -4
- package/dist/data-storage/store-state.d.ts +37 -22
- package/dist/elements/date.d.ts +1 -1
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/object copy.d.ts +29 -0
- package/dist/elements/object.d.ts +15 -0
- package/dist/elements/record.d.ts +19 -0
- package/dist/elements/state.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +491 -141
- package/dist/rtc/index.d.ts +1 -1
- package/dist/rtc/messages.d.ts +3 -3
- package/dist/rtc/rtc.d.ts +4 -3
- package/dist/state/db.d.ts +1 -0
- package/dist/state/index.d.ts +1 -2
- package/dist/state/query-builder.d.ts +3 -2
- package/dist/state/query.d.ts +10 -17
- package/dist/state/state-change.d.ts +1 -0
- package/dist/state/state.d.ts +20 -16
- package/dist/state/util.d.ts +1 -0
- package/dist/utils/deep-merge.d.ts +6 -0
- package/dist/utils.d.ts +10 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
//
|
|
2
|
-
class
|
|
3
|
-
|
|
1
|
+
// context/element.ts
|
|
2
|
+
class ArcContextElement {
|
|
3
|
+
$event;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// context/query.ts
|
|
7
|
+
class ArcQuery {
|
|
4
8
|
lastResult;
|
|
5
9
|
listener;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// collection/queries/abstract-collection-query.ts
|
|
13
|
+
class ArcCollectionQuery extends ArcQuery {
|
|
14
|
+
collection;
|
|
15
|
+
bindedChangeHandler = this.changeHandler.bind(this);
|
|
16
|
+
store;
|
|
6
17
|
constructor(collection) {
|
|
18
|
+
super();
|
|
7
19
|
this.collection = collection;
|
|
8
20
|
}
|
|
9
21
|
async run(dataStorage, listener) {
|
|
10
22
|
const store = dataStorage.getStore(this.collection.name);
|
|
23
|
+
this.store = store;
|
|
11
24
|
const result = await this.fetch(store);
|
|
12
25
|
this.lastResult = result;
|
|
13
26
|
if (listener)
|
|
@@ -15,16 +28,19 @@ class ArcCollectionQuery {
|
|
|
15
28
|
return result;
|
|
16
29
|
}
|
|
17
30
|
changeHandler(changes) {
|
|
31
|
+
let resultChanged = false;
|
|
18
32
|
for (const change of changes) {
|
|
19
33
|
const response = this.onChange(change);
|
|
20
|
-
if (response !== false)
|
|
34
|
+
if (response !== false) {
|
|
21
35
|
this.lastResult = response;
|
|
36
|
+
resultChanged = true;
|
|
37
|
+
}
|
|
22
38
|
}
|
|
23
|
-
if (
|
|
39
|
+
if (resultChanged)
|
|
24
40
|
this.nextResult(this.lastResult);
|
|
25
41
|
}
|
|
26
42
|
unsubscribe() {
|
|
27
|
-
|
|
43
|
+
this.store.unsubscribe(this.bindedChangeHandler);
|
|
28
44
|
}
|
|
29
45
|
nextResult(result) {
|
|
30
46
|
this.lastResult = result;
|
|
@@ -91,7 +107,7 @@ class ArcManyItemsQuery extends ArcCollectionQuery {
|
|
|
91
107
|
// collection/queries/all-items.ts
|
|
92
108
|
class ArcAllItemsQuery extends ArcManyItemsQuery {
|
|
93
109
|
async fetch(store) {
|
|
94
|
-
const results = await store.findAll(this.
|
|
110
|
+
const results = await store.findAll(this.bindedChangeHandler);
|
|
95
111
|
return this.createFiltredResult(results);
|
|
96
112
|
}
|
|
97
113
|
}
|
|
@@ -167,7 +183,7 @@ class ArcIndexedItemsQuery extends ArcManyItemsQuery {
|
|
|
167
183
|
return true;
|
|
168
184
|
}
|
|
169
185
|
async fetch(store) {
|
|
170
|
-
const results = await store.findByIndex(this.index, this.data, this.
|
|
186
|
+
const results = await store.findByIndex(this.index, this.data, this.bindedChangeHandler);
|
|
171
187
|
return this.createFiltredResult(results);
|
|
172
188
|
}
|
|
173
189
|
}
|
|
@@ -198,14 +214,16 @@ class ArcOneItemQuery extends ArcCollectionQuery {
|
|
|
198
214
|
this.id = id;
|
|
199
215
|
}
|
|
200
216
|
onChange(change) {
|
|
217
|
+
if (change.id !== this.id)
|
|
218
|
+
return false;
|
|
201
219
|
if (change.type === "delete")
|
|
202
|
-
return;
|
|
220
|
+
return null;
|
|
203
221
|
if (change.type === "set")
|
|
204
222
|
return change.item;
|
|
205
223
|
return false;
|
|
206
224
|
}
|
|
207
225
|
async fetch(store) {
|
|
208
|
-
const result = await store.findById(this.id, this.
|
|
226
|
+
const result = await store.findById(this.id, this.bindedChangeHandler);
|
|
209
227
|
return result;
|
|
210
228
|
}
|
|
211
229
|
}
|
|
@@ -229,12 +247,13 @@ function collection(name, id, schema, options = {}) {
|
|
|
229
247
|
return new ArcCollection(name, id, schema, options);
|
|
230
248
|
}
|
|
231
249
|
|
|
232
|
-
class ArcCollection {
|
|
250
|
+
class ArcCollection extends ArcContextElement {
|
|
233
251
|
name;
|
|
234
252
|
id;
|
|
235
253
|
schema;
|
|
236
254
|
options;
|
|
237
255
|
constructor(name, id, schema, options) {
|
|
256
|
+
super();
|
|
238
257
|
this.name = name;
|
|
239
258
|
this.id = id;
|
|
240
259
|
this.schema = schema;
|
|
@@ -258,7 +277,7 @@ class ArcCollection {
|
|
|
258
277
|
one: (id) => new ArcOneItemQueryBuilder(this, id)
|
|
259
278
|
};
|
|
260
279
|
}
|
|
261
|
-
commandContext(dataStorage) {
|
|
280
|
+
commandContext(dataStorage, publishEvent) {
|
|
262
281
|
const store = dataStorage.getStore(this.name);
|
|
263
282
|
return {
|
|
264
283
|
add: async (data) => {
|
|
@@ -269,6 +288,10 @@ class ArcCollection {
|
|
|
269
288
|
...parsed
|
|
270
289
|
};
|
|
271
290
|
await store.set(body);
|
|
291
|
+
await publishEvent({
|
|
292
|
+
type: "set",
|
|
293
|
+
to: body
|
|
294
|
+
});
|
|
272
295
|
return { id };
|
|
273
296
|
},
|
|
274
297
|
remove: async (id) => {
|
|
@@ -282,6 +305,10 @@ class ArcCollection {
|
|
|
282
305
|
...parsed
|
|
283
306
|
};
|
|
284
307
|
await store.set(body);
|
|
308
|
+
await publishEvent({
|
|
309
|
+
type: "set",
|
|
310
|
+
to: body
|
|
311
|
+
});
|
|
285
312
|
return { success: true };
|
|
286
313
|
},
|
|
287
314
|
all: async () => {
|
|
@@ -291,8 +318,22 @@ class ArcCollection {
|
|
|
291
318
|
return store.findById(id);
|
|
292
319
|
},
|
|
293
320
|
modify: async (id, data) => {
|
|
294
|
-
const deserialized = this.schema.
|
|
295
|
-
await store.modify(id, deserialized);
|
|
321
|
+
const deserialized = this.schema.serializePartial(data);
|
|
322
|
+
const { from, to } = await store.modify(id, deserialized);
|
|
323
|
+
await publishEvent({
|
|
324
|
+
type: "modify",
|
|
325
|
+
changes: deserialized,
|
|
326
|
+
from,
|
|
327
|
+
to
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
edit: async (id, editCallback) => {
|
|
331
|
+
const { from, to } = await store.mutate(id, editCallback);
|
|
332
|
+
await publishEvent({
|
|
333
|
+
type: "mutate",
|
|
334
|
+
from,
|
|
335
|
+
to
|
|
336
|
+
});
|
|
296
337
|
}
|
|
297
338
|
};
|
|
298
339
|
}
|
|
@@ -307,8 +348,8 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
307
348
|
super(name, id, schema, options);
|
|
308
349
|
this.indexes = indexes;
|
|
309
350
|
}
|
|
310
|
-
commandContext(dataStorage) {
|
|
311
|
-
const superContext = super.commandContext(dataStorage);
|
|
351
|
+
commandContext(dataStorage, publishEvent) {
|
|
352
|
+
const superContext = super.commandContext(dataStorage, publishEvent);
|
|
312
353
|
return new Proxy(superContext, {
|
|
313
354
|
get: (target, name) => {
|
|
314
355
|
if (name in target) {
|
|
@@ -336,19 +377,21 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
336
377
|
}
|
|
337
378
|
}
|
|
338
379
|
// context/context.ts
|
|
339
|
-
function context(elements, commands,
|
|
340
|
-
return new ArcContext(elements, commands,
|
|
380
|
+
function context(version, elements, commands, listeners) {
|
|
381
|
+
return new ArcContext(version, elements, commands, listeners);
|
|
341
382
|
}
|
|
342
383
|
|
|
343
384
|
class ArcContext {
|
|
385
|
+
version;
|
|
344
386
|
elements;
|
|
345
387
|
commands;
|
|
346
|
-
|
|
388
|
+
listeners;
|
|
347
389
|
elementsMap;
|
|
348
|
-
constructor(elements, commands,
|
|
390
|
+
constructor(version, elements, commands, listeners) {
|
|
391
|
+
this.version = version;
|
|
349
392
|
this.elements = elements;
|
|
350
393
|
this.commands = commands;
|
|
351
|
-
this.
|
|
394
|
+
this.listeners = listeners;
|
|
352
395
|
this.elementsMap = Object.fromEntries(elements.map((collection3) => [collection3.name, collection3]));
|
|
353
396
|
}
|
|
354
397
|
queryBuilder() {
|
|
@@ -362,21 +405,83 @@ class ArcContext {
|
|
|
362
405
|
}
|
|
363
406
|
});
|
|
364
407
|
}
|
|
365
|
-
commandContext(dataStorage) {
|
|
408
|
+
commandContext(client, dataStorage, publishEvent) {
|
|
366
409
|
return new Proxy({}, {
|
|
367
410
|
get: (target, name) => {
|
|
411
|
+
if (name === "$client") {
|
|
412
|
+
return client;
|
|
413
|
+
}
|
|
368
414
|
const element = this.elementsMap[name];
|
|
369
|
-
return element.commandContext(dataStorage)
|
|
415
|
+
return element.commandContext(dataStorage, (event) => publishEvent({
|
|
416
|
+
element: name,
|
|
417
|
+
...event
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
commandsClient(client, dataStorage, catchErrorCallback) {
|
|
423
|
+
return new Proxy({}, {
|
|
424
|
+
get: (_, name) => {
|
|
425
|
+
if (name in this.commands) {
|
|
426
|
+
return async (...args) => {
|
|
427
|
+
const forkedDataStorage = dataStorage.fork();
|
|
428
|
+
const commandContext = this.commandContext(client, forkedDataStorage, async (data) => {
|
|
429
|
+
if (!this.listeners)
|
|
430
|
+
return;
|
|
431
|
+
await Promise.all(this.listeners?.map((listener) => listener(data, commandContext)));
|
|
432
|
+
});
|
|
433
|
+
try {
|
|
434
|
+
const result = await this.commands[name](commandContext, ...args);
|
|
435
|
+
await forkedDataStorage.merge();
|
|
436
|
+
return result;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.log("error", error);
|
|
439
|
+
catchErrorCallback(error);
|
|
440
|
+
return error;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
console.warn(`Command '${name}' not found in the context.`);
|
|
445
|
+
return;
|
|
370
446
|
}
|
|
371
447
|
});
|
|
372
448
|
}
|
|
373
449
|
}
|
|
450
|
+
// data-storage/data-storage.abstract.ts
|
|
451
|
+
class DataStorage {
|
|
452
|
+
async commitChanges(changes) {
|
|
453
|
+
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// data-storage/store-state-fork.ts
|
|
458
|
+
import { apply } from "mutative";
|
|
459
|
+
|
|
460
|
+
// data-storage/deep-merge.ts
|
|
461
|
+
function deepMerge(target, source) {
|
|
462
|
+
const output = { ...target };
|
|
463
|
+
for (const key in source) {
|
|
464
|
+
if (source[key] === undefined)
|
|
465
|
+
continue;
|
|
466
|
+
if (isObject(source[key]) && isObject(target[key])) {
|
|
467
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
468
|
+
} else {
|
|
469
|
+
output[key] = source[key];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return output;
|
|
473
|
+
}
|
|
474
|
+
function isObject(item) {
|
|
475
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
476
|
+
}
|
|
477
|
+
|
|
374
478
|
// data-storage/store-state.abstract.ts
|
|
479
|
+
import { create } from "mutative";
|
|
375
480
|
class StoreState {
|
|
376
481
|
storeName;
|
|
377
482
|
dataStorage;
|
|
378
483
|
deserialize;
|
|
379
|
-
listeners = new
|
|
484
|
+
listeners = new Map;
|
|
380
485
|
constructor(storeName, dataStorage, deserialize) {
|
|
381
486
|
this.storeName = storeName;
|
|
382
487
|
this.dataStorage = dataStorage;
|
|
@@ -405,13 +510,27 @@ class StoreState {
|
|
|
405
510
|
id,
|
|
406
511
|
data
|
|
407
512
|
};
|
|
408
|
-
await this.
|
|
513
|
+
const { from, to } = await this.applyChange(change);
|
|
514
|
+
return { from, to };
|
|
515
|
+
}
|
|
516
|
+
async mutate(id, editCallback) {
|
|
517
|
+
const object = await this.findById(id);
|
|
518
|
+
const [draft, finalize] = create(object || {}, { enablePatches: true });
|
|
519
|
+
await editCallback(draft);
|
|
520
|
+
const [_, patches] = finalize();
|
|
521
|
+
const change = {
|
|
522
|
+
type: "mutate",
|
|
523
|
+
id,
|
|
524
|
+
patches
|
|
525
|
+
};
|
|
526
|
+
const { from, to } = await this.applyChange(change);
|
|
527
|
+
return { from, to };
|
|
409
528
|
}
|
|
410
529
|
unsubscribe(listener) {
|
|
411
530
|
this.listeners.delete(listener);
|
|
412
531
|
}
|
|
413
532
|
notifyListeners(events) {
|
|
414
|
-
for (const listener of this.listeners) {
|
|
533
|
+
for (const listener of this.listeners.values()) {
|
|
415
534
|
listener.callback(events);
|
|
416
535
|
}
|
|
417
536
|
}
|
|
@@ -426,42 +545,77 @@ class ForkedStoreState extends StoreState {
|
|
|
426
545
|
super(storeName, dataStorage, deserialize);
|
|
427
546
|
this.master = master;
|
|
428
547
|
}
|
|
548
|
+
async applyChangeAndReturnEvent(change) {
|
|
549
|
+
this.changes.push(change);
|
|
550
|
+
if (change.type === "set") {
|
|
551
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
552
|
+
this.changedItems.set(change.data._id, item);
|
|
553
|
+
return {
|
|
554
|
+
from: null,
|
|
555
|
+
to: item,
|
|
556
|
+
event: {
|
|
557
|
+
type: "set",
|
|
558
|
+
item: change.data,
|
|
559
|
+
id: change.data._id
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (change.type === "delete") {
|
|
564
|
+
const from = await this.findById(change.id);
|
|
565
|
+
this.changedItems.set(change.id, null);
|
|
566
|
+
return {
|
|
567
|
+
from: from || null,
|
|
568
|
+
to: null,
|
|
569
|
+
event: {
|
|
570
|
+
type: "delete",
|
|
571
|
+
item: null,
|
|
572
|
+
id: change.id
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
if (change.type === "modify") {
|
|
577
|
+
const existing = await this.findById(change.id);
|
|
578
|
+
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
579
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
580
|
+
this.changedItems.set(change.id, item);
|
|
581
|
+
return {
|
|
582
|
+
from: existing || null,
|
|
583
|
+
to: item,
|
|
584
|
+
event: {
|
|
585
|
+
type: "set",
|
|
586
|
+
item,
|
|
587
|
+
id: change.id
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
if (change.type === "mutate") {
|
|
592
|
+
const existing = await this.findById(change.id);
|
|
593
|
+
const updated = apply(existing || {}, change.patches);
|
|
594
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
595
|
+
this.changedItems.set(change.id, item);
|
|
596
|
+
return {
|
|
597
|
+
from: existing || null,
|
|
598
|
+
to: item,
|
|
599
|
+
event: {
|
|
600
|
+
type: "set",
|
|
601
|
+
item,
|
|
602
|
+
id: change.id
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
throw new Error("Unknown change type");
|
|
607
|
+
}
|
|
608
|
+
async applyChange(change) {
|
|
609
|
+
const { event, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
610
|
+
this.notifyListeners([event]);
|
|
611
|
+
return { from, to };
|
|
612
|
+
}
|
|
429
613
|
async applyChanges(changes) {
|
|
430
614
|
const events = [];
|
|
431
615
|
for (const change of changes) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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;
|
|
463
|
-
}
|
|
464
|
-
this.changes.push(change);
|
|
616
|
+
const { event } = await this.applyChangeAndReturnEvent(change);
|
|
617
|
+
if (event)
|
|
618
|
+
events.push(event);
|
|
465
619
|
}
|
|
466
620
|
if (events.length > 0) {
|
|
467
621
|
this.notifyListeners(events);
|
|
@@ -469,25 +623,34 @@ class ForkedStoreState extends StoreState {
|
|
|
469
623
|
}
|
|
470
624
|
async findById(id, listener) {
|
|
471
625
|
if (listener)
|
|
472
|
-
this.listeners.
|
|
626
|
+
this.listeners.set(listener, { callback: listener, id });
|
|
473
627
|
if (this.changedItems.has(id))
|
|
474
628
|
return this.changedItems.get(id);
|
|
475
629
|
return await this.master.findById(id);
|
|
476
630
|
}
|
|
477
631
|
async findByIndex(index, data, listener) {
|
|
478
632
|
if (listener)
|
|
479
|
-
this.listeners.
|
|
633
|
+
this.listeners.set(listener, { callback: listener });
|
|
480
634
|
const parentResult = await this.master.findByIndex(index, data);
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
635
|
+
const results = new Map;
|
|
636
|
+
parentResult.forEach((item) => results.set(item._id, item));
|
|
637
|
+
for (const [id, changedItem] of this.changedItems) {
|
|
638
|
+
if (changedItem === null) {
|
|
639
|
+
results.delete(id);
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const matches = Object.entries(data).every(([key, value]) => changedItem[key] === value);
|
|
643
|
+
if (matches) {
|
|
644
|
+
results.set(id, changedItem);
|
|
645
|
+
} else {
|
|
646
|
+
results.delete(id);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return Array.from(results.values());
|
|
487
650
|
}
|
|
488
651
|
async findAll(listener) {
|
|
489
652
|
if (listener)
|
|
490
|
-
this.listeners.
|
|
653
|
+
this.listeners.set(listener, { callback: listener });
|
|
491
654
|
const parentResult = await this.master.findAll();
|
|
492
655
|
return parentResult.map((item) => {
|
|
493
656
|
const id = item._id;
|
|
@@ -499,10 +662,11 @@ class ForkedStoreState extends StoreState {
|
|
|
499
662
|
}
|
|
500
663
|
|
|
501
664
|
// data-storage/data-storage-forked.ts
|
|
502
|
-
class ForkedDataStorage {
|
|
665
|
+
class ForkedDataStorage extends DataStorage {
|
|
503
666
|
master;
|
|
504
667
|
stores = new Map;
|
|
505
668
|
constructor(master) {
|
|
669
|
+
super();
|
|
506
670
|
this.master = master;
|
|
507
671
|
}
|
|
508
672
|
getReadTransaction() {
|
|
@@ -523,57 +687,96 @@ class ForkedDataStorage {
|
|
|
523
687
|
return new ForkedDataStorage(this);
|
|
524
688
|
}
|
|
525
689
|
async merge() {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
690
|
+
const changes = Array.from(this.stores.values()).filter((store) => store.changes.length > 0).map((store) => ({
|
|
691
|
+
store: store.storeName,
|
|
692
|
+
changes: store.changes
|
|
693
|
+
}));
|
|
694
|
+
await this.master.commitChanges(changes);
|
|
529
695
|
}
|
|
530
696
|
}
|
|
531
697
|
// data-storage/store-state-master.ts
|
|
698
|
+
import { apply as apply2 } from "mutative";
|
|
532
699
|
class MasterStoreState extends StoreState {
|
|
533
700
|
items = new Map;
|
|
534
701
|
isComplete = false;
|
|
535
702
|
constructor(storeName, dataStorage, deserialize) {
|
|
536
703
|
super(storeName, dataStorage, deserialize);
|
|
537
704
|
}
|
|
705
|
+
async applyChangeAndReturnEvent(transaction, change) {
|
|
706
|
+
if (change.type === "set") {
|
|
707
|
+
await transaction.set(this.storeName, change.data);
|
|
708
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
709
|
+
this.items.set(change.data._id, item);
|
|
710
|
+
return {
|
|
711
|
+
from: null,
|
|
712
|
+
to: item,
|
|
713
|
+
event: {
|
|
714
|
+
type: "set",
|
|
715
|
+
item: change.data,
|
|
716
|
+
id: change.data._id
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
if (change.type === "delete") {
|
|
721
|
+
await transaction.remove(this.storeName, change.id);
|
|
722
|
+
this.items.set(change.id, null);
|
|
723
|
+
return {
|
|
724
|
+
from: null,
|
|
725
|
+
to: null,
|
|
726
|
+
event: {
|
|
727
|
+
type: "delete",
|
|
728
|
+
item: null,
|
|
729
|
+
id: change.id
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
if (change.type === "modify") {
|
|
734
|
+
const existing = await transaction.findById(this.storeName, change.id);
|
|
735
|
+
const updated = existing ? deepMerge(existing, change.data) : { _id: change.id, ...change.data };
|
|
736
|
+
await transaction.set(this.storeName, updated);
|
|
737
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
738
|
+
this.items.set(change.id, item);
|
|
739
|
+
return {
|
|
740
|
+
from: null,
|
|
741
|
+
to: item,
|
|
742
|
+
event: {
|
|
743
|
+
type: "set",
|
|
744
|
+
item,
|
|
745
|
+
id: change.id
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (change.type === "mutate") {
|
|
750
|
+
const existing = await transaction.findById(this.storeName, change.id);
|
|
751
|
+
const updated = apply2(existing, change.patches);
|
|
752
|
+
await transaction.set(this.storeName, updated);
|
|
753
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
754
|
+
this.items.set(change.id, item);
|
|
755
|
+
return {
|
|
756
|
+
from: null,
|
|
757
|
+
to: item,
|
|
758
|
+
event: {
|
|
759
|
+
type: "set",
|
|
760
|
+
item,
|
|
761
|
+
id: change.id
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
throw new Error("Unknown change type");
|
|
766
|
+
}
|
|
767
|
+
async applyChange(change) {
|
|
768
|
+
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
769
|
+
const { event, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
770
|
+
this.notifyListeners([event]);
|
|
771
|
+
return { from, to };
|
|
772
|
+
}
|
|
538
773
|
async applyChanges(changes) {
|
|
539
774
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
540
775
|
const events = [];
|
|
541
776
|
for (const change of changes) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
}
|
|
777
|
+
const { event } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
778
|
+
if (event)
|
|
779
|
+
events.push(event);
|
|
577
780
|
}
|
|
578
781
|
await transaction.commit();
|
|
579
782
|
if (events.length > 0) {
|
|
@@ -582,20 +785,20 @@ class MasterStoreState extends StoreState {
|
|
|
582
785
|
}
|
|
583
786
|
async findById(id, listener) {
|
|
584
787
|
if (listener)
|
|
585
|
-
this.listeners.
|
|
788
|
+
this.listeners.set(listener, { callback: listener, id });
|
|
586
789
|
if (this.items.has(id))
|
|
587
790
|
return this.items.get(id);
|
|
588
791
|
const transaction = await this.dataStorage.getReadTransaction();
|
|
589
792
|
const result = await transaction.findById(this.storeName, id);
|
|
590
|
-
|
|
793
|
+
const item = result && this.deserialize ? this.deserialize(result) : result;
|
|
794
|
+
if (!item)
|
|
591
795
|
return;
|
|
592
|
-
const item = this.deserialize ? this.deserialize(result) : result;
|
|
593
796
|
this.items.set(id, item);
|
|
594
797
|
return item;
|
|
595
798
|
}
|
|
596
799
|
async findByIndex(index, data, listener) {
|
|
597
800
|
if (listener)
|
|
598
|
-
this.listeners.
|
|
801
|
+
this.listeners.set(listener, { callback: listener });
|
|
599
802
|
if (this.isComplete) {
|
|
600
803
|
const results2 = Array.from(this.items.values()).filter((item) => {
|
|
601
804
|
if (!item)
|
|
@@ -618,7 +821,7 @@ class MasterStoreState extends StoreState {
|
|
|
618
821
|
}
|
|
619
822
|
async findAll(listener) {
|
|
620
823
|
if (listener)
|
|
621
|
-
this.listeners.
|
|
824
|
+
this.listeners.set(listener, { callback: listener });
|
|
622
825
|
if (this.isComplete)
|
|
623
826
|
return Array.from(this.items.values()).filter((e) => !!e);
|
|
624
827
|
const transaction = await this.dataStorage.getReadTransaction();
|
|
@@ -637,12 +840,13 @@ class MasterStoreState extends StoreState {
|
|
|
637
840
|
}
|
|
638
841
|
|
|
639
842
|
// data-storage/data-storage-master.ts
|
|
640
|
-
class MasterDataStorage {
|
|
843
|
+
class MasterDataStorage extends DataStorage {
|
|
641
844
|
dbAdapter;
|
|
642
845
|
arcContext;
|
|
643
846
|
stores = new Map;
|
|
644
847
|
rtcAdapter;
|
|
645
848
|
constructor(dbAdapter, rtcAdapterFactory, arcContext) {
|
|
849
|
+
super();
|
|
646
850
|
this.dbAdapter = dbAdapter;
|
|
647
851
|
this.arcContext = arcContext;
|
|
648
852
|
this.rtcAdapter = rtcAdapterFactory(this);
|
|
@@ -662,6 +866,15 @@ class MasterDataStorage {
|
|
|
662
866
|
}
|
|
663
867
|
return this.stores.get(storeName);
|
|
664
868
|
}
|
|
869
|
+
async applyChanges(changes) {
|
|
870
|
+
return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
871
|
+
}
|
|
872
|
+
async commitChanges(changes) {
|
|
873
|
+
await Promise.all([
|
|
874
|
+
this.applyChanges(changes),
|
|
875
|
+
this.rtcAdapter.commitChanges(changes)
|
|
876
|
+
]);
|
|
877
|
+
}
|
|
665
878
|
fork() {
|
|
666
879
|
return new ForkedDataStorage(this);
|
|
667
880
|
}
|
|
@@ -784,7 +997,10 @@ class ArcObject extends ArcAbstract {
|
|
|
784
997
|
}, {});
|
|
785
998
|
}
|
|
786
999
|
deserialize(value) {
|
|
787
|
-
return Object.fromEntries(Object.entries(this.rawShape).
|
|
1000
|
+
return Object.fromEntries(Object.entries(this.rawShape).map(([key, element]) => [
|
|
1001
|
+
key,
|
|
1002
|
+
element.deserialize(value[key])
|
|
1003
|
+
]));
|
|
788
1004
|
}
|
|
789
1005
|
deserializePath(path, value) {
|
|
790
1006
|
if (path.length === 0) {
|
|
@@ -801,6 +1017,24 @@ class ArcObject extends ArcAbstract {
|
|
|
801
1017
|
}
|
|
802
1018
|
return element.deserialize(value);
|
|
803
1019
|
}
|
|
1020
|
+
parsePartial(value) {
|
|
1021
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1022
|
+
acc[key] = this.rawShape[key].parse(value2);
|
|
1023
|
+
return acc;
|
|
1024
|
+
}, {});
|
|
1025
|
+
}
|
|
1026
|
+
deserializePartial(value) {
|
|
1027
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1028
|
+
acc[key] = this.rawShape[key].deserialize(value2);
|
|
1029
|
+
return acc;
|
|
1030
|
+
}, {});
|
|
1031
|
+
}
|
|
1032
|
+
serializePartial(value) {
|
|
1033
|
+
return Object.entries(value).reduce((acc, [key, value2]) => {
|
|
1034
|
+
acc[key] = this.rawShape[key].serialize(value2);
|
|
1035
|
+
return acc;
|
|
1036
|
+
}, {});
|
|
1037
|
+
}
|
|
804
1038
|
}
|
|
805
1039
|
|
|
806
1040
|
// elements/array.ts
|
|
@@ -869,7 +1103,7 @@ class ArcDate extends ArcAbstract {
|
|
|
869
1103
|
return new Date(value);
|
|
870
1104
|
}
|
|
871
1105
|
serialize(value) {
|
|
872
|
-
return value.
|
|
1106
|
+
return value.getTime();
|
|
873
1107
|
}
|
|
874
1108
|
deserialize(value) {
|
|
875
1109
|
return new Date(value);
|
|
@@ -906,6 +1140,44 @@ function number() {
|
|
|
906
1140
|
|
|
907
1141
|
class ArcNumber extends ArcPrimitive {
|
|
908
1142
|
}
|
|
1143
|
+
// elements/record.ts
|
|
1144
|
+
function record(key, element) {
|
|
1145
|
+
return new ArcRecord(key, element);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
class ArcRecord extends ArcAbstract {
|
|
1149
|
+
key;
|
|
1150
|
+
element;
|
|
1151
|
+
constructor(key, element) {
|
|
1152
|
+
super();
|
|
1153
|
+
this.key = key;
|
|
1154
|
+
this.element = element;
|
|
1155
|
+
}
|
|
1156
|
+
parse(value) {
|
|
1157
|
+
if (!value)
|
|
1158
|
+
return {};
|
|
1159
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1160
|
+
acc[key] = this.element.parse(recordValue);
|
|
1161
|
+
return acc;
|
|
1162
|
+
}, {});
|
|
1163
|
+
}
|
|
1164
|
+
serialize(value) {
|
|
1165
|
+
if (!value)
|
|
1166
|
+
return {};
|
|
1167
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1168
|
+
acc[key] = this.element.serialize(recordValue);
|
|
1169
|
+
return acc;
|
|
1170
|
+
}, {});
|
|
1171
|
+
}
|
|
1172
|
+
deserialize(value) {
|
|
1173
|
+
if (!value)
|
|
1174
|
+
return {};
|
|
1175
|
+
return Object.entries(value).reduce((acc, [key, recordValue]) => {
|
|
1176
|
+
acc[key] = this.element.deserialize(recordValue);
|
|
1177
|
+
return acc;
|
|
1178
|
+
}, {});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
909
1181
|
// elements/string-enum.ts
|
|
910
1182
|
function stringEnum(...values) {
|
|
911
1183
|
return new ArcStringEnum(values);
|
|
@@ -1034,6 +1306,7 @@ class RTCClient {
|
|
|
1034
1306
|
});
|
|
1035
1307
|
break;
|
|
1036
1308
|
case "state-changes":
|
|
1309
|
+
this.storage.applyChanges(message.changes);
|
|
1037
1310
|
break;
|
|
1038
1311
|
default:
|
|
1039
1312
|
console.warn(`Message unsupported`, message);
|
|
@@ -1047,41 +1320,114 @@ class RTCClient {
|
|
|
1047
1320
|
var rtcClientFactory = (storage) => {
|
|
1048
1321
|
return new RTCClient(storage);
|
|
1049
1322
|
};
|
|
1050
|
-
//
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1323
|
+
// state/query.ts
|
|
1324
|
+
class ArcStateQuery extends ArcQuery {
|
|
1325
|
+
state;
|
|
1326
|
+
bindedChangeHandler = this.changeHandler.bind(this);
|
|
1327
|
+
store;
|
|
1328
|
+
constructor(state) {
|
|
1329
|
+
super();
|
|
1330
|
+
this.state = state;
|
|
1331
|
+
}
|
|
1332
|
+
async run(dataStorage, listener) {
|
|
1333
|
+
this.store = dataStorage.getStore("state");
|
|
1334
|
+
const result = await this.store.findById(this.state.name, this.bindedChangeHandler);
|
|
1335
|
+
this.lastResult = this.state.deserialize(result);
|
|
1336
|
+
if (listener)
|
|
1337
|
+
this.listener = listener;
|
|
1338
|
+
return this.lastResult;
|
|
1339
|
+
}
|
|
1340
|
+
onChange(change) {
|
|
1341
|
+
if (change.type === "set")
|
|
1342
|
+
return change.item;
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
changeHandler(changes) {
|
|
1346
|
+
for (const change of changes) {
|
|
1347
|
+
const response = this.onChange(change);
|
|
1348
|
+
if (response !== false)
|
|
1349
|
+
this.lastResult = this.state.deserialize(response);
|
|
1074
1350
|
}
|
|
1075
|
-
|
|
1351
|
+
if (this.lastResult)
|
|
1352
|
+
this.nextResult(this.lastResult);
|
|
1353
|
+
}
|
|
1354
|
+
unsubscribe() {
|
|
1355
|
+
this.store.unsubscribe(this.bindedChangeHandler);
|
|
1356
|
+
}
|
|
1357
|
+
nextResult(result) {
|
|
1358
|
+
this.lastResult = result;
|
|
1359
|
+
this.listener?.(result);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// state/query-builder.ts
|
|
1364
|
+
class ArcStateQueryBuilder extends ArcQueryBuilder {
|
|
1365
|
+
state;
|
|
1366
|
+
constructor(state) {
|
|
1367
|
+
super();
|
|
1368
|
+
this.state = state;
|
|
1369
|
+
}
|
|
1370
|
+
toQuery() {
|
|
1371
|
+
return new ArcStateQuery(this.state);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// state/state.ts
|
|
1376
|
+
function state(name, schema) {
|
|
1377
|
+
return new ArcState(name, schema);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
class ArcState extends ArcContextElement {
|
|
1381
|
+
name;
|
|
1382
|
+
schema;
|
|
1383
|
+
constructor(name, schema) {
|
|
1384
|
+
super();
|
|
1385
|
+
this.name = name;
|
|
1386
|
+
this.schema = schema;
|
|
1387
|
+
}
|
|
1388
|
+
serialize(data) {
|
|
1389
|
+
return {
|
|
1390
|
+
...this.schema.serialize(data),
|
|
1391
|
+
_id: this.name
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
deserialize(data) {
|
|
1395
|
+
return this.schema.deserialize(data || {});
|
|
1396
|
+
}
|
|
1397
|
+
queryBuilder() {
|
|
1398
|
+
return new ArcStateQueryBuilder(this);
|
|
1399
|
+
}
|
|
1400
|
+
commandContext(dataStorage, publishEvent) {
|
|
1401
|
+
const store = dataStorage.getStore("state");
|
|
1402
|
+
return {
|
|
1403
|
+
get: async () => {
|
|
1404
|
+
return store.findById(this.name);
|
|
1405
|
+
},
|
|
1406
|
+
modify: async (data) => {
|
|
1407
|
+
const serialized = this.serialize(data);
|
|
1408
|
+
const { from, to } = await store.modify(this.name, serialized);
|
|
1409
|
+
await publishEvent({
|
|
1410
|
+
type: "modify",
|
|
1411
|
+
changes: data,
|
|
1412
|
+
from,
|
|
1413
|
+
to
|
|
1414
|
+
});
|
|
1415
|
+
},
|
|
1416
|
+
edit: async (editCallback) => {
|
|
1417
|
+
const { from, to } = await store.mutate(this.name, editCallback);
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1076
1421
|
}
|
|
1077
1422
|
export {
|
|
1078
1423
|
stringEnum,
|
|
1079
1424
|
string,
|
|
1425
|
+
state,
|
|
1080
1426
|
rtcClientFactory,
|
|
1427
|
+
record,
|
|
1081
1428
|
object,
|
|
1082
1429
|
number,
|
|
1083
1430
|
id,
|
|
1084
|
-
deserializeChanges,
|
|
1085
1431
|
date,
|
|
1086
1432
|
context,
|
|
1087
1433
|
collection,
|
|
@@ -1092,9 +1438,13 @@ export {
|
|
|
1092
1438
|
MasterDataStorage,
|
|
1093
1439
|
ForkedStoreState,
|
|
1094
1440
|
ForkedDataStorage,
|
|
1441
|
+
DataStorage,
|
|
1095
1442
|
ArcStringEnum,
|
|
1096
1443
|
ArcString,
|
|
1444
|
+
ArcState,
|
|
1445
|
+
ArcRecord,
|
|
1097
1446
|
ArcQueryBuilder,
|
|
1447
|
+
ArcQuery,
|
|
1098
1448
|
ArcOptional,
|
|
1099
1449
|
ArcObject,
|
|
1100
1450
|
ArcNumber,
|