@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.
Files changed (76) hide show
  1. package/dist/collection/collection-change.d.ts +1 -0
  2. package/dist/collection/collection.d.ts +35 -26
  3. package/dist/collection/db.d.ts +11 -12
  4. package/dist/collection/index.d.ts +1 -1
  5. package/dist/collection/queries/abstract-collection-query.d.ts +11 -14
  6. package/dist/collection/queries/abstract-many-items.d.ts +22 -13
  7. package/dist/collection/queries/all-items.d.ts +4 -3
  8. package/dist/collection/queries/indexed.d.ts +8 -8
  9. package/dist/collection/queries/one-item.d.ts +14 -6
  10. package/dist/collection/queries/util.d.ts +3 -6
  11. package/dist/collection/query-builders/abstract-many-items.d.ts +6 -6
  12. package/dist/collection/query-builders/all-items.d.ts +1 -0
  13. package/dist/collection/query-builders/indexed.d.ts +1 -0
  14. package/dist/collection/query-builders/one-item.d.ts +1 -0
  15. package/dist/context/commands.d.ts +3 -1
  16. package/dist/context/context.d.ts +13 -23
  17. package/dist/context/element.d.ts +7 -5
  18. package/dist/context/index.d.ts +4 -0
  19. package/dist/context/query-builders.d.ts +5 -2
  20. package/dist/context/query.d.ts +6 -2
  21. package/dist/data-storage/DataStorage.d.ts +11 -0
  22. package/dist/data-storage/ForkStoreState.d.ts +32 -0
  23. package/dist/data-storage/StoreState.d.ts +24 -0
  24. package/dist/data-storage/data-storage-forked.d.ts +16 -0
  25. package/dist/data-storage/data-storage-master.d.ts +24 -0
  26. package/dist/data-storage/data-storage.d.ts +2 -0
  27. package/dist/data-storage/data-storage.interface.d.ts +37 -0
  28. package/dist/data-storage/index.d.ts +7 -0
  29. package/dist/data-storage/master-store-state.d.ts +17 -0
  30. package/dist/data-storage/store-state-fork.d.ts +15 -0
  31. package/dist/data-storage/store-state-master.d.ts +14 -0
  32. package/dist/data-storage/store-state.abstract.d.ts +22 -0
  33. package/dist/data-storage/store-state.d.ts +24 -0
  34. package/dist/db/index.d.ts +2 -0
  35. package/dist/db/interface.d.ts +17 -0
  36. package/dist/elements/abstract-primitive.d.ts +1 -0
  37. package/dist/elements/abstract.d.ts +1 -0
  38. package/dist/elements/array.d.ts +1 -0
  39. package/dist/elements/boolean.d.ts +1 -0
  40. package/dist/elements/branded.d.ts +1 -0
  41. package/dist/elements/date.d.ts +1 -0
  42. package/dist/elements/default.d.ts +1 -0
  43. package/dist/elements/element.d.ts +1 -0
  44. package/dist/elements/id.d.ts +1 -0
  45. package/dist/elements/index.d.ts +1 -0
  46. package/dist/elements/number.d.ts +1 -0
  47. package/dist/elements/object.d.ts +1 -0
  48. package/dist/elements/optional.d.ts +1 -0
  49. package/dist/elements/string-enum.d.ts +1 -0
  50. package/dist/elements/string.d.ts +1 -0
  51. package/dist/elements/tests/object.test.d.ts +2 -0
  52. package/dist/index.d.ts +3 -1
  53. package/dist/index.js +452 -397
  54. package/dist/model/model.d.ts +18 -19
  55. package/dist/rtc/client.d.ts +2 -1
  56. package/dist/rtc/deserializeChanges.d.ts +1 -0
  57. package/dist/rtc/index.d.ts +1 -0
  58. package/dist/rtc/messages.d.ts +6 -5
  59. package/dist/rtc/rtc.d.ts +10 -0
  60. package/dist/state/db.d.ts +14 -0
  61. package/dist/state/index.d.ts +3 -0
  62. package/dist/state/query-builder.d.ts +8 -0
  63. package/dist/state/query.d.ts +22 -0
  64. package/dist/state/state-change.d.ts +13 -0
  65. package/dist/state/state.d.ts +28 -0
  66. package/dist/state/util.d.ts +2 -0
  67. package/dist/utils.d.ts +17 -14
  68. package/package.json +2 -2
  69. package/dist/collection/in-memory-db-proxy.d.ts +0 -10
  70. package/dist/elements/instanceOf.d.ts +0 -19
  71. package/dist/elements/or.d.ts +0 -11
  72. package/dist/elements/recurent.d.ts +0 -20
  73. package/dist/model/index.d.ts +0 -4
  74. package/dist/model/query-builder.d.ts +0 -3
  75. package/dist/model/rtc.d.ts +0 -6
  76. 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
