j-templates 8.1.1 → 8.2.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/SYNTAX_PRIMER.md +3 -4
- package/Store/Diff/diffTree.d.ts +65 -34
- package/Store/Diff/diffTree.js +528 -407
- package/Store/Diff/diffTree.types.d.ts +5 -79
- package/Store/Diff/diffTree.types.js +1 -3
- package/Store/Diff/diffTreeWorker.d.ts +4 -0
- package/Store/Diff/diffTreeWorker.js +17 -39
- package/Store/Diff/diffTreeWriter.d.ts +27 -38
- package/Store/Diff/diffTreeWriter.js +44 -95
- package/Store/Diff/index.d.ts +1 -0
- package/Store/Diff/index.js +1 -0
- package/Store/Diff/utils/diffTree.types.d.ts +108 -0
- package/Store/Diff/utils/diffTree.types.js +1 -0
- package/Store/Diff/utils/diffTree.utils.d.ts +79 -0
- package/Store/Diff/utils/diffTree.utils.js +398 -0
- package/Store/Store/pushStore.d.ts +1 -5
- package/Store/Store/pushStore.js +3 -22
- package/Store/Store/store.d.ts +2 -4
- package/Store/Store/store.js +12 -11
- package/Store/Store/storeAsync.d.ts +4 -5
- package/Store/Store/storeAsync.js +12 -30
- package/Store/Store/storeSync.d.ts +14 -9
- package/Store/Store/storeSync.js +24 -19
- package/Store/Tree/observableNode.d.ts +2 -23
- package/Store/Tree/observableNode.js +41 -181
- package/Store/Tree/snapshot.d.ts +70 -0
- package/Store/Tree/snapshot.js +141 -0
- package/Utils/json.d.ts +37 -7
- package/Utils/json.js +151 -13
- package/package.json +1 -1
- package/Store/Diff/diffSync.d.ts +0 -36
- package/Store/Diff/diffSync.js +0 -41
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JsonDiffResult } from "../../Utils/json";
|
|
2
2
|
/**
|
|
3
|
-
* `type` of the message a `DiffTreeWriter` posts on `Commit()`: the
|
|
3
|
+
* `type` of the message a `DiffTreeWriter` posts on `Commit()`: the diff since the last commit,
|
|
4
4
|
* which a `PushStore` applies.
|
|
5
5
|
*/
|
|
6
6
|
export declare const DIFF_TREE_EVENT = "DIFF_TREE_EVENT";
|
|
@@ -9,82 +9,6 @@ export declare const DIFF_TREE_EVENT = "DIFF_TREE_EVENT";
|
|
|
9
9
|
* failed, after the `DIFF_TREE_EVENT` carrying the changes that were applied.
|
|
10
10
|
*/
|
|
11
11
|
export declare const DIFF_TREE_ERROR = "DIFF_TREE_ERROR";
|
|
12
|
-
/**
|
|
13
|
-
* Interface for diff tree operations.
|
|
14
|
-
*/
|
|
15
|
-
export interface IDiffTree {
|
|
16
|
-
/**
|
|
17
|
-
* Computes the diff between a new value and the current value at a path.
|
|
18
|
-
* @param path - Dot-separated path to the value
|
|
19
|
-
* @param value - The new value to compare
|
|
20
|
-
* @returns Diff results showing changes
|
|
21
|
-
*/
|
|
22
|
-
DiffPath(path: string, value: any): JsonDiffResult;
|
|
23
|
-
PatchPath(path: string, patch: any): JsonDiffResult;
|
|
24
|
-
SplicePath(path: string, start: number, deleteCount: number | undefined, items: any[]): DiffSpliceResult;
|
|
25
|
-
PushPath(path: string, items: any[]): JsonDiffResult;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Constructor interface for IDiffTree.
|
|
29
|
-
*/
|
|
30
|
-
export interface IDiffTreeConstructor {
|
|
31
|
-
/**
|
|
32
|
-
* Creates a new IDiffTree instance.
|
|
33
|
-
* @param keyFunc - Optional function to extract a key from a value
|
|
34
|
-
* @param projections - Optional map of derived-value projections, keyed by id
|
|
35
|
-
*/
|
|
36
|
-
new (keyFunc?: {
|
|
37
|
-
(val: any): string;
|
|
38
|
-
}, projections?: DiffTreeProjectionMap): IDiffTree;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* A pure function computing a derived value from the current values at `reads`. Called with
|
|
42
|
-
* the resolved read paths (`keys`) first, then each value positionally in the same order as
|
|
43
|
-
* `reads`, so each value parameter can be typed at the call site instead of cast out of a
|
|
44
|
-
* generic read. A trailing wildcard read expands to one key/value pair per matched root key,
|
|
45
|
-
* so `keys` lets the function tell which root each trailing value came from.
|
|
46
|
-
*/
|
|
47
|
-
export type DiffTreeProjectionFunc = (keys: string[], ...values: any[]) => any;
|
|
48
|
-
/**
|
|
49
|
-
* A projection's declaration: the dot-separated paths it reads (a plain tree path, an
|
|
50
|
-
* entity's key, or another projection's `$projection_<id>` key for chaining) and the
|
|
51
|
-
* function computing its result from their current values, positionally. Declaring `reads`
|
|
52
|
-
* up front lets the tree topologically sort projections and reject a dependency cycle at
|
|
53
|
-
* construction instead of on first write.
|
|
54
|
-
*
|
|
55
|
-
* The final entry in `reads` may instead be a wildcard root key (e.g. `"user_*"`, no dot
|
|
56
|
-
* nesting) matching every root key with that prefix; each match is read and passed to the
|
|
57
|
-
* projection as its own trailing key/value pair. A wildcard read can't be chained off a
|
|
58
|
-
* projection's `$projection_<id>` key and can't appear anywhere but last.
|
|
59
|
-
*/
|
|
60
|
-
export type DiffTreeProjectionDefinition = {
|
|
61
|
-
reads: string[];
|
|
62
|
-
projection: DiffTreeProjectionFunc;
|
|
63
|
-
};
|
|
64
|
-
/** A map of projection id to its declaration, keyed by the id used in `$projection_<id>`. */
|
|
65
|
-
export type DiffTreeProjectionMap = {
|
|
66
|
-
[id: string]: DiffTreeProjectionDefinition;
|
|
67
|
-
};
|
|
68
|
-
/** Reserved key prefix a projection's result is stored under: `$projection_<id>`. */
|
|
69
|
-
export declare const PROJECTION_PREFIX = "$projection_";
|
|
70
|
-
export type DiffSpliceResult<T = unknown> = {
|
|
71
|
-
/** Array path segments (strings for object keys, numbers for array indices) */
|
|
72
|
-
path: (string | number)[];
|
|
73
|
-
/** Required by the splice operation **/
|
|
74
|
-
start: number;
|
|
75
|
-
deleteCount: number;
|
|
76
|
-
items: T[];
|
|
77
|
-
spliceResult: T[];
|
|
78
|
-
diffResult: JsonDiffResult;
|
|
79
|
-
};
|
|
80
|
-
export type IDiffMethodBatch = IDiffMethod[];
|
|
81
|
-
export type IDiffMethodBatchResult = ({
|
|
82
|
-
type: "diff";
|
|
83
|
-
result: JsonDiffResult;
|
|
84
|
-
} | {
|
|
85
|
-
type: "splice";
|
|
86
|
-
result: DiffSpliceResult;
|
|
87
|
-
})[];
|
|
88
12
|
/**
|
|
89
13
|
* A message a `DiffTreeWriter` posts to its `PushStore`. Both arrive as ordinary
|
|
90
14
|
* `"message"` events, so the channel can carry other traffic too; listeners tell them apart
|
|
@@ -92,14 +16,16 @@ export type IDiffMethodBatchResult = ({
|
|
|
92
16
|
*/
|
|
93
17
|
export type IDiffPushEvent = {
|
|
94
18
|
type: typeof DIFF_TREE_EVENT;
|
|
95
|
-
data:
|
|
19
|
+
data: JsonDiffResult;
|
|
96
20
|
} | {
|
|
97
21
|
type: typeof DIFF_TREE_ERROR;
|
|
98
22
|
data: any;
|
|
99
23
|
};
|
|
24
|
+
/** A `DiffTree` method call sent to a worker connected with `ConnectWorkerToDiffTree` */
|
|
100
25
|
export interface IDiffMethod {
|
|
101
26
|
/** The method to call */
|
|
102
|
-
method: "
|
|
27
|
+
method: "writepath" | "patchpath" | "pushpath" | "splicepath";
|
|
103
28
|
/** Arguments for the method call */
|
|
104
29
|
arguments: Array<any>;
|
|
105
30
|
}
|
|
31
|
+
export type IDiffMethodBatch = IDiffMethod[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `type` of the message a `DiffTreeWriter` posts on `Commit()`: the
|
|
2
|
+
* `type` of the message a `DiffTreeWriter` posts on `Commit()`: the diff since the last commit,
|
|
3
3
|
* which a `PushStore` applies.
|
|
4
4
|
*/
|
|
5
5
|
export const DIFF_TREE_EVENT = "DIFF_TREE_EVENT";
|
|
@@ -8,5 +8,3 @@ export const DIFF_TREE_EVENT = "DIFF_TREE_EVENT";
|
|
|
8
8
|
* failed, after the `DIFF_TREE_EVENT` carrying the changes that were applied.
|
|
9
9
|
*/
|
|
10
10
|
export const DIFF_TREE_ERROR = "DIFF_TREE_ERROR";
|
|
11
|
-
/** Reserved key prefix a projection's result is stored under: `$projection_<id>`. */
|
|
12
|
-
export const PROJECTION_PREFIX = "$projection_";
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { DiffTree } from "./diffTree";
|
|
2
|
+
/**
|
|
3
|
+
* Runs each batch of method calls a `StoreAsync` sends on the tree, then its projections, and
|
|
4
|
+
* posts back the tree's `Commit()` diff for the store to apply.
|
|
5
|
+
*/
|
|
2
6
|
export declare function ConnectWorkerToDiffTree(diffTree: DiffTree, worker: Worker): void;
|
|
@@ -1,44 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs each batch of method calls a `StoreAsync` sends on the tree, then its projections, and
|
|
3
|
+
* posts back the tree's `Commit()` diff for the store to apply.
|
|
4
|
+
*/
|
|
1
5
|
export function ConnectWorkerToDiffTree(diffTree, worker) {
|
|
2
6
|
worker.onmessage = function (event) {
|
|
3
7
|
const data = event.data;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
case "diffpath": {
|
|
9
|
-
result[x] = {
|
|
10
|
-
type: "diff",
|
|
11
|
-
result: diffTree.DiffPath(call.arguments[0], call.arguments[1])
|
|
12
|
-
};
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
case "splicepath": {
|
|
16
|
-
result[x] = {
|
|
17
|
-
type: "splice",
|
|
18
|
-
result: diffTree.SplicePath(call.arguments[0], call.arguments[1], call.arguments[2], call.arguments[3])
|
|
19
|
-
};
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
case "pushpath": {
|
|
23
|
-
result[x] = {
|
|
24
|
-
type: "diff",
|
|
25
|
-
result: diffTree.PushPath(call.arguments[0], call.arguments[1])
|
|
26
|
-
};
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
case "patchpath": {
|
|
30
|
-
result[x] = {
|
|
31
|
-
type: "diff",
|
|
32
|
-
result: diffTree.PatchPath(call.arguments[0], call.arguments[1])
|
|
33
|
-
};
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
result[result.length - 1] = {
|
|
39
|
-
type: "diff",
|
|
40
|
-
result: diffTree.RunProjections()
|
|
41
|
-
};
|
|
42
|
-
worker.postMessage(result);
|
|
8
|
+
for (let x = 0; x < data.length; x++)
|
|
9
|
+
RunMethod(diffTree, data[x]);
|
|
10
|
+
diffTree.RunProjections();
|
|
11
|
+
worker.postMessage(diffTree.Commit());
|
|
43
12
|
};
|
|
44
13
|
}
|
|
14
|
+
function RunMethod(diffTree, call) {
|
|
15
|
+
const args = call.arguments;
|
|
16
|
+
switch (call.method) {
|
|
17
|
+
case "writepath": return diffTree.WritePath(args[0], args[1]);
|
|
18
|
+
case "splicepath": return diffTree.SplicePath(args[0], args[1], args[2], args[3]);
|
|
19
|
+
case "pushpath": return diffTree.PushPath(args[0], args[1]);
|
|
20
|
+
case "patchpath": return diffTree.PatchPath(args[0], args[1]);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { JsonDiffResult } from "../../Utils/json";
|
|
2
2
|
import { DiffTree } from "./diffTree";
|
|
3
|
-
import {
|
|
3
|
+
import { IKeyFunc, IRouteDefinitions } from "./utils/diffTree.types";
|
|
4
4
|
/**
|
|
5
5
|
* A `DiffTree` that pushes its changes to a `PushStore` instead of answering a store's
|
|
6
6
|
* requests. Use it inside a worker that produces data itself (for example, the result of a
|
|
7
7
|
* computation it runs): write with the usual `DiffTree` methods, then call `Commit()` to run
|
|
8
8
|
* projections and post everything since the last commit as one message.
|
|
9
9
|
*
|
|
10
|
-
* - Push only: don't pass a `DiffTreeWriter` to `ConnectWorkerToDiffTree
|
|
11
|
-
*
|
|
10
|
+
* - Push only: don't pass a `DiffTreeWriter` to `ConnectWorkerToDiffTree`, which commits
|
|
11
|
+
* every batch itself and would post it to the wrong place.
|
|
12
12
|
* - After a change fails, every later change before `Commit()` throws that same error. The
|
|
13
13
|
* changes made before the failure are already in the tree, so `Commit()` still sends them,
|
|
14
14
|
* followed by a `DIFF_TREE_ERROR` message. Projections aren't run for that batch; the next
|
|
@@ -18,8 +18,8 @@ import { DiffSpliceResult, DiffTreeProjectionMap } from "./diffTree.types";
|
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
* // engine-worker.ts
|
|
21
|
-
* const tree = new DiffTreeWriter(self, undefined, projections);
|
|
22
|
-
* tree.
|
|
21
|
+
* const tree = new DiffTreeWriter(self, undefined, { "/": { projections } });
|
|
22
|
+
* tree.WritePath("results", computeResults());
|
|
23
23
|
* tree.Commit();
|
|
24
24
|
*
|
|
25
25
|
* // main thread
|
|
@@ -30,64 +30,51 @@ import { DiffSpliceResult, DiffTreeProjectionMap } from "./diffTree.types";
|
|
|
30
30
|
export declare class DiffTreeWriter extends DiffTree {
|
|
31
31
|
private worker;
|
|
32
32
|
private uncommittedError;
|
|
33
|
-
private uncommitted;
|
|
34
33
|
/**
|
|
35
34
|
* Creates a DiffTreeWriter.
|
|
36
35
|
* @param worker Where `Commit()` posts its messages: the worker's `self`, or a `MessagePort`.
|
|
37
36
|
* On the same thread as the store, anything whose `postMessage` dispatches a `"message"`
|
|
38
|
-
* event on the store's `EventTarget
|
|
37
|
+
* event on the store's `EventTarget`. The store clones every value it applies, so it never
|
|
38
|
+
* shares objects with the writer's tree.
|
|
39
39
|
* @param keyFunc Optional function to extract a key from objects. Pass the same key logic to
|
|
40
40
|
* the `PushStore` that receives the changes.
|
|
41
|
-
* @param
|
|
42
|
-
*
|
|
41
|
+
* @param routes Optional route definitions. Projections run once per `Commit()`, after the
|
|
42
|
+
* batch's changes.
|
|
43
43
|
*/
|
|
44
44
|
constructor(worker: {
|
|
45
45
|
postMessage(message: unknown): void;
|
|
46
|
-
}, keyFunc?:
|
|
47
|
-
(val: any): string;
|
|
48
|
-
}, projections?: DiffTreeProjectionMap);
|
|
46
|
+
}, keyFunc?: IKeyFunc, routes?: IRouteDefinitions);
|
|
49
47
|
/**
|
|
50
48
|
* Not available on a DiffTreeWriter: `Commit()` runs projections once per batch, after its
|
|
51
49
|
* changes. Running them earlier would push a projection result computed from part of a batch.
|
|
52
50
|
* @throws Always.
|
|
53
51
|
*/
|
|
54
|
-
RunProjections():
|
|
52
|
+
RunProjections(): void;
|
|
55
53
|
/**
|
|
56
|
-
*
|
|
57
|
-
* for the next `Commit()`.
|
|
58
|
-
* @param path - Dot-separated path to the value
|
|
59
|
-
* @param value - The new value to compare
|
|
60
|
-
* @returns Diff results showing changes
|
|
54
|
+
* Writes a value at a path.
|
|
61
55
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
62
56
|
*/
|
|
63
|
-
|
|
57
|
+
WritePath(path: string, value: any): void;
|
|
64
58
|
/**
|
|
65
|
-
* Merges a patch into the current value at a path
|
|
66
|
-
* `Commit()`.
|
|
67
|
-
* @param path - Dot-separated path to the value
|
|
68
|
-
* @param patch - The value to merge into the current value
|
|
69
|
-
* @returns Diff results showing changes
|
|
59
|
+
* Merges a patch into the current value at a path.
|
|
70
60
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
71
61
|
*/
|
|
72
|
-
PatchPath(path: string, patch: any):
|
|
62
|
+
PatchPath(path: string, patch: any): void;
|
|
73
63
|
/**
|
|
74
|
-
* Appends items to the array at a path
|
|
75
|
-
* @param path - Dot-separated path to the array
|
|
76
|
-
* @param items - The items to append
|
|
77
|
-
* @returns Diff results showing changes
|
|
64
|
+
* Appends items to the array at a path.
|
|
78
65
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
79
66
|
*/
|
|
80
|
-
PushPath(path: string, items: unknown[]):
|
|
67
|
+
PushPath(path: string, items: unknown[]): void;
|
|
81
68
|
/**
|
|
82
|
-
* Splices the array at a path
|
|
83
|
-
* @param path - Dot-separated path to the array
|
|
84
|
-
* @param start - The position at which to start changing the array
|
|
85
|
-
* @param deleteCount - The number of elements to delete
|
|
86
|
-
* @param items - The elements to insert
|
|
87
|
-
* @returns The splice and its diff results
|
|
69
|
+
* Splices the array at a path.
|
|
88
70
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
89
71
|
*/
|
|
90
|
-
SplicePath(path: string, start: number, deleteCount: number, items: any[]):
|
|
72
|
+
SplicePath(path: string, start: number, deleteCount: number, items: any[]): void;
|
|
73
|
+
/**
|
|
74
|
+
* Deletes a route, its child routes and its route instances.
|
|
75
|
+
* @throws The change's error, or an earlier uncommitted change's error.
|
|
76
|
+
*/
|
|
77
|
+
DeleteRoute(routePath: string): void;
|
|
91
78
|
/**
|
|
92
79
|
* Runs projections and posts every change since the last commit as one `DIFF_TREE_EVENT`
|
|
93
80
|
* message, which a `PushStore` applies as a unit. Posts nothing when nothing changed.
|
|
@@ -97,7 +84,9 @@ export declare class DiffTreeWriter extends DiffTree {
|
|
|
97
84
|
* throw, since the failed change already did. If a projection throws, the batch's changes
|
|
98
85
|
* and the error are posted the same way, then `Commit()` rethrows. Either way the next batch
|
|
99
86
|
* starts clean.
|
|
87
|
+
* @returns The diff that was posted.
|
|
100
88
|
* @throws A projection's error.
|
|
101
89
|
*/
|
|
102
|
-
Commit():
|
|
90
|
+
Commit(): JsonDiffResult;
|
|
91
|
+
private Guard;
|
|
103
92
|
}
|
|
@@ -6,8 +6,8 @@ import { DIFF_TREE_ERROR, DIFF_TREE_EVENT } from "./diffTree.types";
|
|
|
6
6
|
* computation it runs): write with the usual `DiffTree` methods, then call `Commit()` to run
|
|
7
7
|
* projections and post everything since the last commit as one message.
|
|
8
8
|
*
|
|
9
|
-
* - Push only: don't pass a `DiffTreeWriter` to `ConnectWorkerToDiffTree
|
|
10
|
-
*
|
|
9
|
+
* - Push only: don't pass a `DiffTreeWriter` to `ConnectWorkerToDiffTree`, which commits
|
|
10
|
+
* every batch itself and would post it to the wrong place.
|
|
11
11
|
* - After a change fails, every later change before `Commit()` throws that same error. The
|
|
12
12
|
* changes made before the failure are already in the tree, so `Commit()` still sends them,
|
|
13
13
|
* followed by a `DIFF_TREE_ERROR` message. Projections aren't run for that batch; the next
|
|
@@ -17,8 +17,8 @@ import { DIFF_TREE_ERROR, DIFF_TREE_EVENT } from "./diffTree.types";
|
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* // engine-worker.ts
|
|
20
|
-
* const tree = new DiffTreeWriter(self, undefined, projections);
|
|
21
|
-
* tree.
|
|
20
|
+
* const tree = new DiffTreeWriter(self, undefined, { "/": { projections } });
|
|
21
|
+
* tree.WritePath("results", computeResults());
|
|
22
22
|
* tree.Commit();
|
|
23
23
|
*
|
|
24
24
|
* // main thread
|
|
@@ -31,17 +31,17 @@ export class DiffTreeWriter extends DiffTree {
|
|
|
31
31
|
* Creates a DiffTreeWriter.
|
|
32
32
|
* @param worker Where `Commit()` posts its messages: the worker's `self`, or a `MessagePort`.
|
|
33
33
|
* On the same thread as the store, anything whose `postMessage` dispatches a `"message"`
|
|
34
|
-
* event on the store's `EventTarget
|
|
34
|
+
* event on the store's `EventTarget`. The store clones every value it applies, so it never
|
|
35
|
+
* shares objects with the writer's tree.
|
|
35
36
|
* @param keyFunc Optional function to extract a key from objects. Pass the same key logic to
|
|
36
37
|
* the `PushStore` that receives the changes.
|
|
37
|
-
* @param
|
|
38
|
-
*
|
|
38
|
+
* @param routes Optional route definitions. Projections run once per `Commit()`, after the
|
|
39
|
+
* batch's changes.
|
|
39
40
|
*/
|
|
40
|
-
constructor(worker, keyFunc,
|
|
41
|
-
super(keyFunc,
|
|
41
|
+
constructor(worker, keyFunc, routes) {
|
|
42
|
+
super(keyFunc, routes);
|
|
42
43
|
this.worker = worker;
|
|
43
44
|
this.uncommittedError = null;
|
|
44
|
-
this.uncommitted = [];
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Not available on a DiffTreeWriter: `Commit()` runs projections once per batch, after its
|
|
@@ -52,100 +52,39 @@ export class DiffTreeWriter extends DiffTree {
|
|
|
52
52
|
throw new Error("RunProjections is called during Commit()");
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
* for the next `Commit()`.
|
|
57
|
-
* @param path - Dot-separated path to the value
|
|
58
|
-
* @param value - The new value to compare
|
|
59
|
-
* @returns Diff results showing changes
|
|
55
|
+
* Writes a value at a path.
|
|
60
56
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
61
57
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
throw this.uncommittedError;
|
|
65
|
-
try {
|
|
66
|
-
const result = super.DiffPath(path, value);
|
|
67
|
-
result.length > 0 && this.uncommitted.push({
|
|
68
|
-
type: "diff",
|
|
69
|
-
result
|
|
70
|
-
});
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
this.uncommittedError = err;
|
|
75
|
-
throw err;
|
|
76
|
-
}
|
|
58
|
+
WritePath(path, value) {
|
|
59
|
+
this.Guard(() => super.WritePath(path, value));
|
|
77
60
|
}
|
|
78
61
|
/**
|
|
79
|
-
* Merges a patch into the current value at a path
|
|
80
|
-
* `Commit()`.
|
|
81
|
-
* @param path - Dot-separated path to the value
|
|
82
|
-
* @param patch - The value to merge into the current value
|
|
83
|
-
* @returns Diff results showing changes
|
|
62
|
+
* Merges a patch into the current value at a path.
|
|
84
63
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
85
64
|
*/
|
|
86
65
|
PatchPath(path, patch) {
|
|
87
|
-
|
|
88
|
-
throw this.uncommittedError;
|
|
89
|
-
try {
|
|
90
|
-
const result = super.PatchPath(path, patch);
|
|
91
|
-
result.length > 0 && this.uncommitted.push({
|
|
92
|
-
type: "diff",
|
|
93
|
-
result
|
|
94
|
-
});
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
this.uncommittedError = err;
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
66
|
+
this.Guard(() => super.PatchPath(path, patch));
|
|
101
67
|
}
|
|
102
68
|
/**
|
|
103
|
-
* Appends items to the array at a path
|
|
104
|
-
* @param path - Dot-separated path to the array
|
|
105
|
-
* @param items - The items to append
|
|
106
|
-
* @returns Diff results showing changes
|
|
69
|
+
* Appends items to the array at a path.
|
|
107
70
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
108
71
|
*/
|
|
109
72
|
PushPath(path, items) {
|
|
110
|
-
|
|
111
|
-
throw this.uncommittedError;
|
|
112
|
-
try {
|
|
113
|
-
const result = super.PushPath(path, items);
|
|
114
|
-
result.length > 0 && this.uncommitted.push({
|
|
115
|
-
type: "diff",
|
|
116
|
-
result
|
|
117
|
-
});
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
this.uncommittedError = err;
|
|
122
|
-
throw err;
|
|
123
|
-
}
|
|
73
|
+
this.Guard(() => super.PushPath(path, items));
|
|
124
74
|
}
|
|
125
75
|
/**
|
|
126
|
-
* Splices the array at a path
|
|
127
|
-
* @param path - Dot-separated path to the array
|
|
128
|
-
* @param start - The position at which to start changing the array
|
|
129
|
-
* @param deleteCount - The number of elements to delete
|
|
130
|
-
* @param items - The elements to insert
|
|
131
|
-
* @returns The splice and its diff results
|
|
76
|
+
* Splices the array at a path.
|
|
132
77
|
* @throws The change's error, or an earlier uncommitted change's error.
|
|
133
78
|
*/
|
|
134
79
|
SplicePath(path, start, deleteCount, items) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
this.uncommittedError = err;
|
|
147
|
-
throw err;
|
|
148
|
-
}
|
|
80
|
+
this.Guard(() => super.SplicePath(path, start, deleteCount, items));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Deletes a route, its child routes and its route instances.
|
|
84
|
+
* @throws The change's error, or an earlier uncommitted change's error.
|
|
85
|
+
*/
|
|
86
|
+
DeleteRoute(routePath) {
|
|
87
|
+
this.Guard(() => super.DeleteRoute(routePath));
|
|
149
88
|
}
|
|
150
89
|
/**
|
|
151
90
|
* Runs projections and posts every change since the last commit as one `DIFF_TREE_EVENT`
|
|
@@ -156,24 +95,23 @@ export class DiffTreeWriter extends DiffTree {
|
|
|
156
95
|
* throw, since the failed change already did. If a projection throws, the batch's changes
|
|
157
96
|
* and the error are posted the same way, then `Commit()` rethrows. Either way the next batch
|
|
158
97
|
* starts clean.
|
|
98
|
+
* @returns The diff that was posted.
|
|
159
99
|
* @throws A projection's error.
|
|
160
100
|
*/
|
|
161
101
|
Commit() {
|
|
102
|
+
let diff = [];
|
|
162
103
|
try {
|
|
163
|
-
|
|
164
|
-
result.length > 0 && this.uncommitted.push({
|
|
165
|
-
type: "diff",
|
|
166
|
-
result
|
|
167
|
-
});
|
|
104
|
+
!this.uncommittedError && super.RunProjections();
|
|
168
105
|
}
|
|
169
106
|
catch (err) {
|
|
170
107
|
this.uncommittedError = err;
|
|
171
108
|
throw err;
|
|
172
109
|
}
|
|
173
110
|
finally {
|
|
174
|
-
|
|
111
|
+
diff = super.Commit();
|
|
112
|
+
diff.length > 0 && this.worker.postMessage({
|
|
175
113
|
type: DIFF_TREE_EVENT,
|
|
176
|
-
data:
|
|
114
|
+
data: diff
|
|
177
115
|
});
|
|
178
116
|
if (this.uncommittedError) {
|
|
179
117
|
this.worker.postMessage({
|
|
@@ -181,8 +119,19 @@ export class DiffTreeWriter extends DiffTree {
|
|
|
181
119
|
data: this.uncommittedError
|
|
182
120
|
});
|
|
183
121
|
}
|
|
184
|
-
this.uncommitted = [];
|
|
185
122
|
this.uncommittedError = null;
|
|
186
123
|
}
|
|
124
|
+
return diff;
|
|
125
|
+
}
|
|
126
|
+
Guard(change) {
|
|
127
|
+
if (this.uncommittedError)
|
|
128
|
+
throw this.uncommittedError;
|
|
129
|
+
try {
|
|
130
|
+
change();
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
this.uncommittedError = err;
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
187
136
|
}
|
|
188
137
|
}
|
package/Store/Diff/index.d.ts
CHANGED
package/Store/Diff/index.js
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/** A projection of the values at one or more paths, rerun when any of them changes */
|
|
2
|
+
export type IReadsProjectionDefinition = {
|
|
3
|
+
/**
|
|
4
|
+
* Paths relative to the route, or from the root when starting with `/`. One route segment may
|
|
5
|
+
* end in `*` to read every matching route, e.g. `child/row_*` + `/name`, which passes an object
|
|
6
|
+
* keyed by matched segment.
|
|
7
|
+
*/
|
|
8
|
+
reads: string[];
|
|
9
|
+
projection: {
|
|
10
|
+
(...reads: any): any;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A projection of each entry of one keyed record, by key. Its output is a record with the same
|
|
15
|
+
* keys: an entry whose value is the same as on the last run keeps its output without a call, and
|
|
16
|
+
* an entry the projection returns undefined for is left out.
|
|
17
|
+
*/
|
|
18
|
+
export type IKeyedProjectionDefinition = {
|
|
19
|
+
reads?: never;
|
|
20
|
+
/**
|
|
21
|
+
* The record, relative to the route or from the root when starting with `/`; no wildcard. When
|
|
22
|
+
* left out, the route's own state, without its projections' outputs. A missing record reads as
|
|
23
|
+
* an empty one.
|
|
24
|
+
*/
|
|
25
|
+
read?: string;
|
|
26
|
+
projection: {
|
|
27
|
+
(entry: any, key: string): any;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type IProjectionDefinition = IReadsProjectionDefinition | IKeyedProjectionDefinition;
|
|
31
|
+
export type IProjectionDefinitions = {
|
|
32
|
+
[key: string]: IProjectionDefinition;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A keyed projection's state: each key's value and output from its last run, kept for a key
|
|
36
|
+
* left out of the output too, so it isn't called again while its value is the same.
|
|
37
|
+
*/
|
|
38
|
+
export type IKeyedState = {
|
|
39
|
+
entries: Map<string, {
|
|
40
|
+
value: any;
|
|
41
|
+
output: any;
|
|
42
|
+
}>;
|
|
43
|
+
/** Whether it reads its route's own state, which leaves out the route's projection outputs */
|
|
44
|
+
routeState: boolean;
|
|
45
|
+
};
|
|
46
|
+
/** A route a read resolved through on its last full read: its path, raw state and the snapshot read from it */
|
|
47
|
+
export type IRouteSource = {
|
|
48
|
+
route: string;
|
|
49
|
+
state: object;
|
|
50
|
+
snapshot: any;
|
|
51
|
+
};
|
|
52
|
+
/** A route a wildcard read matched: the segment, the path read in it, and what was read */
|
|
53
|
+
export type IWildcardMatch = IRouteSource & {
|
|
54
|
+
segment: string;
|
|
55
|
+
path: string;
|
|
56
|
+
value: any;
|
|
57
|
+
found: boolean;
|
|
58
|
+
};
|
|
59
|
+
/** A wildcard read's matches, and the tree's route version, which changes when a route instance may have been added or removed */
|
|
60
|
+
export type IWildcardSource = {
|
|
61
|
+
version: number;
|
|
62
|
+
matches: IWildcardMatch[];
|
|
63
|
+
};
|
|
64
|
+
/** What a read resolved through on its last full read */
|
|
65
|
+
export type IReadSource = IRouteSource | IWildcardSource;
|
|
66
|
+
export type IProjection = {
|
|
67
|
+
key: string;
|
|
68
|
+
reads: string[];
|
|
69
|
+
projection: {
|
|
70
|
+
(...reads: any): any;
|
|
71
|
+
};
|
|
72
|
+
/** Where the output is written: `$projection_<key>` in the route, e.g. `/channel/1/$projection_count` */
|
|
73
|
+
output: string;
|
|
74
|
+
/** Whether a read covers the projection's own output, e.g. a read of the route's root object */
|
|
75
|
+
readsOutput: boolean;
|
|
76
|
+
/** The read values from the last run, compared by reference to skip the next one */
|
|
77
|
+
values?: any[];
|
|
78
|
+
/**
|
|
79
|
+
* What each read resolved through on the last full read; undefined for a read that didn't
|
|
80
|
+
* resolve. While every route still has the same cached snapshot, no read can have changed.
|
|
81
|
+
*/
|
|
82
|
+
sources?: (IReadSource | undefined)[];
|
|
83
|
+
/** Set for a keyed projection, whose one read is the record it maps */
|
|
84
|
+
keyed?: IKeyedState;
|
|
85
|
+
};
|
|
86
|
+
export type IKeyFunc = {
|
|
87
|
+
(val: any): string | undefined;
|
|
88
|
+
};
|
|
89
|
+
export type IRouteDefinition = {
|
|
90
|
+
projections: IProjectionDefinitions;
|
|
91
|
+
keyFunc?: IKeyFunc;
|
|
92
|
+
routes?: IRouteDefinitions;
|
|
93
|
+
};
|
|
94
|
+
export type IRouteDefinitions = {
|
|
95
|
+
[route: string]: IRouteDefinition;
|
|
96
|
+
};
|
|
97
|
+
export type IRoute = {
|
|
98
|
+
/** The instance's full route path from the tree root, e.g. `/channel/1/` */
|
|
99
|
+
path: string;
|
|
100
|
+
parts: string[];
|
|
101
|
+
projections: IProjection[];
|
|
102
|
+
/** Child instances by their own segments joined with `/`, e.g. `channel/1`, in creation order */
|
|
103
|
+
childRoutes: Map<string, IRoute>;
|
|
104
|
+
/** How many segments each child definition's pattern spans, in definition order, without repeats */
|
|
105
|
+
childPartCounts: number[];
|
|
106
|
+
keyFunc?: IKeyFunc;
|
|
107
|
+
routeDefinitions?: IRouteDefinitions;
|
|
108
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|