@verdant-web/store 2.8.5 → 3.0.0-next.0
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/bundle/index.js +9 -10
- package/dist/bundle/index.js.map +4 -4
- package/dist/cjs/DocumentManager.d.ts +6 -5
- package/dist/cjs/DocumentManager.js +2 -2
- package/dist/cjs/DocumentManager.js.map +1 -1
- package/dist/cjs/IDBService.d.ts +28 -7
- package/dist/cjs/IDBService.js +50 -13
- package/dist/cjs/IDBService.js.map +1 -1
- package/dist/cjs/UndoHistory.d.ts +1 -1
- package/dist/cjs/UndoHistory.js +6 -2
- package/dist/cjs/UndoHistory.js.map +1 -1
- package/dist/cjs/__tests__/batching.test.js +3 -1
- package/dist/cjs/__tests__/batching.test.js.map +1 -1
- package/dist/cjs/__tests__/documents.test.js +37 -6
- package/dist/cjs/__tests__/documents.test.js.map +1 -1
- package/dist/cjs/__tests__/fixtures/testStorage.d.ts +2 -2
- package/dist/cjs/__tests__/fixtures/testStorage.js +2 -1
- package/dist/cjs/__tests__/fixtures/testStorage.js.map +1 -1
- package/dist/cjs/__tests__/legacyOids.test.js +50 -17
- package/dist/cjs/__tests__/legacyOids.test.js.map +1 -1
- package/dist/cjs/__tests__/mutations.test.js +9 -3
- package/dist/cjs/__tests__/mutations.test.js.map +1 -1
- package/dist/cjs/__tests__/queries.test.js +6 -2
- package/dist/cjs/__tests__/queries.test.js.map +1 -1
- package/dist/cjs/__tests__/setup/indexedDB.d.ts +1 -1
- package/dist/cjs/__tests__/setup/indexedDB.js +8 -1
- package/dist/cjs/__tests__/setup/indexedDB.js.map +1 -1
- package/dist/cjs/__tests__/undo.test.js +16 -9
- package/dist/cjs/__tests__/undo.test.js.map +1 -1
- package/dist/cjs/client/Client.d.ts +2 -3
- package/dist/cjs/client/Client.js +8 -4
- package/dist/cjs/client/Client.js.map +1 -1
- package/dist/cjs/client/ClientDescriptor.js +21 -6
- package/dist/cjs/client/ClientDescriptor.js.map +1 -1
- package/dist/cjs/context.d.ts +10 -1
- package/dist/cjs/entities/2/Entity.d.ts +148 -0
- package/dist/cjs/entities/2/Entity.js +711 -0
- package/dist/cjs/entities/2/Entity.js.map +1 -0
- package/dist/cjs/entities/2/Entity.test.d.ts +1 -0
- package/dist/cjs/entities/2/Entity.test.js +194 -0
- package/dist/cjs/entities/2/Entity.test.js.map +1 -0
- package/dist/cjs/entities/2/EntityCache.d.ts +15 -0
- package/dist/cjs/entities/2/EntityCache.js +39 -0
- package/dist/cjs/entities/2/EntityCache.js.map +1 -0
- package/dist/cjs/entities/2/EntityMetadata.d.ts +68 -0
- package/dist/cjs/entities/2/EntityMetadata.js +261 -0
- package/dist/cjs/entities/2/EntityMetadata.js.map +1 -0
- package/dist/cjs/entities/2/EntityStore.d.ts +78 -0
- package/dist/cjs/entities/2/EntityStore.js +352 -0
- package/dist/cjs/entities/2/EntityStore.js.map +1 -0
- package/dist/cjs/entities/2/OperationBatcher.d.ts +52 -0
- package/dist/cjs/entities/2/OperationBatcher.js +165 -0
- package/dist/cjs/entities/2/OperationBatcher.js.map +1 -0
- package/dist/cjs/entities/2/types.d.ts +84 -0
- package/dist/cjs/entities/2/types.js +3 -0
- package/dist/cjs/entities/2/types.js.map +1 -0
- package/dist/cjs/entities/Entity.d.ts +0 -7
- package/dist/cjs/entities/Entity.js +7 -0
- package/dist/cjs/entities/Entity.js.map +1 -1
- package/dist/cjs/entities/EntityStore.js +4 -20
- package/dist/cjs/entities/EntityStore.js.map +1 -1
- package/dist/cjs/entities/FakeWeakRef.d.ts +11 -0
- package/dist/cjs/entities/FakeWeakRef.js +19 -0
- package/dist/cjs/entities/FakeWeakRef.js.map +1 -0
- package/dist/cjs/files/EntityFile.d.ts +5 -2
- package/dist/cjs/files/EntityFile.js +8 -4
- package/dist/cjs/files/EntityFile.js.map +1 -1
- package/dist/cjs/files/FileManager.d.ts +3 -1
- package/dist/cjs/files/FileManager.js +5 -3
- package/dist/cjs/files/FileManager.js.map +1 -1
- package/dist/cjs/files/FileStorage.js +7 -7
- package/dist/cjs/files/FileStorage.js.map +1 -1
- package/dist/cjs/files/utils.d.ts +2 -0
- package/dist/cjs/files/utils.js +5 -2
- package/dist/cjs/files/utils.js.map +1 -1
- package/dist/cjs/idb.d.ts +2 -0
- package/dist/cjs/idb.js +50 -4
- package/dist/cjs/idb.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/indexes.d.ts +3 -0
- package/dist/cjs/indexes.js +20 -0
- package/dist/cjs/indexes.js.map +1 -0
- package/dist/cjs/metadata/AckInfoStore.js +1 -1
- package/dist/cjs/metadata/AckInfoStore.js.map +1 -1
- package/dist/cjs/metadata/BaselinesStore.d.ts +4 -1
- package/dist/cjs/metadata/BaselinesStore.js +19 -10
- package/dist/cjs/metadata/BaselinesStore.js.map +1 -1
- package/dist/cjs/metadata/LocalReplicaStore.d.ts +1 -1
- package/dist/cjs/metadata/LocalReplicaStore.js +11 -5
- package/dist/cjs/metadata/LocalReplicaStore.js.map +1 -1
- package/dist/cjs/metadata/Metadata.d.ts +26 -5
- package/dist/cjs/metadata/Metadata.js +55 -18
- package/dist/cjs/metadata/Metadata.js.map +1 -1
- package/dist/cjs/metadata/OperationsStore.d.ts +3 -0
- package/dist/cjs/metadata/OperationsStore.js +35 -15
- package/dist/cjs/metadata/OperationsStore.js.map +1 -1
- package/dist/cjs/migration/openDatabase.js +31 -10
- package/dist/cjs/migration/openDatabase.js.map +1 -1
- package/dist/cjs/queries/BaseQuery.js +14 -2
- package/dist/cjs/queries/BaseQuery.js.map +1 -1
- package/dist/cjs/queries/CollectionQueries.d.ts +2 -4
- package/dist/cjs/queries/CollectionQueries.js +1 -1
- package/dist/cjs/queries/CollectionQueries.js.map +1 -1
- package/dist/cjs/queries/FindAllQuery.js +1 -0
- package/dist/cjs/queries/FindAllQuery.js.map +1 -1
- package/dist/cjs/queries/QueryCache.d.ts +1 -0
- package/dist/cjs/queries/QueryCache.js +4 -0
- package/dist/cjs/queries/QueryCache.js.map +1 -1
- package/dist/cjs/queries/QueryableStorage.d.ts +20 -0
- package/dist/cjs/queries/QueryableStorage.js +84 -0
- package/dist/cjs/queries/QueryableStorage.js.map +1 -0
- package/dist/cjs/queries/dbQueries.js +13 -3
- package/dist/cjs/queries/dbQueries.js.map +1 -1
- package/dist/cjs/queries/utils.js +1 -1
- package/dist/cjs/queries/utils.js.map +1 -1
- package/dist/cjs/sync/FileSync.d.ts +1 -0
- package/dist/cjs/sync/FileSync.js +1 -0
- package/dist/cjs/sync/FileSync.js.map +1 -1
- package/dist/cjs/sync/PushPullSync.d.ts +2 -1
- package/dist/cjs/sync/PushPullSync.js +7 -1
- package/dist/cjs/sync/PushPullSync.js.map +1 -1
- package/dist/cjs/sync/Sync.d.ts +6 -3
- package/dist/cjs/sync/Sync.js +9 -4
- package/dist/cjs/sync/Sync.js.map +1 -1
- package/dist/cjs/sync/WebSocketSync.d.ts +4 -1
- package/dist/cjs/sync/WebSocketSync.js +41 -11
- package/dist/cjs/sync/WebSocketSync.js.map +1 -1
- package/dist/esm/DocumentManager.d.ts +6 -5
- package/dist/esm/DocumentManager.js +2 -2
- package/dist/esm/DocumentManager.js.map +1 -1
- package/dist/esm/IDBService.d.ts +28 -7
- package/dist/esm/IDBService.js +51 -14
- package/dist/esm/IDBService.js.map +1 -1
- package/dist/esm/UndoHistory.d.ts +1 -1
- package/dist/esm/UndoHistory.js +6 -2
- package/dist/esm/UndoHistory.js.map +1 -1
- package/dist/esm/__tests__/batching.test.js +3 -1
- package/dist/esm/__tests__/batching.test.js.map +1 -1
- package/dist/esm/__tests__/documents.test.js +37 -6
- package/dist/esm/__tests__/documents.test.js.map +1 -1
- package/dist/esm/__tests__/fixtures/testStorage.d.ts +2 -2
- package/dist/esm/__tests__/fixtures/testStorage.js +2 -1
- package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
- package/dist/esm/__tests__/legacyOids.test.js +50 -17
- package/dist/esm/__tests__/legacyOids.test.js.map +1 -1
- package/dist/esm/__tests__/mutations.test.js +9 -3
- package/dist/esm/__tests__/mutations.test.js.map +1 -1
- package/dist/esm/__tests__/queries.test.js +6 -2
- package/dist/esm/__tests__/queries.test.js.map +1 -1
- package/dist/esm/__tests__/setup/indexedDB.d.ts +1 -1
- package/dist/esm/__tests__/setup/indexedDB.js +8 -1
- package/dist/esm/__tests__/setup/indexedDB.js.map +1 -1
- package/dist/esm/__tests__/undo.test.js +16 -9
- package/dist/esm/__tests__/undo.test.js.map +1 -1
- package/dist/esm/client/Client.d.ts +2 -3
- package/dist/esm/client/Client.js +8 -4
- package/dist/esm/client/Client.js.map +1 -1
- package/dist/esm/client/ClientDescriptor.js +21 -6
- package/dist/esm/client/ClientDescriptor.js.map +1 -1
- package/dist/esm/context.d.ts +10 -1
- package/dist/esm/entities/2/Entity.d.ts +148 -0
- package/dist/esm/entities/2/Entity.js +707 -0
- package/dist/esm/entities/2/Entity.js.map +1 -0
- package/dist/esm/entities/2/Entity.test.d.ts +1 -0
- package/dist/esm/entities/2/Entity.test.js +192 -0
- package/dist/esm/entities/2/Entity.test.js.map +1 -0
- package/dist/esm/entities/2/EntityCache.d.ts +15 -0
- package/dist/esm/entities/2/EntityCache.js +35 -0
- package/dist/esm/entities/2/EntityCache.js.map +1 -0
- package/dist/esm/entities/2/EntityMetadata.d.ts +68 -0
- package/dist/esm/entities/2/EntityMetadata.js +256 -0
- package/dist/esm/entities/2/EntityMetadata.js.map +1 -0
- package/dist/esm/entities/2/EntityStore.d.ts +78 -0
- package/dist/esm/entities/2/EntityStore.js +348 -0
- package/dist/esm/entities/2/EntityStore.js.map +1 -0
- package/dist/esm/entities/2/OperationBatcher.d.ts +52 -0
- package/dist/esm/entities/2/OperationBatcher.js +161 -0
- package/dist/esm/entities/2/OperationBatcher.js.map +1 -0
- package/dist/esm/entities/2/types.d.ts +84 -0
- package/dist/esm/entities/2/types.js +2 -0
- package/dist/esm/entities/2/types.js.map +1 -0
- package/dist/esm/entities/Entity.d.ts +0 -7
- package/dist/esm/entities/Entity.js +7 -0
- package/dist/esm/entities/Entity.js.map +1 -1
- package/dist/esm/entities/EntityStore.js +4 -20
- package/dist/esm/entities/EntityStore.js.map +1 -1
- package/dist/esm/entities/FakeWeakRef.d.ts +11 -0
- package/dist/esm/entities/FakeWeakRef.js +15 -0
- package/dist/esm/entities/FakeWeakRef.js.map +1 -0
- package/dist/esm/files/EntityFile.d.ts +5 -2
- package/dist/esm/files/EntityFile.js +8 -4
- package/dist/esm/files/EntityFile.js.map +1 -1
- package/dist/esm/files/FileManager.d.ts +3 -1
- package/dist/esm/files/FileManager.js +5 -3
- package/dist/esm/files/FileManager.js.map +1 -1
- package/dist/esm/files/FileStorage.js +7 -7
- package/dist/esm/files/FileStorage.js.map +1 -1
- package/dist/esm/files/utils.d.ts +2 -0
- package/dist/esm/files/utils.js +4 -2
- package/dist/esm/files/utils.js.map +1 -1
- package/dist/esm/idb.d.ts +2 -0
- package/dist/esm/idb.js +47 -3
- package/dist/esm/idb.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes.d.ts +3 -0
- package/dist/esm/indexes.js +15 -0
- package/dist/esm/indexes.js.map +1 -0
- package/dist/esm/metadata/AckInfoStore.js +1 -1
- package/dist/esm/metadata/AckInfoStore.js.map +1 -1
- package/dist/esm/metadata/BaselinesStore.d.ts +4 -1
- package/dist/esm/metadata/BaselinesStore.js +19 -10
- package/dist/esm/metadata/BaselinesStore.js.map +1 -1
- package/dist/esm/metadata/LocalReplicaStore.d.ts +1 -1
- package/dist/esm/metadata/LocalReplicaStore.js +11 -5
- package/dist/esm/metadata/LocalReplicaStore.js.map +1 -1
- package/dist/esm/metadata/Metadata.d.ts +26 -5
- package/dist/esm/metadata/Metadata.js +56 -19
- package/dist/esm/metadata/Metadata.js.map +1 -1
- package/dist/esm/metadata/OperationsStore.d.ts +3 -0
- package/dist/esm/metadata/OperationsStore.js +35 -15
- package/dist/esm/metadata/OperationsStore.js.map +1 -1
- package/dist/esm/migration/openDatabase.js +32 -11
- package/dist/esm/migration/openDatabase.js.map +1 -1
- package/dist/esm/queries/BaseQuery.js +14 -2
- package/dist/esm/queries/BaseQuery.js.map +1 -1
- package/dist/esm/queries/CollectionQueries.d.ts +2 -4
- package/dist/esm/queries/CollectionQueries.js +1 -1
- package/dist/esm/queries/CollectionQueries.js.map +1 -1
- package/dist/esm/queries/FindAllQuery.js +1 -0
- package/dist/esm/queries/FindAllQuery.js.map +1 -1
- package/dist/esm/queries/QueryCache.d.ts +1 -0
- package/dist/esm/queries/QueryCache.js +4 -0
- package/dist/esm/queries/QueryCache.js.map +1 -1
- package/dist/esm/queries/QueryableStorage.d.ts +20 -0
- package/dist/esm/queries/QueryableStorage.js +80 -0
- package/dist/esm/queries/QueryableStorage.js.map +1 -0
- package/dist/esm/queries/dbQueries.js +13 -3
- package/dist/esm/queries/dbQueries.js.map +1 -1
- package/dist/esm/queries/utils.js +1 -1
- package/dist/esm/queries/utils.js.map +1 -1
- package/dist/esm/sync/FileSync.d.ts +1 -0
- package/dist/esm/sync/FileSync.js +1 -0
- package/dist/esm/sync/FileSync.js.map +1 -1
- package/dist/esm/sync/PushPullSync.d.ts +2 -1
- package/dist/esm/sync/PushPullSync.js +7 -1
- package/dist/esm/sync/PushPullSync.js.map +1 -1
- package/dist/esm/sync/Sync.d.ts +6 -3
- package/dist/esm/sync/Sync.js +9 -4
- package/dist/esm/sync/Sync.js.map +1 -1
- package/dist/esm/sync/WebSocketSync.d.ts +4 -1
- package/dist/esm/sync/WebSocketSync.js +41 -11
- package/dist/esm/sync/WebSocketSync.js.map +1 -1
- package/dist/tsconfig-cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/DocumentManager.ts +3 -7
- package/src/IDBService.ts +78 -17
- package/src/UndoHistory.ts +5 -3
- package/src/__tests__/batching.test.ts +5 -2
- package/src/__tests__/documents.test.ts +44 -6
- package/src/__tests__/fixtures/testStorage.ts +3 -0
- package/src/__tests__/legacyOids.test.ts +53 -17
- package/src/__tests__/mutations.test.ts +9 -3
- package/src/__tests__/queries.test.ts +6 -2
- package/src/__tests__/setup/indexedDB.ts +8 -1
- package/src/__tests__/undo.test.ts +17 -9
- package/src/client/Client.ts +8 -4
- package/src/client/ClientDescriptor.ts +24 -8
- package/src/context.ts +16 -1
- package/src/entities/2/Entity.test.ts +218 -0
- package/src/entities/2/Entity.ts +954 -0
- package/src/entities/2/EntityCache.ts +41 -0
- package/src/entities/2/EntityMetadata.ts +364 -0
- package/src/entities/2/EntityStore.ts +490 -0
- package/src/entities/2/NOTES.md +22 -0
- package/src/entities/2/OperationBatcher.ts +251 -0
- package/src/entities/2/types.ts +154 -0
- package/src/files/EntityFile.ts +9 -4
- package/src/files/FileManager.ts +5 -3
- package/src/files/FileStorage.ts +7 -13
- package/src/files/utils.ts +6 -2
- package/src/idb.ts +51 -3
- package/src/index.ts +2 -2
- package/src/metadata/AckInfoStore.ts +1 -1
- package/src/metadata/BaselinesStore.ts +16 -24
- package/src/metadata/LocalReplicaStore.ts +13 -6
- package/src/metadata/Metadata.ts +109 -24
- package/src/metadata/OperationsStore.ts +37 -16
- package/src/migration/openDatabase.ts +32 -10
- package/src/queries/BaseQuery.ts +15 -2
- package/src/queries/CollectionQueries.ts +3 -3
- package/src/queries/FindAllQuery.ts +4 -0
- package/src/queries/QueryCache.ts +5 -0
- package/src/queries/QueryableStorage.ts +107 -0
- package/src/queries/dbQueries.ts +10 -3
- package/src/queries/utils.ts +1 -1
- package/src/sync/FileSync.ts +2 -0
- package/src/sync/PushPullSync.ts +8 -1
- package/src/sync/Sync.ts +14 -6
- package/src/sync/WebSocketSync.ts +47 -10
- package/src/entities/DocumentFamiliyCache.ts +0 -426
- package/src/entities/Entity.ts +0 -874
- package/src/entities/EntityStore.ts +0 -731
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DocumentBaseline, ObjectIdentifier, Operation } from '@verdant-web/common';
|
|
2
|
+
import { Context } from '../../context.js';
|
|
3
|
+
import { Metadata } from '../../metadata/Metadata.js';
|
|
4
|
+
import { Entity } from './Entity.js';
|
|
5
|
+
import { Disposable } from '../../utils/Disposable.js';
|
|
6
|
+
import { FileManager } from '../../files/FileManager.js';
|
|
7
|
+
import { WeakEvent } from 'weak-event';
|
|
8
|
+
import { abort } from 'process';
|
|
9
|
+
export type EntityStoreEventData = {
|
|
10
|
+
oid: ObjectIdentifier;
|
|
11
|
+
operations?: Record<string, Operation[]>;
|
|
12
|
+
baselines?: DocumentBaseline[];
|
|
13
|
+
isLocal: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type EntityStoreEvents = {
|
|
16
|
+
add: WeakEvent<EntityStore, EntityStoreEventData>;
|
|
17
|
+
replace: WeakEvent<EntityStore, EntityStoreEventData>;
|
|
18
|
+
resetAll: WeakEvent<EntityStore, void>;
|
|
19
|
+
};
|
|
20
|
+
type IncomingData = {
|
|
21
|
+
operations?: Operation[];
|
|
22
|
+
baselines?: DocumentBaseline[];
|
|
23
|
+
reset?: boolean;
|
|
24
|
+
isLocal?: boolean;
|
|
25
|
+
};
|
|
26
|
+
export declare class EntityStore extends Disposable {
|
|
27
|
+
private ctx;
|
|
28
|
+
private meta;
|
|
29
|
+
private files;
|
|
30
|
+
private batcher;
|
|
31
|
+
private queryableStorage;
|
|
32
|
+
private events;
|
|
33
|
+
private cache;
|
|
34
|
+
private pendingEntityPromises;
|
|
35
|
+
private abortDataQueueController;
|
|
36
|
+
private ongoingResetPromise;
|
|
37
|
+
private entityFinalizationRegistry;
|
|
38
|
+
constructor({ ctx, meta, files, }: {
|
|
39
|
+
ctx: Context;
|
|
40
|
+
meta: Metadata;
|
|
41
|
+
files: FileManager;
|
|
42
|
+
});
|
|
43
|
+
get batch(): ({ undoable, batchName, max, timeout, }?: {
|
|
44
|
+
undoable?: boolean | undefined;
|
|
45
|
+
batchName?: string | undefined;
|
|
46
|
+
max?: number | null | undefined;
|
|
47
|
+
timeout?: number | null | undefined;
|
|
48
|
+
}) => import("./OperationBatcher.js").OperationBatch;
|
|
49
|
+
get flushAllBatches(): () => Promise<any[]>;
|
|
50
|
+
addData: (data: IncomingData) => Promise<void>;
|
|
51
|
+
private resetData;
|
|
52
|
+
private processData;
|
|
53
|
+
hydrate: (oid: string, opts?: {
|
|
54
|
+
abort: AbortSignal;
|
|
55
|
+
}) => Promise<Entity | null>;
|
|
56
|
+
destroy: () => Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new Entity with the given initial data.
|
|
59
|
+
*/
|
|
60
|
+
create: (initial: any, oid: ObjectIdentifier) => Promise<Entity<any, any, any>>;
|
|
61
|
+
deleteAll: (oids: ObjectIdentifier[], options?: {
|
|
62
|
+
undoable?: boolean;
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
delete: (oid: ObjectIdentifier, options?: {
|
|
65
|
+
undoable?: boolean;
|
|
66
|
+
}) => Promise<void>;
|
|
67
|
+
private getCollectionSchema;
|
|
68
|
+
/**
|
|
69
|
+
* Constructs an entity from an OID, but does not load it.
|
|
70
|
+
*/
|
|
71
|
+
private constructEntity;
|
|
72
|
+
private onPendingOperations;
|
|
73
|
+
/**
|
|
74
|
+
* Loads initial Entity data from storage
|
|
75
|
+
*/
|
|
76
|
+
private loadEntity;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { assert, assignOid, decomposeOid, getOidRoot, groupBaselinesByRootOid, groupPatchesByOid, groupPatchesByRootOid, isRootOid, removeOidsFromAllSubObjects, } from '@verdant-web/common';
|
|
2
|
+
import { Entity } from './Entity.js';
|
|
3
|
+
import { Disposable } from '../../utils/Disposable.js';
|
|
4
|
+
import { EntityFamilyMetadata } from './EntityMetadata.js';
|
|
5
|
+
import { OperationBatcher } from './OperationBatcher.js';
|
|
6
|
+
import { QueryableStorage } from '../../queries/QueryableStorage.js';
|
|
7
|
+
import { WeakEvent } from 'weak-event';
|
|
8
|
+
import { processValueFiles } from '../../files/utils.js';
|
|
9
|
+
var AbortReason;
|
|
10
|
+
(function (AbortReason) {
|
|
11
|
+
AbortReason[AbortReason["Reset"] = 0] = "Reset";
|
|
12
|
+
})(AbortReason || (AbortReason = {}));
|
|
13
|
+
export class EntityStore extends Disposable {
|
|
14
|
+
constructor({ ctx, meta, files, }) {
|
|
15
|
+
super();
|
|
16
|
+
this.events = {
|
|
17
|
+
add: new WeakEvent(),
|
|
18
|
+
replace: new WeakEvent(),
|
|
19
|
+
resetAll: new WeakEvent(),
|
|
20
|
+
};
|
|
21
|
+
this.cache = new Map();
|
|
22
|
+
this.pendingEntityPromises = new Map();
|
|
23
|
+
// halts the current data queue processing
|
|
24
|
+
this.abortDataQueueController = new AbortController();
|
|
25
|
+
this.ongoingResetPromise = null;
|
|
26
|
+
this.entityFinalizationRegistry = new FinalizationRegistry((oid) => {
|
|
27
|
+
this.ctx.log('debug', 'Entity GC', oid);
|
|
28
|
+
});
|
|
29
|
+
// internal-ish API to load remote / stored data
|
|
30
|
+
this.addData = async (data) => {
|
|
31
|
+
if (this.disposed) {
|
|
32
|
+
this.ctx.log('warn', 'EntityStore is disposed, not adding incoming data');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// for resets - abort any other changes, reset everything,
|
|
36
|
+
// then proceed
|
|
37
|
+
if (data.reset) {
|
|
38
|
+
this.ctx.log('info', 'Resetting local store to replicate remote synced data - dropping any current transactions');
|
|
39
|
+
// cancel any other ongoing data - it will all
|
|
40
|
+
// be replaced by the reset
|
|
41
|
+
this.abortDataQueueController.abort(AbortReason.Reset);
|
|
42
|
+
this.abortDataQueueController = new AbortController();
|
|
43
|
+
this.ongoingResetPromise = this.resetData().finally(() => {
|
|
44
|
+
this.ongoingResetPromise = null;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// await either the reset we just started, or any that was
|
|
48
|
+
// in progress when this data came in.
|
|
49
|
+
if (this.ongoingResetPromise) {
|
|
50
|
+
this.ctx.log('debug', 'Waiting for ongoing reset to complete');
|
|
51
|
+
await this.ongoingResetPromise;
|
|
52
|
+
this.ctx.log('debug', 'Ongoing reset complete');
|
|
53
|
+
}
|
|
54
|
+
await this.processData(data);
|
|
55
|
+
};
|
|
56
|
+
this.resetData = async () => {
|
|
57
|
+
if (this.disposed) {
|
|
58
|
+
this.ctx.log('warn', 'EntityStore is disposed, not resetting local data');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await this.meta.reset();
|
|
62
|
+
await this.queryableStorage.reset();
|
|
63
|
+
this.events.resetAll.invoke(this);
|
|
64
|
+
};
|
|
65
|
+
this.processData = async (data) => {
|
|
66
|
+
var _a, _b, _c;
|
|
67
|
+
if (this.disposed) {
|
|
68
|
+
this.ctx.log('warn', 'EntityStore is disposed, not processing incoming data');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const baselines = (_a = data === null || data === void 0 ? void 0 : data.baselines) !== null && _a !== void 0 ? _a : [];
|
|
72
|
+
const operations = (_b = data === null || data === void 0 ? void 0 : data.operations) !== null && _b !== void 0 ? _b : [];
|
|
73
|
+
this.ctx.log('debug', 'Processing incoming data', {
|
|
74
|
+
operations: operations.length,
|
|
75
|
+
baselines: baselines.length,
|
|
76
|
+
reset: !!data.reset,
|
|
77
|
+
});
|
|
78
|
+
const allDocumentOids = Array.from(new Set(baselines
|
|
79
|
+
.map((b) => getOidRoot(b.oid))
|
|
80
|
+
.concat(operations.map((o) => getOidRoot(o.oid)))));
|
|
81
|
+
const baselinesGroupedByOid = groupBaselinesByRootOid(baselines);
|
|
82
|
+
const operationsGroupedByOid = groupPatchesByRootOid(operations);
|
|
83
|
+
this.ctx.log('debug', 'Applying data to live entities');
|
|
84
|
+
// synchronously add/replace data in any open entities via eventing
|
|
85
|
+
for (const oid of allDocumentOids) {
|
|
86
|
+
const baselines = baselinesGroupedByOid[oid];
|
|
87
|
+
const operations = (_c = operationsGroupedByOid[oid]) !== null && _c !== void 0 ? _c : [];
|
|
88
|
+
const groupedOperations = groupPatchesByOid(operations);
|
|
89
|
+
// what happens if an entity is being hydrated
|
|
90
|
+
// while this is happening? - we wait for the hydration promise
|
|
91
|
+
// to complete, then invoke the event
|
|
92
|
+
const event = data.reset ? this.events.replace : this.events.add;
|
|
93
|
+
const hydrationPromise = this.pendingEntityPromises.get(oid);
|
|
94
|
+
if (hydrationPromise) {
|
|
95
|
+
hydrationPromise.then(() => {
|
|
96
|
+
event.invoke(this, {
|
|
97
|
+
oid,
|
|
98
|
+
baselines,
|
|
99
|
+
operations: groupedOperations,
|
|
100
|
+
isLocal: false,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (this.cache.has(oid)) {
|
|
106
|
+
this.ctx.log('debug', 'Cache has', oid, ', an event should follow.');
|
|
107
|
+
}
|
|
108
|
+
event.invoke(this, {
|
|
109
|
+
oid,
|
|
110
|
+
baselines,
|
|
111
|
+
operations: groupedOperations,
|
|
112
|
+
isLocal: false,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const abortOptions = {
|
|
117
|
+
abort: this.abortDataQueueController.signal,
|
|
118
|
+
};
|
|
119
|
+
// then, asynchronously add to the database
|
|
120
|
+
await this.meta.insertData(data, abortOptions);
|
|
121
|
+
// FIXME: entities hydrated here are not seeing
|
|
122
|
+
// the operations just inserted above!!
|
|
123
|
+
// IDEA: can we coordinate here with hydrate promises
|
|
124
|
+
// based on affected OIDs?
|
|
125
|
+
// recompute all affected documents for querying
|
|
126
|
+
const entities = await Promise.all(allDocumentOids.map(async (oid) => {
|
|
127
|
+
const entity = await this.hydrate(oid, abortOptions);
|
|
128
|
+
// if the entity is not found, we return a stub that
|
|
129
|
+
// indicates it's deleted and should be cleared
|
|
130
|
+
return (entity !== null && entity !== void 0 ? entity : {
|
|
131
|
+
oid,
|
|
132
|
+
getSnapshot() {
|
|
133
|
+
return null;
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}));
|
|
137
|
+
try {
|
|
138
|
+
await this.queryableStorage.saveEntities(entities, abortOptions);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (this.disposed) {
|
|
142
|
+
this.ctx.log('warn', 'Error saving entities to queryable storage - EntityStore is disposed', err);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this.ctx.log('error', 'Error saving entities to queryable storage', err);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
// internal-ish API for creating Entities from OIDs
|
|
150
|
+
// when query results come in
|
|
151
|
+
this.hydrate = async (oid, opts) => {
|
|
152
|
+
if (!isRootOid(oid)) {
|
|
153
|
+
throw new Error('Cannot hydrate non-root entity');
|
|
154
|
+
}
|
|
155
|
+
if (this.cache.has(oid)) {
|
|
156
|
+
this.ctx.log('debug', 'Hydrating entity from cache', oid);
|
|
157
|
+
const cached = this.cache.get(oid);
|
|
158
|
+
if (cached) {
|
|
159
|
+
const entity = cached.deref();
|
|
160
|
+
if (entity) {
|
|
161
|
+
if (entity.deleted) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return entity;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.ctx.log('debug', "Removing GC'd entity from cache", oid);
|
|
168
|
+
this.cache.delete(oid);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// we don't want to hydrate two entities in parallel, so
|
|
173
|
+
// we use a promise to ensure that only one is ever
|
|
174
|
+
// constructed at a time
|
|
175
|
+
const pendingPromise = this.pendingEntityPromises.get(oid);
|
|
176
|
+
if (!pendingPromise) {
|
|
177
|
+
this.ctx.log('debug', 'Hydrating entity from storage', oid);
|
|
178
|
+
const entity = this.constructEntity(oid);
|
|
179
|
+
if (!entity) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
const pendingPromise = this.loadEntity(entity, opts);
|
|
183
|
+
pendingPromise.finally(() => {
|
|
184
|
+
this.pendingEntityPromises.delete(oid);
|
|
185
|
+
});
|
|
186
|
+
this.pendingEntityPromises.set(oid, pendingPromise);
|
|
187
|
+
return pendingPromise;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
this.ctx.log('debug', 'Waiting for entity hydration', oid);
|
|
191
|
+
return pendingPromise;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
this.destroy = async () => {
|
|
195
|
+
this.dispose();
|
|
196
|
+
await this.batcher.flushAll();
|
|
197
|
+
};
|
|
198
|
+
// public APIs for manipulating entities
|
|
199
|
+
/**
|
|
200
|
+
* Creates a new Entity with the given initial data.
|
|
201
|
+
*/
|
|
202
|
+
this.create = async (initial, oid) => {
|
|
203
|
+
this.ctx.log('debug', 'Creating new entity', oid);
|
|
204
|
+
const { collection } = decomposeOid(oid);
|
|
205
|
+
// remove any OID associations from the initial data
|
|
206
|
+
removeOidsFromAllSubObjects(initial);
|
|
207
|
+
// grab files and replace them with refs
|
|
208
|
+
const processed = processValueFiles(initial, this.files.add);
|
|
209
|
+
assignOid(processed, oid);
|
|
210
|
+
// creating a new Entity with no data, then preloading the operations
|
|
211
|
+
const entity = this.constructEntity(oid);
|
|
212
|
+
if (!entity) {
|
|
213
|
+
throw new Error(`Could not put new document: no schema exists for collection ${collection}`);
|
|
214
|
+
}
|
|
215
|
+
const operations = this.meta.patchCreator.createInitialize(processed, oid);
|
|
216
|
+
await this.batcher.commitOperations(operations, {
|
|
217
|
+
undoable: true,
|
|
218
|
+
source: entity,
|
|
219
|
+
});
|
|
220
|
+
// TODO: what happens if you create an entity with an OID that already
|
|
221
|
+
// exists?
|
|
222
|
+
// we still need to synchronously add the initial operations to the Entity
|
|
223
|
+
// even though they are flowing through the system
|
|
224
|
+
// TODO: this could be better aligned to avoid grouping here
|
|
225
|
+
const operationsGroupedByOid = groupPatchesByOid(operations);
|
|
226
|
+
this.events.add.invoke(this, {
|
|
227
|
+
operations: operationsGroupedByOid,
|
|
228
|
+
isLocal: true,
|
|
229
|
+
oid,
|
|
230
|
+
});
|
|
231
|
+
this.cache.set(oid, this.ctx.weakRef(entity));
|
|
232
|
+
return entity;
|
|
233
|
+
};
|
|
234
|
+
this.deleteAll = async (oids, options) => {
|
|
235
|
+
this.ctx.log('info', 'Deleting documents', oids);
|
|
236
|
+
assert(oids.every((oid) => oid === getOidRoot(oid)), 'Only root documents may be deleted via client methods');
|
|
237
|
+
const allOids = await Promise.all(oids.flatMap(async (oid) => {
|
|
238
|
+
var _a;
|
|
239
|
+
const entity = await this.hydrate(oid);
|
|
240
|
+
return (_a = entity === null || entity === void 0 ? void 0 : entity.__getFamilyOids__()) !== null && _a !== void 0 ? _a : [];
|
|
241
|
+
}));
|
|
242
|
+
// remove the entities from cache
|
|
243
|
+
oids.forEach((oid) => {
|
|
244
|
+
this.cache.delete(oid);
|
|
245
|
+
this.ctx.log('debug', 'Deleted document from cache', oid);
|
|
246
|
+
});
|
|
247
|
+
// create the delete patches and wait for them to be applied
|
|
248
|
+
const operations = this.meta.patchCreator.createDeleteAll(allOids.flat());
|
|
249
|
+
await this.batcher.commitOperations(operations, {
|
|
250
|
+
undoable: (options === null || options === void 0 ? void 0 : options.undoable) === undefined ? true : options.undoable,
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
this.delete = async (oid, options) => {
|
|
254
|
+
return this.deleteAll([oid], options);
|
|
255
|
+
};
|
|
256
|
+
this.getCollectionSchema = (collectionName) => {
|
|
257
|
+
const schema = this.ctx.schema.collections[collectionName];
|
|
258
|
+
if (!schema) {
|
|
259
|
+
this.ctx.log('warn', `Missing schema for collection: ${collectionName}`);
|
|
260
|
+
return {
|
|
261
|
+
schema: null,
|
|
262
|
+
readonlyKeys: [],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
// convert to object schema for compatibility
|
|
267
|
+
schema: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
nullable: false,
|
|
270
|
+
properties: schema.fields,
|
|
271
|
+
},
|
|
272
|
+
readonlyKeys: [schema.primaryKey],
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Constructs an entity from an OID, but does not load it.
|
|
277
|
+
*/
|
|
278
|
+
this.constructEntity = (oid) => {
|
|
279
|
+
const { collection } = decomposeOid(oid);
|
|
280
|
+
const { schema, readonlyKeys } = this.getCollectionSchema(collection);
|
|
281
|
+
if (!schema) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
if (this.disposed) {
|
|
285
|
+
throw new Error('Cannot hydrate entity after store has been disposed');
|
|
286
|
+
}
|
|
287
|
+
const metadataFamily = new EntityFamilyMetadata({
|
|
288
|
+
ctx: this.ctx,
|
|
289
|
+
onPendingOperations: this.onPendingOperations,
|
|
290
|
+
rootOid: oid,
|
|
291
|
+
});
|
|
292
|
+
// this is created synchronously so it's immediately available
|
|
293
|
+
// to begin capturing incoming data.
|
|
294
|
+
return new Entity({
|
|
295
|
+
ctx: this.ctx,
|
|
296
|
+
oid,
|
|
297
|
+
schema,
|
|
298
|
+
readonlyKeys,
|
|
299
|
+
files: this.files,
|
|
300
|
+
metadataFamily: metadataFamily,
|
|
301
|
+
patchCreator: this.meta.patchCreator,
|
|
302
|
+
events: this.events,
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
this.onPendingOperations = (operations) => {
|
|
306
|
+
this.batcher.addOperations(operations);
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Loads initial Entity data from storage
|
|
310
|
+
*/
|
|
311
|
+
this.loadEntity = async (entity, opts) => {
|
|
312
|
+
const { operations, baselines } = await this.meta.getDocumentData(entity.oid, opts);
|
|
313
|
+
if (!baselines.length && !Object.keys(operations).length) {
|
|
314
|
+
this.ctx.log('debug', 'No data found for entity', entity.oid);
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
this.ctx.log('debug', 'Loaded entity from storage', entity.oid);
|
|
318
|
+
this.events.replace.invoke(this, {
|
|
319
|
+
oid: entity.oid,
|
|
320
|
+
baselines,
|
|
321
|
+
operations,
|
|
322
|
+
isLocal: false,
|
|
323
|
+
});
|
|
324
|
+
// only set the cache after loading.
|
|
325
|
+
// TODO: is this cache/promise stuff redundant?
|
|
326
|
+
this.cache.set(entity.oid, this.ctx.weakRef(entity));
|
|
327
|
+
this.entityFinalizationRegistry.register(entity, entity.oid);
|
|
328
|
+
return entity;
|
|
329
|
+
};
|
|
330
|
+
this.ctx = ctx;
|
|
331
|
+
this.meta = meta;
|
|
332
|
+
this.files = files;
|
|
333
|
+
this.queryableStorage = new QueryableStorage({ ctx });
|
|
334
|
+
this.batcher = new OperationBatcher({
|
|
335
|
+
ctx,
|
|
336
|
+
meta,
|
|
337
|
+
entities: this,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
// expose batch APIs
|
|
341
|
+
get batch() {
|
|
342
|
+
return this.batcher.batch;
|
|
343
|
+
}
|
|
344
|
+
get flushAllBatches() {
|
|
345
|
+
return this.batcher.flushAll;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=EntityStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityStore.js","sourceRoot":"","sources":["../../../../src/entities/2/EntityStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Operation } from '@verdant-web/common';
|
|
2
|
+
import { Metadata } from '../../metadata/Metadata.js';
|
|
3
|
+
import { Context } from '../../context.js';
|
|
4
|
+
import type { EntityStore } from './EntityStore.js';
|
|
5
|
+
import { Entity } from './Entity.js';
|
|
6
|
+
export interface OperationBatch {
|
|
7
|
+
run: (fn: () => void) => this;
|
|
8
|
+
/** @deprecated - use commit() */
|
|
9
|
+
flush: () => Promise<void>;
|
|
10
|
+
commit: () => Promise<void>;
|
|
11
|
+
discard: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare class OperationBatcher {
|
|
14
|
+
private batcher;
|
|
15
|
+
private currentBatchKey;
|
|
16
|
+
private defaultBatchTimeout;
|
|
17
|
+
private meta;
|
|
18
|
+
private ctx;
|
|
19
|
+
private entities;
|
|
20
|
+
constructor({ batchTimeout, meta, ctx, entities, }: {
|
|
21
|
+
batchTimeout?: number;
|
|
22
|
+
meta: Metadata;
|
|
23
|
+
ctx: Context;
|
|
24
|
+
entities: EntityStore;
|
|
25
|
+
});
|
|
26
|
+
get isDefaultBatch(): boolean;
|
|
27
|
+
private flushOperations;
|
|
28
|
+
/**
|
|
29
|
+
* Immediately flushes operations to storage / sync.
|
|
30
|
+
* Providing source to second arg skips hydrating related
|
|
31
|
+
* Entity from storage, which is useful when that Entity
|
|
32
|
+
* isn't in storage (i.e. still creating) or just to speed
|
|
33
|
+
* up the commit.
|
|
34
|
+
*/
|
|
35
|
+
commitOperations: (operations: Operation[], meta: {
|
|
36
|
+
undoable?: boolean;
|
|
37
|
+
source?: Entity;
|
|
38
|
+
}) => Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Adds operations to the active batch.
|
|
41
|
+
*/
|
|
42
|
+
addOperations: (operations: Operation[]) => void;
|
|
43
|
+
batch: ({ undoable, batchName, max, timeout, }?: {
|
|
44
|
+
undoable?: boolean | undefined;
|
|
45
|
+
batchName?: string | undefined;
|
|
46
|
+
max?: number | null | undefined;
|
|
47
|
+
timeout?: number | null | undefined;
|
|
48
|
+
}) => OperationBatch;
|
|
49
|
+
flushAll: () => Promise<any[]>;
|
|
50
|
+
private createUndo;
|
|
51
|
+
private getInverseOperations;
|
|
52
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Batcher, generateId, getOidRoot, getUndoOperations, groupPatchesByOid, } from '@verdant-web/common';
|
|
2
|
+
const DEFAULT_BATCH_KEY = '@@default';
|
|
3
|
+
export class OperationBatcher {
|
|
4
|
+
constructor({ batchTimeout = 200, meta, ctx, entities, }) {
|
|
5
|
+
this.currentBatchKey = DEFAULT_BATCH_KEY;
|
|
6
|
+
this.flushOperations = async (operations, batchKey, meta) => {
|
|
7
|
+
this.ctx.log('debug', 'Flushing', operations.length, 'operations from batch', batchKey, 'to storage / sync');
|
|
8
|
+
if (!operations.length)
|
|
9
|
+
return;
|
|
10
|
+
// rewrite timestamps of all operations to now - this preserves
|
|
11
|
+
// the linear history of operations which are sent to the server.
|
|
12
|
+
// even if multiple batches are spun up in parallel and flushed
|
|
13
|
+
// after delay, the final operations in each one should reflect
|
|
14
|
+
// when the batch flushed, not when the changes were made.
|
|
15
|
+
// This also corresponds to user-observed behavior, since unconfirmed
|
|
16
|
+
// operations are applied universally after confirmed operations locally,
|
|
17
|
+
// so even operations which were made before a remote operation but
|
|
18
|
+
// have not been confirmed yet will appear to come after the remote one
|
|
19
|
+
// despite the provisional timestamp being earlier
|
|
20
|
+
// NOTE: this MUST be mutating the original operation object! this timestamp
|
|
21
|
+
// also serves as a unique ID for deduplication later.
|
|
22
|
+
for (const op of operations) {
|
|
23
|
+
op.timestamp = this.meta.now;
|
|
24
|
+
}
|
|
25
|
+
await this.commitOperations(operations, meta);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Immediately flushes operations to storage / sync.
|
|
29
|
+
* Providing source to second arg skips hydrating related
|
|
30
|
+
* Entity from storage, which is useful when that Entity
|
|
31
|
+
* isn't in storage (i.e. still creating) or just to speed
|
|
32
|
+
* up the commit.
|
|
33
|
+
*/
|
|
34
|
+
this.commitOperations = async (operations, meta) => {
|
|
35
|
+
if (!operations.length)
|
|
36
|
+
return;
|
|
37
|
+
// now is the time to decide on what the undo operations will
|
|
38
|
+
// look like, based on the confirmed view of the related entities.
|
|
39
|
+
if (meta.undoable) {
|
|
40
|
+
const undo = await this.createUndo({
|
|
41
|
+
ops: operations,
|
|
42
|
+
source: meta.source,
|
|
43
|
+
});
|
|
44
|
+
if (undo)
|
|
45
|
+
this.ctx.undoHistory.addUndo(undo);
|
|
46
|
+
}
|
|
47
|
+
// ship it out to EntityStore to compute final snapshots
|
|
48
|
+
// write to storage and refresh entities and queries
|
|
49
|
+
await this.entities.addData({
|
|
50
|
+
operations,
|
|
51
|
+
baselines: [],
|
|
52
|
+
isLocal: true,
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Adds operations to the active batch.
|
|
57
|
+
*/
|
|
58
|
+
this.addOperations = (operations) => {
|
|
59
|
+
if (!operations.length)
|
|
60
|
+
return;
|
|
61
|
+
this.batcher.add({
|
|
62
|
+
key: this.currentBatchKey,
|
|
63
|
+
items: operations,
|
|
64
|
+
});
|
|
65
|
+
this.ctx.log(`debug`, 'added', operations.length, 'ops to batch', this.currentBatchKey, ', size = ', this.batcher.getSize(this.currentBatchKey));
|
|
66
|
+
};
|
|
67
|
+
this.batch = ({ undoable = true, batchName = generateId(), max = null, timeout = this.defaultBatchTimeout, } = {}) => {
|
|
68
|
+
const internalBatch = this.batcher.add({
|
|
69
|
+
key: batchName,
|
|
70
|
+
max,
|
|
71
|
+
timeout,
|
|
72
|
+
items: [],
|
|
73
|
+
userData: { undoable },
|
|
74
|
+
});
|
|
75
|
+
const externalApi = {
|
|
76
|
+
run: (fn) => {
|
|
77
|
+
// while the provided function runs, operations are forwarded
|
|
78
|
+
// to the new batch instead of default. this relies on the function
|
|
79
|
+
// being synchronous.
|
|
80
|
+
this.currentBatchKey = batchName;
|
|
81
|
+
fn();
|
|
82
|
+
this.currentBatchKey = DEFAULT_BATCH_KEY;
|
|
83
|
+
return externalApi;
|
|
84
|
+
},
|
|
85
|
+
commit: async () => {
|
|
86
|
+
// before running a batch, the default operations must be flushed
|
|
87
|
+
// this better preserves undo history behavior...
|
|
88
|
+
// if we left the default batch open while flushing a named batch,
|
|
89
|
+
// then the default batch would be flushed after the named batch,
|
|
90
|
+
// and the default batch could contain operations both prior and
|
|
91
|
+
// after the named batch. this would result in a confusing undo
|
|
92
|
+
// history where the first undo might reverse changes before and
|
|
93
|
+
// after a set of other changes.
|
|
94
|
+
await this.batcher.flush(DEFAULT_BATCH_KEY);
|
|
95
|
+
return internalBatch.flush();
|
|
96
|
+
},
|
|
97
|
+
flush: () => externalApi.commit(),
|
|
98
|
+
discard: () => {
|
|
99
|
+
this.batcher.discard(batchName);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
return externalApi;
|
|
103
|
+
};
|
|
104
|
+
this.flushAll = () => Promise.all(this.batcher.flushAll());
|
|
105
|
+
this.createUndo = async (data) => {
|
|
106
|
+
// this can't be done on-demand because we rely on the current
|
|
107
|
+
// state of the entities to calculate the inverse operations.
|
|
108
|
+
const inverseOps = await this.getInverseOperations(data);
|
|
109
|
+
if (!inverseOps.length)
|
|
110
|
+
return null;
|
|
111
|
+
return async () => {
|
|
112
|
+
const redo = await this.createUndo({
|
|
113
|
+
ops: inverseOps,
|
|
114
|
+
source: data.source,
|
|
115
|
+
});
|
|
116
|
+
// set time to now for all undo operations, they're happening now.
|
|
117
|
+
for (const op of inverseOps) {
|
|
118
|
+
op.timestamp = this.meta.now;
|
|
119
|
+
}
|
|
120
|
+
await this.commitOperations(inverseOps,
|
|
121
|
+
// undos should not generate their own undo operations
|
|
122
|
+
// since they already calculate redo as the inverse.
|
|
123
|
+
{ undoable: false });
|
|
124
|
+
return redo;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
this.getInverseOperations = async ({ ops, source, }) => {
|
|
128
|
+
const grouped = groupPatchesByOid(ops);
|
|
129
|
+
const inverseOps = [];
|
|
130
|
+
const getNow = () => this.meta.now;
|
|
131
|
+
await Promise.all(Object.entries(grouped).map(async ([oid, patches]) => {
|
|
132
|
+
const entity = source !== null && source !== void 0 ? source : (await this.entities.hydrate(getOidRoot(oid)));
|
|
133
|
+
// TODO: this is getting the rebased baseline? how? are ops being submitted early?
|
|
134
|
+
const viewData = entity === null || entity === void 0 ? void 0 : entity.__getViewData__(oid, 'confirmed');
|
|
135
|
+
if (!viewData) {
|
|
136
|
+
this.ctx.log('warn', 'could not find entity', oid, 'for undo operation', ops);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const inverse = getUndoOperations(oid, viewData.view, patches, getNow);
|
|
140
|
+
inverseOps.unshift(...inverse);
|
|
141
|
+
}));
|
|
142
|
+
return inverseOps;
|
|
143
|
+
};
|
|
144
|
+
this.meta = meta;
|
|
145
|
+
this.ctx = ctx;
|
|
146
|
+
this.entities = entities;
|
|
147
|
+
this.defaultBatchTimeout = batchTimeout;
|
|
148
|
+
this.batcher = new Batcher(this.flushOperations);
|
|
149
|
+
this.batcher.add({
|
|
150
|
+
key: DEFAULT_BATCH_KEY,
|
|
151
|
+
items: [],
|
|
152
|
+
max: 100,
|
|
153
|
+
timeout: batchTimeout,
|
|
154
|
+
userData: { undoable: true },
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
get isDefaultBatch() {
|
|
158
|
+
return this.currentBatchKey === DEFAULT_BATCH_KEY;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=OperationBatcher.js.map
|