- result$ = new Subject;
10
- resultQueue$;
11
- subscriptions = [];
4
+ lastResult;
5
+ listener;
12
6
  constructor(collection) {
13
7
  this.collection = collection;
14
8
  }
15
- getLastResult() {
16
- return this.resultQueue$.getValue();
17
- }
18
- async run(db, changes$) {
19
- const subscription = changes$.subscribe(this.changeHandler.bind(this));
20
- this.subscriptions.push(subscription);
21
- const transaction = await db.readTransaction([this.collection]);
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(change) {
28
- if (change.collection !== this.collection.name)
29
- return;
30
- if (!this.resultQueue$)
31
- return;
32
- const response = this.onChange(change);
33
- if (response !== false)
34
- this.nextResult(response);
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
- this.subscriptions.forEach((sub) => sub.unsubscribe());
27
+ console.log("unsubscribe");
38
28
  }
39
29
  nextResult(result) {
40
- if (!this.resultQueue$)
41
- return console.warn("Weird, it shouldn't happen :/");
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.getLastResult();
76
- const lastResultAsArray = lastResult.toArray();
77
- const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.body._id));
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.body);
65
+ const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
80
66
  if (isInLastResult && shouldBeInTheResult)
81
- return this.createResult(lastResultAsArray.toSpliced(index, 1, change.body));
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.body));
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(transaction) {
108
- return this.createFiltredResult(await transaction.findAll(this.collection));
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(transaction) {
183
- return this.createFiltredResult(await transaction.findByIndex(this.collection, this.index, this.data));
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" && change.id === this.id)
201
+ if (change.type === "delete")
214
202
  return;
215
- if (change.type === "set" && change.body._id === this.id)
216
- return change.body;
203
+ if (change.type === "set")
204
+ return change.item;
217
205
  return false;
218
206
  }
219
- fetch(transaction) {
220
- if (!this.id)
221
- return;
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(transaction, changes, getDraft) {
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
- changes.push({ type: "set", collection: this.name, body });
271
+ await store.set(body);
284
272
  return { id };
285
273
  },
286
- remove: (id) => {
287
- changes.push({ type: "delete", collection: this.name, id });
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
- changes.push({ type: "set", collection: this.name, body });
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
- const object = await transaction.findById(this, id);
301
- return getDraft(this.name, object);
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
- import { create } from "mutative";
356
- function context(...collections) {
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
- collections;
362
- collectionsMap;
363
- constructor(collections) {
364
- this.collections = collections;
365
- this.collectionsMap = Object.fromEntries(collections.map((collection3) => [collection3.name, collection3]));
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.collectionsMap[name];
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(transaction) {
379
- const changes = [];
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 collection3 = this.collectionsMap[name];
392
- if (!collection3) {
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
- function finalize() {
399
- for (const { finalize: finalize2, collection: collection3 } of collectionDrafts) {
400
- const [object, patches, inversePatches] = finalize2();
401
- changes.push({
402
- type: "mutate",
403
- collection: collection3,
404
- id: object._id,
405
- patches
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
- return changes;
464
+ this.changes.push(change);
409
465
  }
410
- return { context: contextProxy, finalize };
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 applyChange(transaction, change, events) {
413
- const collection3 = this.collectionsMap[change.collection];
414
- if (!collection3) {
415
- throw new Error(`Collection "${change.collection}" not found`);
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
- withCommands(commands) {
420
- return new ArcContextWithCommands(this.collections, commands);
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
- class ArcContextWithCommands extends ArcContext {
425
- commands;
426
- constructor(context2, commands) {
427
- super(context2);
428
- this.commands = commands;
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
- model3;
935
+ storage;
934
936
  _socket;
935
937
  openSocket;
936
938
  reconnectAttempts = 0;
937
939
  maxReconnectAttempts = 5;
938
- constructor(model3) {
939
- this.model = model3;
940
- this.connect();
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: window.localStorage.getItem("lastSync")
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
- changesExecuted(changes) {
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 { collection: collection3, items } = message;
985
- const c = this.model.getCollection(collection3);
986
- if (!c) {
987
- console.error(`Collection ${collection3} not found`);
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 deserializedItems = items.map((item) => {
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
- body: item
1016
+ data: item
1002
1017
  };
1003
1018
  });
1004
- this.model.applyChanges(deserializedItems);
1019
+ this.pendingStoreChanges.push(storeState.applyChanges(changes));
1005
1020
  }
1006
1021
  break;
1007
1022
  case "sync-done":
1008
- window.localStorage.setItem("lastSync", message.date);
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 "collection-changes":
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 = (model3) => {
1024
- return new RTCClient(model3);
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
- ArcSubModel,
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