@arcote.tech/arc 0.0.12 → 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 (73) hide show
  1. package/dist/collection/collection-change.d.ts +1 -0
  2. package/dist/collection/collection.d.ts +40 -27
  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 +8 -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 +460 -404
  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/model/index.d.ts +0 -4
  71. package/dist/model/query-builder.d.ts +0 -3
  72. package/dist/model/rtc.d.ts +0 -6
  73. 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
 
@@ -114,6 +101,23 @@ class ArcQueryBuilder {
114
101
  }
115
102
 
116
103
  // collection/query-builders/abstract-many-items.ts
104
+ function sortComparatorBuilder(comparator) {
105
+ if (typeof comparator === "function") {
106
+ return comparator;
107
+ } else if (Array.isArray(comparator)) {
108
+ return (a, b) => {
109
+ for (const [key, order] of comparator) {
110
+ if (a[key] < b[key])
111
+ return order === "ASC" ? -1 : 1;
112
+ if (a[key] > b[key])
113
+ return order === "ASC" ? 1 : -1;
114
+ }
115
+ return 0;
116
+ };
117
+ }
118
+ throw new Error("Invalid comparator type");
119
+ }
120
+
117
121
  class ArcManyItemsQueryBuilder extends ArcQueryBuilder {
118
122
  filterFn;
119
123
  sortFn;
@@ -121,8 +125,8 @@ class ArcManyItemsQueryBuilder extends ArcQueryBuilder {
121
125
  this.filterFn = callback;
122
126
  return this;
123
127
  }
124
- sort(callback) {
125
- this.sortFn = callback;
128
+ sort(comparator) {
129
+ this.sortFn = sortComparatorBuilder(comparator);
126
130
  return this;
127
131
  }
128
132
  }
@@ -133,6 +137,8 @@ class ArcAllItemsQueryBuilder extends ArcManyItemsQueryBuilder {
133
137
  constructor(collection) {
134
138
  super();
135
139
  this.collection = collection;
140
+ if (collection.options.sort)
141
+ this.sortFn = collection.options.sort;
136
142
  }
137
143
  toQuery() {
138
144
  return new ArcAllItemsQuery(this.collection, this.filterFn, this.sortFn);
@@ -160,8 +166,9 @@ class ArcIndexedItemsQuery extends ArcManyItemsQuery {
160
166
  return false;
161
167
  return true;
162
168
  }
163
- async fetch(transaction) {
164
- 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);
165
172
  }
166
173
  }
167
174
 
@@ -175,6 +182,8 @@ class ArcIndexedItemsQueryBuilder extends ArcManyItemsQueryBuilder {
175
182
  this.collection = collection;
176
183
  this.index = index;
177
184
  this.data = data;
185
+ if (collection.options.sort)
186
+ this.sortFn = collection.options.sort;
178
187
  }
179
188
  toQuery() {
180
189
  return new ArcIndexedItemsQuery(this.collection, this.index, this.data, this.filterFn, this.sortFn);
@@ -189,16 +198,15 @@ class ArcOneItemQuery extends ArcCollectionQuery {
189
198
  this.id = id;
190
199
  }
191
200
  onChange(change) {
192
- if (change.type === "delete" && change.id === this.id)
201
+ if (change.type === "delete")
193
202
  return;
194
- if (change.type === "set" && change.body._id === this.id)
195
- return change.body;
203
+ if (change.type === "set")
204
+ return change.item;
196
205
  return false;
197
206
  }
198
- fetch(transaction) {
199
- if (!this.id)
200
- return;
201
- 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;
202
210
  }
203
211
  }
204
212
 
@@ -217,18 +225,20 @@ class ArcOneItemQueryBuilder extends ArcQueryBuilder {
217
225
  }
218
226
 
219
227
  // collection/collection.ts
220
- function collection(name, id, schema) {
221
- return new ArcCollection(name, id, schema);
228
+ function collection(name, id, schema, options = {}) {
229
+ return new ArcCollection(name, id, schema, options);
222
230
  }
223
231
 
224
232
  class ArcCollection {
225
233
  name;
226
234
  id;
227
235
  schema;
228
- constructor(name, id, schema) {
236
+ options;
237
+ constructor(name, id, schema, options) {
229
238
  this.name = name;
230
239
  this.id = id;
231
240
  this.schema = schema;
241
+ this.options = options;
232
242
  }
233
243
  serialize(data) {
234
244
  return {
@@ -248,72 +258,69 @@ class ArcCollection {
248
258
  one: (id) => new ArcOneItemQueryBuilder(this, id)
249
259
  };
250
260
  }
251
- commandContext(transaction, changes, getDraft) {
261
+ commandContext(dataStorage) {
262
+ const store = dataStorage.getStore(this.name);
252
263
  return {
253
- add: (data) => {
264
+ add: async (data) => {
254
265
  const id = this.id.generate();
255
266
  const parsed = this.schema.parse(data);
256
267
  const body = {
257
268
  _id: id,
258
269
  ...parsed
259
270
  };
260
- changes.push({ type: "set", collection: this.name, body });
271
+ await store.set(body);
261
272
  return { id };
262
273
  },
263
- remove: (id) => {
264
- changes.push({ type: "delete", collection: this.name, id });
274
+ remove: async (id) => {
275
+ await store.remove(id);
265
276
  return { success: true };
266
277
  },
267
- set: (id, data) => {
278
+ set: async (id, data) => {
268
279
  const parsed = this.schema.parse(data);
269
280
  const body = {
270
281
  _id: id,
271
282
  ...parsed
272
283
  };
273
- changes.push({ type: "set", collection: this.name, body });
284
+ await store.set(body);
274
285
  return { success: true };
275
286
  },
287
+ all: async () => {
288
+ return store.findAll();
289
+ },
276
290
  one: async (id) => {
277
- const object = await transaction.findById(this, id);
278
- 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);
279
296
  }
280
297
  };
281
298
  }
282
- async applyChange(transaction, change, events) {
283
- switch (change.type) {
284
- case "set":
285
- await transaction.set(this, change.body);
286
- events.push(change);
287
- break;
288
- case "delete":
289
- await transaction.remove(this, change.id);
290
- events.push(change);
291
- break;
292
- case "mutate":
293
- const existingObject = await transaction.findById(this, change.id);
294
- if (existingObject) {
295
- const updatedObject = apply(existingObject, change.patches);
296
- await transaction.set(this, updatedObject);
297
- events.push({
298
- type: "set",
299
- collection: change.collection,
300
- body: updatedObject
301
- });
302
- }
303
- break;
304
- }
305
- }
306
299
  indexBy(indexes) {
307
- return new ArcIndexedCollection(this.name, this.id, this.schema, indexes);
300
+ return new ArcIndexedCollection(this.name, this.id, this.schema, this.options, indexes);
308
301
  }
309
302
  }
310
303
 
311
304
  class ArcIndexedCollection extends ArcCollection {
312
305
  indexes;
313
- constructor(name, id, schema, indexes) {
314
- super(name, id, schema);
306
+ constructor(name, id, schema, options, indexes) {
307
+ super(name, id, schema, options);
315
308
  this.indexes = indexes;
316
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
+ }
317
324
  queryBuilder() {
318
325
  const superQueryBuilder = super.queryBuilder();
319
326
  return new Proxy({}, {
@@ -329,22 +336,25 @@ class ArcIndexedCollection extends ArcCollection {
329
336
  }
330
337
  }
331
338
  // context/context.ts
332
- import { create } from "mutative";
333
- function context(...collections) {
334
- return new ArcContext(collections);
339
+ function context(elements, commands, version) {
340
+ return new ArcContext(elements, commands, version);
335
341
  }
336
342
 
337
343
  class ArcContext {
338
- collections;
339
- collectionsMap;
340
- constructor(collections) {
341
- this.collections = collections;
342
- 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]));
343
353
  }
344
354
  queryBuilder() {
345
355
  return new Proxy({}, {
346
356
  get: (target, name) => {
347
- const collection3 = this.collectionsMap[name];
357
+ const collection3 = this.elementsMap[name];
348
358
  if (!collection3) {
349
359
  throw new Error(`Collection "${name}" not found`);
350
360
  }
@@ -352,57 +362,308 @@ class ArcContext {
352
362
  }
353
363
  });
354
364
  }
355
- commandContext(transaction) {
356
- const changes = [];
357
- const collectionDrafts = [];
358
- function getDraft(collection3, base) {
359
- const [draft, finalize2] = create(base, { enablePatches: true });
360
- collectionDrafts.push({
361
- collection: collection3,
362
- finalize: finalize2
363
- });
364
- return draft;
365
- }
366
- const contextProxy = new Proxy({}, {
365
+ commandContext(dataStorage) {
366
+ return new Proxy({}, {
367
367
  get: (target, name) => {
368
- const collection3 = this.collectionsMap[name];
369
- if (!collection3) {
370
- throw new Error(`Collection "${name}" not found`);
371
- }
372
- return collection3.commandContext(transaction, changes, getDraft);
368
+ const element = this.elementsMap[name];
369
+ return element.commandContext(dataStorage);
373
370
  }
374
371
  });
375
- function finalize() {
376
- for (const { finalize: finalize2, collection: collection3 } of collectionDrafts) {
377
- const [object, patches, inversePatches] = finalize2();
378
- changes.push({
379
- type: "mutate",
380
- collection: collection3,
381
- id: object._id,
382
- patches
383
- });
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;
384
463
  }
385
- return changes;
464
+ this.changes.push(change);
465
+ }
466
+ if (events.length > 0) {
467
+ this.notifyListeners(events);
386
468
  }
387
- return { context: contextProxy, finalize };
388
469
  }
389
- async applyChange(transaction, change, events) {
390
- const collection3 = this.collectionsMap[change.collection];
391
- if (!collection3) {
392
- 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);
393
528
  }
394
- return collection3.applyChange(transaction, change, events);
395
529
  }
396
- withCommands(commands) {
397
- 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;
398
636
  }
399
637
  }
400
638
 
401
- class ArcContextWithCommands extends ArcContext {
402
- commands;
403
- constructor(context2, commands) {
404
- super(context2);
405
- 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);
406
667
  }
407
668
  }
408
669
  // elements/optional.ts
@@ -666,257 +927,18 @@ class ArcStringEnum extends ArcAbstract {
666
927
  return this.values;
667
928
  }
668
929
  }
669
- // model/model.ts
670
- import { Subject as Subject2 } from "rxjs";
671
- function model(context3, dependencies) {
672
- return new ArcModel(context3, dependencies.dbAdapterFactory, dependencies.rtcAdapterFactory);
673
- }
674
-
675
- class ArcModel {
676
- context3;
677
- rtc;
678
- dbAdapterPromise;
679
- changes$ = new Subject2;
680
- constructor(context3, dbAdapterFactory, rtcAdapterFactory) {
681
- this.context = context3;
682
- this.rtc = rtcAdapterFactory(this);
683
- this.dbAdapterPromise = dbAdapterFactory(this.context);
684
- }
685
- query(queryFactory) {
686
- const queryBuilder = this.context.queryBuilder();
687
- const query = queryFactory(queryBuilder).toQuery();
688
- this.runQuery(query);
689
- return query;
690
- }
691
- async runQuery(query) {
692
- const dbAdapter = await this.dbAdapterPromise;
693
- query.run(dbAdapter, this.changes$);
694
- }
695
- commands() {
696
- return new Proxy({}, {
697
- get: (target, name) => {
698
- if (name in this.context.commands) {
699
- return async (...args) => {
700
- const { result } = await this.executeCommand(this.context.commands[name], ...args);
701
- return result;
702
- };
703
- }
704
- console.warn(`Command '${name}' not found in the context.`);
705
- return;
706
- }
707
- });
708
- }
709
- async executeCommand(command, ...args) {
710
- const dbAdapter = await this.dbAdapterPromise;
711
- const transaction = await dbAdapter.readWriteTransaction(this.context.collections);
712
- const { context: context3, finalize } = await this.context.commandContext(transaction);
713
- const result = await command(context3, ...args);
714
- const changes = await finalize();
715
- await this.applyChangesForTransaction(transaction, changes);
716
- this.rtc.changesExecuted(changes);
717
- return { changes, result };
718
- }
719
- async applyChangesForTransaction(transaction, changes) {
720
- const events = [];
721
- for (const change of changes) {
722
- await this.context.applyChange(transaction, change, events);
723
- }
724
- await transaction.commit();
725
- events.forEach((change) => this.notifyChange(change));
726
- }
727
- notifyChange(change) {
728
- this.changes$.next(change);
729
- }
730
- async applyChanges(changes) {
731
- const dbAdapter = await this.dbAdapterPromise;
732
- const transaction = await dbAdapter.readWriteTransaction(this.context.collections);
733
- await this.applyChangesForTransaction(transaction, changes);
734
- }
735
- getCollection(name) {
736
- return this.context.collectionsMap[name];
737
- }
738
- }
739
- // model/submodel.ts
740
- import { Subject as Subject3 } from "rxjs";
741
-
742
- // collection/in-memory-db-proxy.ts
743
- class InMemoryTransaction {
744
- parentTransaction;
745
- local;
746
- constructor(parentTransaction, local) {
747
- this.parentTransaction = parentTransaction;
748
- this.local = local;
749
- }
750
- async set(collection3, data) {
751
- let collectionLocal = this.local.get(collection3.name);
752
- if (!collectionLocal) {
753
- collectionLocal = new Map;
754
- this.local.set(collection3.name, collectionLocal);
755
- }
756
- collectionLocal.set(data._id, data);
757
- }
758
- async remove(collection3, id3) {
759
- let collectionLocal = this.local.get(collection3.name);
760
- if (!collectionLocal) {
761
- collectionLocal = new Map;
762
- this.local.set(collection3.name, collectionLocal);
763
- }
764
- collectionLocal.set(id3, null);
765
- }
766
- async findById(collection3, id3) {
767
- const localItem = this.local.get(collection3.name)?.get(id3);
768
- if (localItem !== undefined) {
769
- return localItem === null ? undefined : localItem;
770
- }
771
- return this.parentTransaction.findById(collection3, id3);
772
- }
773
- async findByIndex(collection3, index, data) {
774
- const parentResults = await this.parentTransaction.findByIndex(collection3, index, data);
775
- const localResults = Array.from(this.local.get(collection3.name)?.values() || []).filter((item) => item !== null && this.matchesIndex(item, index, data));
776
- return this.mergeResults(parentResults, localResults);
777
- }
778
- async findAll(collection3) {
779
- const parentResults = await this.parentTransaction.findAll(collection3);
780
- const localResults = Array.from(this.local.get(collection3.name)?.values() || []).filter((item) => item !== null);
781
- return this.mergeResults(parentResults, localResults);
782
- }
783
- async commit() {
784
- }
785
- matchesIndex(item, index, data) {
786
- return Object.entries(data).every(([key, value]) => item[key] === value);
787
- }
788
- mergeResults(parentResults, localResults) {
789
- const mergedMap = new Map;
790
- parentResults.forEach((item) => mergedMap.set(item._id, item));
791
- localResults.forEach((item) => mergedMap.set(item._id, item));
792
- return Array.from(mergedMap.values());
793
- }
794
- }
795
-
796
- class InMemoryDatabaseProxyAdapter {
797
- parentAdapter;
798
- local = new Map;
799
- constructor(parentAdapter) {
800
- this.parentAdapter = parentAdapter;
801
- }
802
- readWriteTransaction(collections) {
803
- const parentTransaction = this.parentAdapter.readWriteTransaction(collections);
804
- return new InMemoryTransaction(parentTransaction, this.local);
805
- }
806
- readTransaction(collections) {
807
- const parentTransaction = this.parentAdapter.readTransaction(collections);
808
- return new InMemoryTransaction(parentTransaction, this.local);
809
- }
810
- clearLocal() {
811
- this.local.clear();
812
- }
813
- }
814
-
815
- // model/submodel.ts
816
- class ArcSubModel {
817
- parentModel;
818
- changes$ = new Subject3;
819
- proxyAdapterPromise;
820
- uncommitedChanges = [];
821
- constructor(parentModel) {
822
- this.parentModel = parentModel;
823
- this.parentModel.changes$.subscribe(this.changes$);
824
- this.proxyAdapterPromise = this.parentModel.dbAdapterPromise.then((dbAdapter) => new InMemoryDatabaseProxyAdapter(dbAdapter));
825
- }
826
- query(queryFactory) {
827
- const queryBuilder = this.context.queryBuilder();
828
- const query = queryFactory(queryBuilder).toQuery();
829
- this.runQuery(query);
830
- return query;
831
- }
832
- async runQuery(query) {
833
- const proxyAdapter = await this.proxyAdapterPromise;
834
- query.run(proxyAdapter, this.changes$);
835
- }
836
- commands() {
837
- return new Proxy({}, {
838
- get: (target, name) => {
839
- if (name in this.parentModel.context.commands) {
840
- return async (...args) => {
841
- const { result } = await this.executeCommand(this.parentModel.context.commands[name], ...args);
842
- return result;
843
- };
844
- }
845
- console.warn(`Command '${name}' not found in the context.`);
846
- return;
847
- }
848
- });
849
- }
850
- async executeCommand(command, ...args) {
851
- const proxyAdapter = await this.proxyAdapterPromise;
852
- const transaction = proxyAdapter.readWriteTransaction(this.parentModel.context.collections);
853
- const { context: context3, finalize } = await this.parentModel.context.commandContext(transaction);
854
- const result = await command(context3, ...args);
855
- const changes = await finalize();
856
- const events = [];
857
- for (const change of changes) {
858
- await this.context.applyChange(transaction, change, events);
859
- }
860
- this.uncommitedChanges.push(...changes);
861
- events.forEach((change) => this.changes$.next(change));
862
- return { changes, result };
863
- }
864
- getCollection(name) {
865
- return this.parentModel.getCollection(name);
866
- }
867
- async applyChanges(changes) {
868
- const proxyAdapter = await this.proxyAdapterPromise;
869
- const transaction = proxyAdapter.readWriteTransaction(this.parentModel.context.collections);
870
- await this.parentModel.applyChangesForTransaction(transaction, changes);
871
- }
872
- get context() {
873
- return this.parentModel.context;
874
- }
875
- async commitChanges() {
876
- this.parentModel.applyChanges(this.uncommitedChanges);
877
- this.parentModel.rtc.changesExecuted(this.uncommitedChanges);
878
- }
879
- }
880
- // rtc/deserializeChanges.ts
881
- function deserializeChanges(changes, getCollection) {
882
- return changes.map((change) => {
883
- const c = getCollection(change.collection);
884
- if (!c) {
885
- console.error(`Collection ${change.collection} not found`);
886
- return change;
887
- }
888
- switch (change.type) {
889
- case "set":
890
- return {
891
- ...change,
892
- body: c.deserialize(change.body)
893
- };
894
- case "mutate":
895
- return {
896
- ...change,
897
- patches: change.patches.map((patch) => ({
898
- ...patch,
899
- value: c.schema.deserializePath(patch.path, patch.value)
900
- }))
901
- };
902
- default:
903
- return change;
904
- }
905
- });
906
- }
907
-
908
930
  // rtc/client.ts
909
931
  class RTCClient {
910
- model3;
932
+ storage;
911
933
  _socket;
912
934
  openSocket;
913
935
  reconnectAttempts = 0;
914
936
  maxReconnectAttempts = 5;
915
- constructor(model3) {
916
- this.model = model3;
937
+ constructor(storage) {
938
+ this.storage = storage;
917
939
  this.connect();
918
940
  }
919
- connect() {
941
+ async connect() {
920
942
  this._socket = new WebSocket(`wss://${window.location.host}/ws`);
921
943
  this.openSocket = new Promise((resolve) => {
922
944
  this._socket.addEventListener("open", () => {
@@ -924,6 +946,7 @@ class RTCClient {
924
946
  resolve(this._socket);
925
947
  });
926
948
  });
949
+ const arcState = await this.storage.getStore("state").findById("$arc", (a) => a);
927
950
  this._socket.addEventListener("message", (e) => {
928
951
  this.onMessage(JSON.parse(e.data));
929
952
  });
@@ -934,7 +957,7 @@ class RTCClient {
934
957
  });
935
958
  this.sendMessage({
936
959
  type: "sync",
937
- lastDate: window.localStorage.getItem("lastSync")
960
+ lastDate: arcState?.lastSyncDate || null
938
961
  });
939
962
  }
940
963
  reconnect() {
@@ -948,7 +971,7 @@ class RTCClient {
948
971
  console.error("Max reconnect attempts reached. Giving up.");
949
972
  }
950
973
  }
951
- changesExecuted(changes) {
974
+ commitChanges(changes) {
952
975
  return this.sendMessage({
953
976
  type: "changes-executed",
954
977
  changes
@@ -958,35 +981,37 @@ class RTCClient {
958
981
  switch (message.type) {
959
982
  case "sync-result":
960
983
  {
961
- const { collection: collection3, items } = message;
962
- const c = this.model.getCollection(collection3);
963
- if (!c) {
964
- 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`);
965
988
  return;
966
989
  }
967
- const deserializedItems = items.map((item) => {
990
+ const changes = items.map((item) => {
968
991
  if (item.deleted) {
969
992
  return {
970
- collection: collection3,
971
993
  type: "delete",
972
994
  id: item._id
973
995
  };
974
996
  }
975
997
  return {
976
- collection: collection3,
977
998
  type: "set",
978
- body: item
999
+ data: item
979
1000
  };
980
1001
  });
981
- this.model.applyChanges(deserializedItems);
1002
+ storeState.applyChanges(changes);
982
1003
  }
983
1004
  break;
984
1005
  case "sync-done":
985
- 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
+ ]);
986
1013
  break;
987
- case "collection-changes":
988
- const deserializedChanges = deserializeChanges(message.changes, (name) => this.model.getCollection(name));
989
- this.model.applyChanges(deserializedChanges);
1014
+ case "state-changes":
990
1015
  break;
991
1016
  default:
992
1017
  console.warn(`Message unsupported`, message);
@@ -997,16 +1022,42 @@ class RTCClient {
997
1022
  socket.send(JSON.stringify(message));
998
1023
  }
999
1024
  }
1000
- var rtcClientFactory = (model3) => {
1001
- return new RTCClient(model3);
1025
+ var rtcClientFactory = (storage) => {
1026
+ return new RTCClient(storage);
1002
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
+ }
1003
1055
  export {
1004
1056
  stringEnum,
1005
1057
  string,
1006
1058
  rtcClientFactory,
1007
1059
  object,
1008
1060
  number,
1009
- model,
1010
1061
  id,
1011
1062
  deserializeChanges,
1012
1063
  date,
@@ -1014,17 +1065,22 @@ export {
1014
1065
  collection,
1015
1066
  boolean,
1016
1067
  array,
1017
- ArcSubModel,
1068
+ StoreState,
1069
+ MasterStoreState,
1070
+ MasterDataStorage,
1071
+ ForkedStoreState,
1072
+ ForkedDataStorage,
1018
1073
  ArcStringEnum,
1019
1074
  ArcString,
1075
+ ArcQueryBuilder,
1020
1076
  ArcOptional,
1021
1077
  ArcObject,
1022
1078
  ArcNumber,
1023
- ArcModel,
1024
1079
  ArcIndexedCollection,
1025
1080
  ArcId,
1026
1081
  ArcDate,
1027
1082
  ArcCollectionQuery,
1083
+ ArcCollection,
1028
1084
  ArcBranded,
1029
1085
  ArcBoolean,
1030
1086
  ArcArray