@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.
Files changed (42) hide show
  1. package/dist/collection/collection.d.ts +27 -13
  2. package/dist/collection/index.d.ts +0 -1
  3. package/dist/collection/queries/abstract-collection-query.d.ts +7 -8
  4. package/dist/collection/queries/one-item.d.ts +1 -1
  5. package/dist/collection/queries/util.d.ts +2 -2
  6. package/dist/context/context.d.ts +21 -10
  7. package/dist/context/element.d.ts +7 -7
  8. package/dist/context/query.d.ts +6 -3
  9. package/dist/data-storage/ForkStoreState.d.ts +1 -1
  10. package/dist/data-storage/StoreState.d.ts +37 -22
  11. package/dist/data-storage/data-storage-forked.d.ts +2 -2
  12. package/dist/data-storage/data-storage-master.d.ts +5 -3
  13. package/dist/data-storage/data-storage.abstract.d.ts +47 -0
  14. package/dist/data-storage/data-storage.interface.d.ts +14 -5
  15. package/dist/data-storage/deep-merge.d.ts +6 -0
  16. package/dist/data-storage/index.d.ts +1 -1
  17. package/dist/data-storage/master-store-state.d.ts +26 -13
  18. package/dist/data-storage/store-state-fork.d.ts +10 -1
  19. package/dist/data-storage/store-state-master.d.ts +11 -1
  20. package/dist/data-storage/store-state.abstract.d.ts +15 -4
  21. package/dist/data-storage/store-state.d.ts +37 -22
  22. package/dist/elements/date.d.ts +1 -1
  23. package/dist/elements/index.d.ts +1 -0
  24. package/dist/elements/object copy.d.ts +29 -0
  25. package/dist/elements/object.d.ts +15 -0
  26. package/dist/elements/record.d.ts +19 -0
  27. package/dist/elements/state.d.ts +2 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +491 -141
  30. package/dist/rtc/index.d.ts +1 -1
  31. package/dist/rtc/messages.d.ts +3 -3
  32. package/dist/rtc/rtc.d.ts +4 -3
  33. package/dist/state/db.d.ts +1 -0
  34. package/dist/state/index.d.ts +1 -2
  35. package/dist/state/query-builder.d.ts +3 -2
  36. package/dist/state/query.d.ts +10 -17
  37. package/dist/state/state-change.d.ts +1 -0
  38. package/dist/state/state.d.ts +20 -16
  39. package/dist/state/util.d.ts +1 -0
  40. package/dist/utils/deep-merge.d.ts +6 -0
  41. package/dist/utils.d.ts +10 -1
  42. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,13 +1,26 @@
1
- // collection/queries/abstract-collection-query.ts
2
- class ArcCollectionQuery {
3
- collection;
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 (this.lastResult)
39
+ if (resultChanged)
24
40
  this.nextResult(this.lastResult);
25
41
  }
26
42
  unsubscribe() {
27
- console.log("unsubscribe");
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.changeHandler.bind(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.changeHandler.bind(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.changeHandler.bind(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.deserialize(data);
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, version) {
340
- return new ArcContext(elements, commands, version);
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
- version;
388
+ listeners;
347
389
  elementsMap;
348
- constructor(elements, commands, version) {
390
+ constructor(version, elements, commands, listeners) {
391
+ this.version = version;
349
392
  this.elements = elements;
350
393
  this.commands = commands;
351
- this.version = version;
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 Set;
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.applyChanges([change]);
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
- 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;
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.add({ callback: listener, id });
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.add({ callback: listener });
633
+ this.listeners.set(listener, { callback: listener });
480
634
  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
- });
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.add({ callback: listener });
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
- for (const store of this.stores.values()) {
527
- await store.master?.applyChanges(store.changes);
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
- 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
- }
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.add({ callback: listener, id });
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
- if (!result)
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.add({ callback: listener });
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.add({ callback: listener });
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).filter(([key]) => (key in value)).map(([key, element]) => [key, element.deserialize(value[key])]));
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.toString();
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
- // 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;
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,