@arcote.tech/arc 0.0.13 → 0.0.14

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 +20 -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 +429 -396
  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 +6 -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,308 @@ 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);
465
+ }
466
+ if (events.length > 0) {
467
+ this.notifyListeners(events);
409
468
  }
410
- return { context: contextProxy, finalize };
411
469
  }
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`);
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
+ });
487
+ }
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);
528
+ }
529
+ }
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);
416
581
  }
417
- return collection3.applyChange(transaction, change, events);
418
582
  }
419
- withCommands(commands) {
420
- return new ArcContextWithCommands(this.collections, commands);
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);
429
667
  }
430
668
  }
431
669
  // elements/optional.ts
@@ -689,257 +927,18 @@ class ArcStringEnum extends ArcAbstract {
689
927
  return this.values;
690
928
  }
691
929
  }
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
930
  // rtc/client.ts
932
931
  class RTCClient {
933
- model3;
932
+ storage;
934
933
  _socket;
935
934
  openSocket;
936
935
  reconnectAttempts = 0;
937
936
  maxReconnectAttempts = 5;
938
- constructor(model3) {
939
- this.model = model3;
937
+ constructor(storage) {
938
+ this.storage = storage;
940
939
  this.connect();
941
940
  }
942
- connect() {
941
+ async connect() {
943
942
  this._socket = new WebSocket(`wss://${window.location.host}/ws`);
944
943
  this.openSocket = new Promise((resolve) => {
945
944
  this._socket.addEventListener("open", () => {
@@ -947,6 +946,7 @@ class RTCClient {
947
946
  resolve(this._socket);
948
947
  });
949
948
  });
949
+ const arcState = await this.storage.getStore("state").findById("$arc", (a) => a);
950
950
  this._socket.addEventListener("message", (e) => {
951
951
  this.onMessage(JSON.parse(e.data));
952
952
  });
@@ -957,7 +957,7 @@ class RTCClient {
957
957
  });
958
958
  this.sendMessage({
959
959
  type: "sync",
960
- lastDate: window.localStorage.getItem("lastSync")
960
+ lastDate: arcState?.lastSyncDate || null
961
961
  });
962
962
  }
963
963
  reconnect() {
@@ -971,7 +971,7 @@ class RTCClient {
971
971
  console.error("Max reconnect attempts reached. Giving up.");
972
972
  }
973
973
  }
974
- changesExecuted(changes) {
974
+ commitChanges(changes) {
975
975
  return this.sendMessage({
976
976
  type: "changes-executed",
977
977
  changes
@@ -981,35 +981,37 @@ class RTCClient {
981
981
  switch (message.type) {
982
982
  case "sync-result":
983
983
  {
984
- const { collection: collection3, items } = message;
985
- const c = this.model.getCollection(collection3);
986
- if (!c) {
987
- console.error(`Collection ${collection3} not found`);
984
+ const { store, items } = message;
985
+ const storeState = this.storage.getStore(store);
986
+ if (!storeState) {
987
+ console.error(`Store ${store} not found`);
988
988
  return;
989
989
  }
990
- const deserializedItems = items.map((item) => {
990
+ const changes = items.map((item) => {
991
991
  if (item.deleted) {
992
992
  return {
993
- collection: collection3,
994
993
  type: "delete",
995
994
  id: item._id
996
995
  };
997
996
  }
998
997
  return {
999
- collection: collection3,
1000
998
  type: "set",
1001
- body: item
999
+ data: item
1002
1000
  };
1003
1001
  });
1004
- this.model.applyChanges(deserializedItems);
1002
+ storeState.applyChanges(changes);
1005
1003
  }
1006
1004
  break;
1007
1005
  case "sync-done":
1008
- window.localStorage.setItem("lastSync", message.date);
1006
+ const stateStorage = this.storage.getStore("state");
1007
+ stateStorage.applyChanges([
1008
+ {
1009
+ type: "set",
1010
+ data: { _id: "$arc", lastSyncDate: message.date }
1011
+ }
1012
+ ]);
1009
1013
  break;
1010
- case "collection-changes":
1011
- const deserializedChanges = deserializeChanges(message.changes, (name) => this.model.getCollection(name));
1012
- this.model.applyChanges(deserializedChanges);
1014
+ case "state-changes":
1013
1015
  break;
1014
1016
  default:
1015
1017
  console.warn(`Message unsupported`, message);
@@ -1020,16 +1022,42 @@ class RTCClient {
1020
1022
  socket.send(JSON.stringify(message));
1021
1023
  }
1022
1024
  }
1023
- var rtcClientFactory = (model3) => {
1024
- return new RTCClient(model3);
1025
+ var rtcClientFactory = (storage) => {
1026
+ return new RTCClient(storage);
1025
1027
  };
1028
+ // rtc/deserializeChanges.ts
1029
+ function deserializeChanges(changes, getCollection) {
1030
+ return changes.map((change) => {
1031
+ const c = getCollection(change.collection);
1032
+ if (!c) {
1033
+ console.error(`Collection ${change.collection} not found`);
1034
+ return change;
1035
+ }
1036
+ switch (change.type) {
1037
+ case "set":
1038
+ return {
1039
+ ...change,
1040
+ body: c.deserialize(change.body)
1041
+ };
1042
+ case "mutate":
1043
+ return {
1044
+ ...change,
1045
+ patches: change.patches.map((patch) => ({
1046
+ ...patch,
1047
+ value: c.schema.deserializePath(patch.path, patch.value)
1048
+ }))
1049
+ };
1050
+ default:
1051
+ return change;
1052
+ }
1053
+ });
1054
+ }
1026
1055
  export {
1027
1056
  stringEnum,
1028
1057
  string,
1029
1058
  rtcClientFactory,
1030
1059
  object,
1031
1060
  number,
1032
- model,
1033
1061
  id,
1034
1062
  deserializeChanges,
1035
1063
  date,
@@ -1037,17 +1065,22 @@ export {
1037
1065
  collection,
1038
1066
  boolean,
1039
1067
  array,
1040
- ArcSubModel,
1068
+ StoreState,
1069
+ MasterStoreState,
1070
+ MasterDataStorage,
1071
+ ForkedStoreState,
1072
+ ForkedDataStorage,
1041
1073
  ArcStringEnum,
1042
1074
  ArcString,
1075
+ ArcQueryBuilder,
1043
1076
  ArcOptional,
1044
1077
  ArcObject,
1045
1078
  ArcNumber,
1046
- ArcModel,
1047
1079
  ArcIndexedCollection,
1048
1080
  ArcId,
1049
1081
  ArcDate,
1050
1082
  ArcCollectionQuery,
1083
+ ArcCollection,
1051
1084
  ArcBranded,
1052
1085
  ArcBoolean,
1053
1086
  ArcArray