@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.
- package/dist/collection/collection-change.d.ts +1 -0
- package/dist/collection/collection.d.ts +40 -27
- package/dist/collection/db.d.ts +11 -12
- package/dist/collection/index.d.ts +1 -1
- package/dist/collection/queries/abstract-collection-query.d.ts +11 -14
- package/dist/collection/queries/abstract-many-items.d.ts +22 -13
- package/dist/collection/queries/all-items.d.ts +4 -3
- package/dist/collection/queries/indexed.d.ts +8 -8
- package/dist/collection/queries/one-item.d.ts +14 -6
- package/dist/collection/queries/util.d.ts +3 -6
- package/dist/collection/query-builders/abstract-many-items.d.ts +8 -6
- package/dist/collection/query-builders/all-items.d.ts +1 -0
- package/dist/collection/query-builders/indexed.d.ts +1 -0
- package/dist/collection/query-builders/one-item.d.ts +1 -0
- package/dist/context/commands.d.ts +3 -1
- package/dist/context/context.d.ts +13 -23
- package/dist/context/element.d.ts +7 -5
- package/dist/context/index.d.ts +4 -0
- package/dist/context/query-builders.d.ts +5 -2
- package/dist/context/query.d.ts +6 -2
- package/dist/data-storage/DataStorage.d.ts +11 -0
- package/dist/data-storage/ForkStoreState.d.ts +32 -0
- package/dist/data-storage/StoreState.d.ts +24 -0
- package/dist/data-storage/data-storage-forked.d.ts +16 -0
- package/dist/data-storage/data-storage-master.d.ts +20 -0
- package/dist/data-storage/data-storage.d.ts +2 -0
- package/dist/data-storage/data-storage.interface.d.ts +37 -0
- package/dist/data-storage/index.d.ts +7 -0
- package/dist/data-storage/master-store-state.d.ts +17 -0
- package/dist/data-storage/store-state-fork.d.ts +15 -0
- package/dist/data-storage/store-state-master.d.ts +14 -0
- package/dist/data-storage/store-state.abstract.d.ts +22 -0
- package/dist/data-storage/store-state.d.ts +24 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/interface.d.ts +17 -0
- package/dist/elements/abstract-primitive.d.ts +1 -0
- package/dist/elements/abstract.d.ts +1 -0
- package/dist/elements/array.d.ts +1 -0
- package/dist/elements/boolean.d.ts +1 -0
- package/dist/elements/branded.d.ts +1 -0
- package/dist/elements/date.d.ts +1 -0
- package/dist/elements/default.d.ts +1 -0
- package/dist/elements/element.d.ts +1 -0
- package/dist/elements/id.d.ts +1 -0
- package/dist/elements/index.d.ts +1 -0
- package/dist/elements/number.d.ts +1 -0
- package/dist/elements/object.d.ts +1 -0
- package/dist/elements/optional.d.ts +1 -0
- package/dist/elements/string-enum.d.ts +1 -0
- package/dist/elements/string.d.ts +1 -0
- package/dist/elements/tests/object.test.d.ts +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +460 -404
- package/dist/model/model.d.ts +18 -19
- package/dist/rtc/client.d.ts +2 -1
- package/dist/rtc/deserializeChanges.d.ts +1 -0
- package/dist/rtc/index.d.ts +1 -0
- package/dist/rtc/messages.d.ts +6 -5
- package/dist/rtc/rtc.d.ts +6 -0
- package/dist/state/db.d.ts +14 -0
- package/dist/state/index.d.ts +3 -0
- package/dist/state/query-builder.d.ts +8 -0
- package/dist/state/query.d.ts +22 -0
- package/dist/state/state-change.d.ts +13 -0
- package/dist/state/state.d.ts +28 -0
- package/dist/state/util.d.ts +2 -0
- package/dist/utils.d.ts +17 -14
- package/package.json +2 -2
- package/dist/collection/in-memory-db-proxy.d.ts +0 -10
- package/dist/model/index.d.ts +0 -4
- package/dist/model/query-builder.d.ts +0 -3
- package/dist/model/rtc.d.ts +0 -6
- 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
|
-
|
|
10
|
-
|
|
11
|
-
subscriptions = [];
|
|
4
|
+
lastResult;
|
|
5
|
+
listener;
|
|
12
6
|
constructor(collection) {
|
|
13
7
|
this.collection = collection;
|
|
14
8
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
this.nextResult(
|
|
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
|
-
|
|
27
|
+
console.log("unsubscribe");
|
|
38
28
|
}
|
|
39
29
|
nextResult(result) {
|
|
40
|
-
|
|
41
|
-
|
|
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.
|
|
76
|
-
const lastResultAsArray = lastResult
|
|
77
|
-
const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.
|
|
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.
|
|
65
|
+
const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
|
|
80
66
|
if (isInLastResult && shouldBeInTheResult)
|
|
81
|
-
return this.createResult(lastResultAsArray.toSpliced(index, 1, change.
|
|
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.
|
|
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(
|
|
108
|
-
|
|
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(
|
|
125
|
-
this.sortFn =
|
|
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(
|
|
164
|
-
|
|
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"
|
|
201
|
+
if (change.type === "delete")
|
|
193
202
|
return;
|
|
194
|
-
if (change.type === "set"
|
|
195
|
-
return change.
|
|
203
|
+
if (change.type === "set")
|
|
204
|
+
return change.item;
|
|
196
205
|
return false;
|
|
197
206
|
}
|
|
198
|
-
fetch(
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
271
|
+
await store.set(body);
|
|
261
272
|
return { id };
|
|
262
273
|
},
|
|
263
|
-
remove: (id) => {
|
|
264
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
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
|
-
|
|
333
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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.
|
|
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(
|
|
356
|
-
|
|
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
|
|
369
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
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
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
397
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
932
|
+
storage;
|
|
911
933
|
_socket;
|
|
912
934
|
openSocket;
|
|
913
935
|
reconnectAttempts = 0;
|
|
914
936
|
maxReconnectAttempts = 5;
|
|
915
|
-
constructor(
|
|
916
|
-
this.
|
|
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:
|
|
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
|
-
|
|
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 {
|
|
962
|
-
const
|
|
963
|
-
if (!
|
|
964
|
-
console.error(`
|
|
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
|
|
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
|
-
|
|
999
|
+
data: item
|
|
979
1000
|
};
|
|
980
1001
|
});
|
|
981
|
-
|
|
1002
|
+
storeState.applyChanges(changes);
|
|
982
1003
|
}
|
|
983
1004
|
break;
|
|
984
1005
|
case "sync-done":
|
|
985
|
-
|
|
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 "
|
|
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 = (
|
|
1001
|
-
return new RTCClient(
|
|
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
|
-
|
|
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
|