@arcote.tech/arc 0.0.14 → 0.0.16
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.d.ts +15 -5
- package/dist/collection/index.d.ts +0 -1
- package/dist/context/context.d.ts +19 -10
- package/dist/context/element.d.ts +7 -7
- package/dist/data-storage/data-storage-master.d.ts +4 -0
- package/dist/data-storage/store-state-fork.d.ts +10 -1
- package/dist/data-storage/store-state-master.d.ts +11 -1
- package/dist/data-storage/store-state.abstract.d.ts +8 -1
- package/dist/index.js +191 -118
- package/dist/rtc/index.d.ts +0 -1
- package/dist/rtc/rtc.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ArcContextElement } from "../context/element";
|
|
2
2
|
import type { ArcIdAny } from "../elements/id";
|
|
3
3
|
import { type ArcObjectAny } from "../elements/object";
|
|
4
4
|
import { objectUtil, type util } from "../utils";
|
|
5
|
-
import type { CollectionChange } from "./collection-change";
|
|
6
5
|
import type { DataStorage } from "../data-storage";
|
|
7
6
|
import type { ArcDefault } from "../elements/default";
|
|
8
7
|
import { ArcAllItemsQueryBuilder } from "./query-builders/all-items";
|
|
@@ -28,7 +27,18 @@ type CollectionCommandContext<Id extends ArcIdAny, Schema extends ArcObjectAny |
|
|
|
28
27
|
set: (id: util.GetType<Id>, data: util.FirstArgument<Schema["serialize"]>) => Promise<any>;
|
|
29
28
|
modify: (id: util.GetType<Id>, data: objectUtil.addQuestionMarks<Partial<util.FirstArgument<Schema["serialize"]>>>) => Promise<any>;
|
|
30
29
|
};
|
|
31
|
-
export declare class ArcCollection<Name extends string, Id extends ArcIdAny, Schema extends ArcObjectAny | ArcDefault<ArcObjectAny>>
|
|
30
|
+
export declare class ArcCollection<Name extends string, Id extends ArcIdAny, Schema extends ArcObjectAny | ArcDefault<ArcObjectAny>> extends ArcContextElement<{
|
|
31
|
+
type: "delete";
|
|
32
|
+
item: CollectionItem<ArcCollection<Name, Id, Schema>>;
|
|
33
|
+
} | {
|
|
34
|
+
type: "set";
|
|
35
|
+
item: CollectionItem<ArcCollection<Name, Id, Schema>>;
|
|
36
|
+
} | {
|
|
37
|
+
type: "modify";
|
|
38
|
+
from: CollectionItem<ArcCollection<Name, Id, Schema>>;
|
|
39
|
+
to: CollectionItem<ArcCollection<Name, Id, Schema>>;
|
|
40
|
+
changes: objectUtil.simplify<Partial<util.FirstArgument<Schema["serialize"]>>>;
|
|
41
|
+
}> {
|
|
32
42
|
readonly name: Name;
|
|
33
43
|
readonly id: Id;
|
|
34
44
|
readonly schema: Schema;
|
|
@@ -43,7 +53,7 @@ export declare class ArcCollection<Name extends string, Id extends ArcIdAny, Sch
|
|
|
43
53
|
_id: util.FirstArgument<Id["deserialize"]>;
|
|
44
54
|
} & objectUtil.addQuestionMarks<util.FirstArgument<Schema["deserialize"]>>>): Deserialize<Id, Schema>;
|
|
45
55
|
queryBuilder(): CollectionQueryBuilder<ArcCollection<Name, Id, Schema>>;
|
|
46
|
-
commandContext(dataStorage: DataStorage): CollectionCommandContext<Id, Schema>;
|
|
56
|
+
commandContext(dataStorage: DataStorage, publishEvent: (event: this["$event"]) => Promise<void>): CollectionCommandContext<Id, Schema>;
|
|
47
57
|
indexBy<Indexes extends keyof ReturnType<Schema["deserialize"]>, const I extends {
|
|
48
58
|
[name: string]: Indexes[];
|
|
49
59
|
}>(indexes: I): ArcIndexedCollection<Name, Id, Schema, keyof ReturnType<Schema["deserialize"]>, I>;
|
|
@@ -53,7 +63,7 @@ export declare class ArcIndexedCollection<Name extends string, Id extends ArcIdA
|
|
|
53
63
|
}> extends ArcCollection<Name, Id, Schema> {
|
|
54
64
|
readonly indexes: I;
|
|
55
65
|
constructor(name: Name, id: Id, schema: Schema, options: ArcCollectionOptions<Id, Schema>, indexes: I);
|
|
56
|
-
commandContext(dataStorage: DataStorage): CollectionCommandContext<Id, Schema> & {
|
|
66
|
+
commandContext(dataStorage: DataStorage, publishEvent: (event: this["$event"]) => Promise<void>): CollectionCommandContext<Id, Schema> & {
|
|
57
67
|
[func in keyof I]: (args: {
|
|
58
68
|
[Key in I[func][number]]: util.GetType<Schema>[Key];
|
|
59
69
|
}) => Deserialize<Id, Schema>[];
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import type { DataStorage } from "../data-storage";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
readonly commands: Commands<ArcContext<C>>;
|
|
2
|
+
import type { objectUtil } from "../utils";
|
|
3
|
+
import type { CommandContext, Commands, CommandsClient } from "./commands";
|
|
4
|
+
import type { ArcContextElement, ArcContextElementAny } from "./element";
|
|
5
|
+
declare class ArcContext<const C extends ArcContextElementAny[], const Cmds extends Commands<ArcContext<C, any, any>>, const L extends Listener<ArcContext<C, any, any>>[]> {
|
|
7
6
|
readonly version: number;
|
|
7
|
+
readonly elements: C;
|
|
8
|
+
readonly commands: Cmds;
|
|
9
|
+
readonly listeners?: L | undefined;
|
|
8
10
|
elementsMap: {
|
|
9
11
|
[key: string]: C[number];
|
|
10
12
|
};
|
|
11
|
-
constructor(elements: C, commands:
|
|
13
|
+
constructor(version: number, elements: C, commands: Cmds, listeners?: L | undefined);
|
|
12
14
|
queryBuilder(): {
|
|
13
15
|
[Collection in C[number] as Collection["name"]]: ReturnType<Collection["queryBuilder"]>;
|
|
14
16
|
};
|
|
15
|
-
commandContext(dataStorage: DataStorage): { [ContextElement in C[number] as ContextElement["name"]]: ReturnType<ContextElement["commandContext"]>; };
|
|
17
|
+
commandContext(dataStorage: DataStorage, publishEvent: (event: any) => Promise<void>): { [ContextElement in C[number] as ContextElement["name"]]: ReturnType<ContextElement["commandContext"]>; };
|
|
18
|
+
commandsClient(dataStorage: DataStorage): CommandsClient<Cmds>;
|
|
16
19
|
}
|
|
17
|
-
export type ArcContextAny = ArcContext<ArcContextElementAny[]>;
|
|
18
|
-
export type
|
|
19
|
-
export
|
|
20
|
+
export type ArcContextAny = ArcContext<ArcContextElementAny[], Commands<any>, any>;
|
|
21
|
+
export type ElementEvents<Element extends ArcContextElementAny> = Element extends ArcContextElement<any> ? Element["$event"] : never;
|
|
22
|
+
export type ContextEvents<Context extends ArcContextAny> = {
|
|
23
|
+
[Element in Context["elements"][number] as Element["name"]]: objectUtil.simplify<{
|
|
24
|
+
element: Element["name"];
|
|
25
|
+
} & ElementEvents<Element>>;
|
|
26
|
+
}[Context["elements"][number]["name"]];
|
|
27
|
+
export type Listener<Context extends ArcContextAny> = (event: ContextEvents<Context>, context: CommandContext<Context>) => Promise<void> | void;
|
|
28
|
+
export declare function context<const Elements extends ArcContextElementAny[], Cmds extends Commands<ArcContext<Elements, any, any>>, L extends Listener<ArcContext<Elements, Cmds, any>>[]>(version: number, elements: Elements, commands: Cmds, listeners?: L): ArcContext<Elements, Cmds, L>;
|
|
20
29
|
export {};
|
|
21
30
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { DataStorage } from "../data-storage";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export declare abstract class ArcContextElement<const Event> {
|
|
3
|
+
readonly $event: Event;
|
|
4
|
+
abstract name: string;
|
|
5
|
+
abstract queryBuilder(): any;
|
|
6
|
+
abstract commandContext(dataStorage: DataStorage, publishEvent: (event: Event) => Promise<void>): any;
|
|
7
|
+
abstract deserialize(data: any): any;
|
|
7
8
|
}
|
|
8
|
-
export type ArcContextElementAny = ArcContextElement<any
|
|
9
|
-
export type ArcContextElementChanges<Element extends ArcContextElementAny> = Element extends ArcContextElement<infer Change, any> ? Change : never;
|
|
9
|
+
export type ArcContextElementAny = ArcContextElement<any>;
|
|
10
10
|
//# sourceMappingURL=element.d.ts.map
|
|
@@ -16,5 +16,9 @@ export declare class MasterDataStorage implements DataStorage {
|
|
|
16
16
|
_id: string;
|
|
17
17
|
}>(storeName: string): StoreState<Item>;
|
|
18
18
|
fork(): ForkedDataStorage;
|
|
19
|
+
sync(progressCallback: ({ store, size }: {
|
|
20
|
+
store: string;
|
|
21
|
+
size: number;
|
|
22
|
+
}) => void): Promise<void>;
|
|
19
23
|
}
|
|
20
24
|
//# sourceMappingURL=data-storage-master.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataStorage, QueryListenerCallback, StoreStateChange } from "./data-storage.interface";
|
|
1
|
+
import type { DataStorage, ListenerEvent, QueryListenerCallback, StoreStateChange } from "./data-storage.interface";
|
|
2
2
|
import { StoreState } from "./store-state.abstract";
|
|
3
3
|
export declare class ForkedStoreState<Item extends {
|
|
4
4
|
_id: string;
|
|
@@ -7,6 +7,15 @@ export declare class ForkedStoreState<Item extends {
|
|
|
7
7
|
protected changedItems: Map<string, Item | null>;
|
|
8
8
|
changes: StoreStateChange<Item>[];
|
|
9
9
|
constructor(storeName: string, dataStorage: DataStorage, master: StoreState<Item>, deserialize?: (data: any) => Item);
|
|
10
|
+
applyChangeAndReturnEvent(change: StoreStateChange<Item>): Promise<{
|
|
11
|
+
from: Item | null;
|
|
12
|
+
to: Item | null;
|
|
13
|
+
event: ListenerEvent<Item>;
|
|
14
|
+
}>;
|
|
15
|
+
applyChange(change: StoreStateChange<Item>): Promise<{
|
|
16
|
+
from: Item | null;
|
|
17
|
+
to: Item | null;
|
|
18
|
+
}>;
|
|
10
19
|
applyChanges(changes: StoreStateChange<Item>[]): Promise<void>;
|
|
11
20
|
findById(id: string, listener?: QueryListenerCallback<Item>): Promise<Item | null | undefined>;
|
|
12
21
|
findByIndex(index: string, data: any, listener?: QueryListenerCallback<Item>): Promise<Item[]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadWriteTransaction } from "../db";
|
|
2
|
+
import type { DataStorage, ListenerEvent, QueryListenerCallback, StoreStateChange } from "./data-storage.interface";
|
|
2
3
|
import { StoreState } from "./store-state.abstract";
|
|
3
4
|
export declare class MasterStoreState<Item extends {
|
|
4
5
|
_id: string;
|
|
@@ -6,6 +7,15 @@ export declare class MasterStoreState<Item extends {
|
|
|
6
7
|
private items;
|
|
7
8
|
private isComplete;
|
|
8
9
|
constructor(storeName: string, dataStorage: DataStorage, deserialize?: (data: any) => Item);
|
|
10
|
+
applyChangeAndReturnEvent(transaction: ReadWriteTransaction, change: StoreStateChange<Item>): Promise<{
|
|
11
|
+
from: Item | null;
|
|
12
|
+
to: Item | null;
|
|
13
|
+
event: ListenerEvent<Item>;
|
|
14
|
+
}>;
|
|
15
|
+
applyChange(change: StoreStateChange<Item>): Promise<{
|
|
16
|
+
from: Item | null;
|
|
17
|
+
to: Item | null;
|
|
18
|
+
}>;
|
|
9
19
|
applyChanges(changes: StoreStateChange<Item>[]): Promise<void>;
|
|
10
20
|
findById(id: string, listener?: QueryListenerCallback<Item>): Promise<Item | null | undefined>;
|
|
11
21
|
findByIndex(index: string, data: any, listener?: QueryListenerCallback<Item>): Promise<Item[]>;
|
|
@@ -8,6 +8,10 @@ export declare abstract class StoreState<Item extends {
|
|
|
8
8
|
protected deserialize?: ((data: any) => Item) | undefined;
|
|
9
9
|
protected listeners: Set<QueryListener<Item>>;
|
|
10
10
|
constructor(storeName: string, dataStorage: DataStorage, deserialize?: ((data: any) => Item) | undefined);
|
|
11
|
+
abstract applyChange(change: StoreStateChange<Item>): Promise<{
|
|
12
|
+
from: Item | null;
|
|
13
|
+
to: Item | null;
|
|
14
|
+
}>;
|
|
11
15
|
abstract applyChanges(changes: StoreStateChange<Item>[]): Promise<void>;
|
|
12
16
|
abstract findById(id: string, listener?: QueryListenerCallback<Item>): Promise<Item | null | undefined>;
|
|
13
17
|
abstract findAll(listener?: QueryListenerCallback<Item>): Promise<Item[]>;
|
|
@@ -15,7 +19,10 @@ export declare abstract class StoreState<Item extends {
|
|
|
15
19
|
fork(): ForkedStoreState<Item>;
|
|
16
20
|
set(item: Item): Promise<void>;
|
|
17
21
|
remove(id: string): Promise<void>;
|
|
18
|
-
modify(id: string, data: Partial<Item>): Promise<
|
|
22
|
+
modify(id: string, data: Partial<Item>): Promise<{
|
|
23
|
+
from: Item | null;
|
|
24
|
+
to: Item | null;
|
|
25
|
+
}>;
|
|
19
26
|
unsubscribe(listener: QueryListener<Item>): void;
|
|
20
27
|
protected notifyListeners(events: ListenerEvent<Item>[]): void;
|
|
21
28
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// context/element.ts
|
|
2
|
+
class ArcContextElement {
|
|
3
|
+
$event;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
// collection/queries/abstract-collection-query.ts
|
|
2
7
|
class ArcCollectionQuery {
|
|
3
8
|
collection;
|
|
@@ -229,12 +234,13 @@ function collection(name, id, schema, options = {}) {
|
|
|
229
234
|
return new ArcCollection(name, id, schema, options);
|
|
230
235
|
}
|
|
231
236
|
|
|
232
|
-
class ArcCollection {
|
|
237
|
+
class ArcCollection extends ArcContextElement {
|
|
233
238
|
name;
|
|
234
239
|
id;
|
|
235
240
|
schema;
|
|
236
241
|
options;
|
|
237
242
|
constructor(name, id, schema, options) {
|
|
243
|
+
super();
|
|
238
244
|
this.name = name;
|
|
239
245
|
this.id = id;
|
|
240
246
|
this.schema = schema;
|
|
@@ -258,7 +264,7 @@ class ArcCollection {
|
|
|
258
264
|
one: (id) => new ArcOneItemQueryBuilder(this, id)
|
|
259
265
|
};
|
|
260
266
|
}
|
|
261
|
-
commandContext(dataStorage) {
|
|
267
|
+
commandContext(dataStorage, publishEvent) {
|
|
262
268
|
const store = dataStorage.getStore(this.name);
|
|
263
269
|
return {
|
|
264
270
|
add: async (data) => {
|
|
@@ -292,7 +298,13 @@ class ArcCollection {
|
|
|
292
298
|
},
|
|
293
299
|
modify: async (id, data) => {
|
|
294
300
|
const deserialized = this.schema.deserialize(data);
|
|
295
|
-
await store.modify(id, deserialized);
|
|
301
|
+
const { from, to } = await store.modify(id, deserialized);
|
|
302
|
+
await publishEvent({
|
|
303
|
+
type: "modify",
|
|
304
|
+
changes: data,
|
|
305
|
+
from,
|
|
306
|
+
to
|
|
307
|
+
});
|
|
296
308
|
}
|
|
297
309
|
};
|
|
298
310
|
}
|
|
@@ -307,8 +319,8 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
307
319
|
super(name, id, schema, options);
|
|
308
320
|
this.indexes = indexes;
|
|
309
321
|
}
|
|
310
|
-
commandContext(dataStorage) {
|
|
311
|
-
const superContext = super.commandContext(dataStorage);
|
|
322
|
+
commandContext(dataStorage, publishEvent) {
|
|
323
|
+
const superContext = super.commandContext(dataStorage, publishEvent);
|
|
312
324
|
return new Proxy(superContext, {
|
|
313
325
|
get: (target, name) => {
|
|
314
326
|
if (name in target) {
|
|
@@ -336,19 +348,21 @@ class ArcIndexedCollection extends ArcCollection {
|
|
|
336
348
|
}
|
|
337
349
|
}
|
|
338
350
|
// context/context.ts
|
|
339
|
-
function context(elements, commands,
|
|
340
|
-
return new ArcContext(elements, commands,
|
|
351
|
+
function context(version, elements, commands, listeners) {
|
|
352
|
+
return new ArcContext(version, elements, commands, listeners);
|
|
341
353
|
}
|
|
342
354
|
|
|
343
355
|
class ArcContext {
|
|
356
|
+
version;
|
|
344
357
|
elements;
|
|
345
358
|
commands;
|
|
346
|
-
|
|
359
|
+
listeners;
|
|
347
360
|
elementsMap;
|
|
348
|
-
constructor(elements, commands,
|
|
361
|
+
constructor(version, elements, commands, listeners) {
|
|
362
|
+
this.version = version;
|
|
349
363
|
this.elements = elements;
|
|
350
364
|
this.commands = commands;
|
|
351
|
-
this.
|
|
365
|
+
this.listeners = listeners;
|
|
352
366
|
this.elementsMap = Object.fromEntries(elements.map((collection3) => [collection3.name, collection3]));
|
|
353
367
|
}
|
|
354
368
|
queryBuilder() {
|
|
@@ -362,11 +376,35 @@ class ArcContext {
|
|
|
362
376
|
}
|
|
363
377
|
});
|
|
364
378
|
}
|
|
365
|
-
commandContext(dataStorage) {
|
|
379
|
+
commandContext(dataStorage, publishEvent) {
|
|
366
380
|
return new Proxy({}, {
|
|
367
381
|
get: (target, name) => {
|
|
368
382
|
const element = this.elementsMap[name];
|
|
369
|
-
return element.commandContext(dataStorage)
|
|
383
|
+
return element.commandContext(dataStorage, (event) => publishEvent({
|
|
384
|
+
element: name,
|
|
385
|
+
...event
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
commandsClient(dataStorage) {
|
|
391
|
+
return new Proxy({}, {
|
|
392
|
+
get: (_, name) => {
|
|
393
|
+
if (name in this.commands) {
|
|
394
|
+
return async (...args) => {
|
|
395
|
+
const forkedDataStorage = dataStorage.fork();
|
|
396
|
+
const commandContext = this.commandContext(forkedDataStorage, async (data) => {
|
|
397
|
+
if (!this.listeners)
|
|
398
|
+
return;
|
|
399
|
+
await Promise.all(this.listeners?.map((listener) => listener(data, commandContext)));
|
|
400
|
+
});
|
|
401
|
+
const result = await this.commands[name](commandContext, ...args);
|
|
402
|
+
await forkedDataStorage.merge();
|
|
403
|
+
return result;
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
console.warn(`Command '${name}' not found in the context.`);
|
|
407
|
+
return;
|
|
370
408
|
}
|
|
371
409
|
});
|
|
372
410
|
}
|
|
@@ -405,7 +443,8 @@ class StoreState {
|
|
|
405
443
|
id,
|
|
406
444
|
data
|
|
407
445
|
};
|
|
408
|
-
await this.
|
|
446
|
+
const { from, to } = await this.applyChange(change);
|
|
447
|
+
return { from, to };
|
|
409
448
|
}
|
|
410
449
|
unsubscribe(listener) {
|
|
411
450
|
this.listeners.delete(listener);
|
|
@@ -426,42 +465,62 @@ class ForkedStoreState extends StoreState {
|
|
|
426
465
|
super(storeName, dataStorage, deserialize);
|
|
427
466
|
this.master = master;
|
|
428
467
|
}
|
|
468
|
+
async applyChangeAndReturnEvent(change) {
|
|
469
|
+
this.changes.push(change);
|
|
470
|
+
if (change.type === "set") {
|
|
471
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
472
|
+
this.changedItems.set(change.data._id, item);
|
|
473
|
+
return {
|
|
474
|
+
from: null,
|
|
475
|
+
to: item,
|
|
476
|
+
event: {
|
|
477
|
+
type: "set",
|
|
478
|
+
item: change.data,
|
|
479
|
+
id: change.data._id
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
if (change.type === "delete") {
|
|
484
|
+
const from = await this.findById(change.id);
|
|
485
|
+
this.changedItems.set(change.id, null);
|
|
486
|
+
return {
|
|
487
|
+
from: from || null,
|
|
488
|
+
to: null,
|
|
489
|
+
event: {
|
|
490
|
+
type: "delete",
|
|
491
|
+
item: null,
|
|
492
|
+
id: change.id
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (change.type === "modify") {
|
|
497
|
+
const existing = await this.findById(change.id);
|
|
498
|
+
const updated = existing ? { ...existing, ...change.data } : change.data;
|
|
499
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
500
|
+
this.changedItems.set(change.id, item);
|
|
501
|
+
return {
|
|
502
|
+
from: existing || null,
|
|
503
|
+
to: item,
|
|
504
|
+
event: {
|
|
505
|
+
type: "set",
|
|
506
|
+
item,
|
|
507
|
+
id: change.id
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
throw new Error("Unknown change type");
|
|
512
|
+
}
|
|
513
|
+
async applyChange(change) {
|
|
514
|
+
const { event, from, to } = await this.applyChangeAndReturnEvent(change);
|
|
515
|
+
this.notifyListeners([event]);
|
|
516
|
+
return { from, to };
|
|
517
|
+
}
|
|
429
518
|
async applyChanges(changes) {
|
|
430
519
|
const events = [];
|
|
431
520
|
for (const change of changes) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
this.changedItems.set(change.data._id, item);
|
|
436
|
-
events.push({
|
|
437
|
-
type: "set",
|
|
438
|
-
item: change.data,
|
|
439
|
-
id: change.data._id
|
|
440
|
-
});
|
|
441
|
-
break;
|
|
442
|
-
case "delete":
|
|
443
|
-
this.changedItems.set(change.id, null);
|
|
444
|
-
events.push({
|
|
445
|
-
type: "delete",
|
|
446
|
-
item: null,
|
|
447
|
-
id: change.id
|
|
448
|
-
});
|
|
449
|
-
break;
|
|
450
|
-
case "modify":
|
|
451
|
-
{
|
|
452
|
-
const existing = await this.findById(change.id);
|
|
453
|
-
const updated = existing ? { ...existing, ...change.data } : change.data;
|
|
454
|
-
const item2 = this.deserialize ? this.deserialize(updated) : updated;
|
|
455
|
-
this.changedItems.set(change.id, item2);
|
|
456
|
-
events.push({
|
|
457
|
-
type: "set",
|
|
458
|
-
item: item2,
|
|
459
|
-
id: change.id
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
this.changes.push(change);
|
|
521
|
+
const { event } = await this.applyChangeAndReturnEvent(change);
|
|
522
|
+
if (event)
|
|
523
|
+
events.push(event);
|
|
465
524
|
}
|
|
466
525
|
if (events.length > 0) {
|
|
467
526
|
this.notifyListeners(events);
|
|
@@ -535,45 +594,65 @@ class MasterStoreState extends StoreState {
|
|
|
535
594
|
constructor(storeName, dataStorage, deserialize) {
|
|
536
595
|
super(storeName, dataStorage, deserialize);
|
|
537
596
|
}
|
|
597
|
+
async applyChangeAndReturnEvent(transaction, change) {
|
|
598
|
+
if (change.type === "set") {
|
|
599
|
+
await transaction.set(this.storeName, change.data);
|
|
600
|
+
const item = this.deserialize ? this.deserialize(change.data) : change.data;
|
|
601
|
+
this.items.set(change.data._id, item);
|
|
602
|
+
return {
|
|
603
|
+
from: null,
|
|
604
|
+
to: item,
|
|
605
|
+
event: {
|
|
606
|
+
type: "set",
|
|
607
|
+
item: change.data,
|
|
608
|
+
id: change.data._id
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (change.type === "delete") {
|
|
613
|
+
await transaction.remove(this.storeName, change.id);
|
|
614
|
+
this.items.set(change.id, null);
|
|
615
|
+
return {
|
|
616
|
+
from: null,
|
|
617
|
+
to: null,
|
|
618
|
+
event: {
|
|
619
|
+
type: "delete",
|
|
620
|
+
item: null,
|
|
621
|
+
id: change.id
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
if (change.type === "modify") {
|
|
626
|
+
const existing = await transaction.findById(this.storeName, change.id);
|
|
627
|
+
const updated = existing ? { ...existing, ...change.data } : change.data;
|
|
628
|
+
await transaction.set(this.storeName, updated);
|
|
629
|
+
const item = this.deserialize ? this.deserialize(updated) : updated;
|
|
630
|
+
this.items.set(change.id, item);
|
|
631
|
+
return {
|
|
632
|
+
from: null,
|
|
633
|
+
to: item,
|
|
634
|
+
event: {
|
|
635
|
+
type: "set",
|
|
636
|
+
item,
|
|
637
|
+
id: change.id
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
throw new Error("Unknown change type");
|
|
642
|
+
}
|
|
643
|
+
async applyChange(change) {
|
|
644
|
+
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
645
|
+
const { event, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
646
|
+
this.notifyListeners([event]);
|
|
647
|
+
return { from, to };
|
|
648
|
+
}
|
|
538
649
|
async applyChanges(changes) {
|
|
539
650
|
const transaction = await this.dataStorage.getReadWriteTransaction();
|
|
540
651
|
const events = [];
|
|
541
652
|
for (const change of changes) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
}
|
|
653
|
+
const { event } = await this.applyChangeAndReturnEvent(transaction, change);
|
|
654
|
+
if (event)
|
|
655
|
+
events.push(event);
|
|
577
656
|
}
|
|
578
657
|
await transaction.commit();
|
|
579
658
|
if (events.length > 0) {
|
|
@@ -665,6 +744,9 @@ class MasterDataStorage {
|
|
|
665
744
|
fork() {
|
|
666
745
|
return new ForkedDataStorage(this);
|
|
667
746
|
}
|
|
747
|
+
async sync(progressCallback) {
|
|
748
|
+
await this.rtcAdapter.sync(progressCallback);
|
|
749
|
+
}
|
|
668
750
|
}
|
|
669
751
|
// elements/optional.ts
|
|
670
752
|
class ArcOptional {
|
|
@@ -934,9 +1016,22 @@ class RTCClient {
|
|
|
934
1016
|
openSocket;
|
|
935
1017
|
reconnectAttempts = 0;
|
|
936
1018
|
maxReconnectAttempts = 5;
|
|
1019
|
+
syncProgressCallback;
|
|
1020
|
+
syncResolve;
|
|
1021
|
+
syncPromise = null;
|
|
1022
|
+
pendingStoreChanges = [];
|
|
937
1023
|
constructor(storage) {
|
|
938
1024
|
this.storage = storage;
|
|
939
|
-
|
|
1025
|
+
}
|
|
1026
|
+
async sync(progressCallback) {
|
|
1027
|
+
if (this.syncPromise)
|
|
1028
|
+
return this.syncPromise;
|
|
1029
|
+
this.syncProgressCallback = progressCallback;
|
|
1030
|
+
this.syncPromise = new Promise((resolve) => {
|
|
1031
|
+
this.syncResolve = resolve;
|
|
1032
|
+
this.connect();
|
|
1033
|
+
});
|
|
1034
|
+
return this.syncPromise;
|
|
940
1035
|
}
|
|
941
1036
|
async connect() {
|
|
942
1037
|
this._socket = new WebSocket(`wss://${window.location.host}/ws`);
|
|
@@ -982,6 +1077,7 @@ class RTCClient {
|
|
|
982
1077
|
case "sync-result":
|
|
983
1078
|
{
|
|
984
1079
|
const { store, items } = message;
|
|
1080
|
+
this.syncProgressCallback?.({ store, size: items.length });
|
|
985
1081
|
const storeState = this.storage.getStore(store);
|
|
986
1082
|
if (!storeState) {
|
|
987
1083
|
console.error(`Store ${store} not found`);
|
|
@@ -999,17 +1095,22 @@ class RTCClient {
|
|
|
999
1095
|
data: item
|
|
1000
1096
|
};
|
|
1001
1097
|
});
|
|
1002
|
-
storeState.applyChanges(changes);
|
|
1098
|
+
this.pendingStoreChanges.push(storeState.applyChanges(changes));
|
|
1003
1099
|
}
|
|
1004
1100
|
break;
|
|
1005
1101
|
case "sync-done":
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1102
|
+
Promise.all(this.pendingStoreChanges).then(() => {
|
|
1103
|
+
const stateStorage = this.storage.getStore("state");
|
|
1104
|
+
stateStorage.applyChanges([
|
|
1105
|
+
{
|
|
1106
|
+
type: "set",
|
|
1107
|
+
data: { _id: "$arc", lastSyncDate: message.date }
|
|
1108
|
+
}
|
|
1109
|
+
]).then(() => {
|
|
1110
|
+
this.pendingStoreChanges = [];
|
|
1111
|
+
this.syncResolve?.();
|
|
1112
|
+
});
|
|
1113
|
+
});
|
|
1013
1114
|
break;
|
|
1014
1115
|
case "state-changes":
|
|
1015
1116
|
break;
|
|
@@ -1025,33 +1126,6 @@ class RTCClient {
|
|
|
1025
1126
|
var rtcClientFactory = (storage) => {
|
|
1026
1127
|
return new RTCClient(storage);
|
|
1027
1128
|
};
|
|
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
|
-
}
|
|
1055
1129
|
export {
|
|
1056
1130
|
stringEnum,
|
|
1057
1131
|
string,
|
|
@@ -1059,7 +1133,6 @@ export {
|
|
|
1059
1133
|
object,
|
|
1060
1134
|
number,
|
|
1061
1135
|
id,
|
|
1062
|
-
deserializeChanges,
|
|
1063
1136
|
date,
|
|
1064
1137
|
context,
|
|
1065
1138
|
collection,
|
package/dist/rtc/index.d.ts
CHANGED
package/dist/rtc/rtc.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { DataStorage, StoreStateChange } from "../data-storage/data-storage.interface";
|
|
2
2
|
export interface RealTimeCommunicationAdapter {
|
|
3
3
|
commitChanges(changes: StoreStateChange<any>[]): void;
|
|
4
|
+
sync(progressCallback: ({ store, size }: {
|
|
5
|
+
store: string;
|
|
6
|
+
size: number;
|
|
7
|
+
}) => void): Promise<void>;
|
|
4
8
|
}
|
|
5
9
|
export type RealTimeCommunicationAdapterFactory = (storage: DataStorage) => RealTimeCommunicationAdapter;
|
|
6
10
|
//# sourceMappingURL=rtc.d.ts.map
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.0.
|
|
7
|
+
"version": "0.0.16",
|
|
8
8
|
"private": false,
|
|
9
9
|
"author": "Przemysław Krasiński [arcote.tech]",
|
|
10
10
|
"description": "Arc is a framework designed to align code closely with business logic, streamlining development and enhancing productivity.",
|