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.
@@ -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 batch's diff results,
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: IDiffMethodBatchResult;
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: "diffpath" | "splicepath" | "pushpath" | "patchpath";
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 batch's diff results,
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
- const result = new Array(data.length + 1);
5
- for (let x = 0; x < data.length; x++) {
6
- const call = data[x];
7
- switch (call.method) {
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 { DiffSpliceResult, DiffTreeProjectionMap } from "./diffTree.types";
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`. It would record
11
- * the request/response batches too and send them again on the next `Commit()`.
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.DiffPath("results", computeResults());
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`; post copies, or construct the store with `cloneData`.
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 projections Optional map of derived-value projections, keyed by id. They run once
42
- * per `Commit()`, after the batch's changes.
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(): JsonDiffResult;
52
+ RunProjections(): void;
55
53
  /**
56
- * Computes the diff between a new value and the current value at a path, and records it
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
- DiffPath(path: string, value: any): JsonDiffResult;
57
+ WritePath(path: string, value: any): void;
64
58
  /**
65
- * Merges a patch into the current value at a path, and records the diff for the next
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): JsonDiffResult;
62
+ PatchPath(path: string, patch: any): void;
73
63
  /**
74
- * Appends items to the array at a path, and records the diff for the next `Commit()`.
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[]): JsonDiffResult;
67
+ PushPath(path: string, items: unknown[]): void;
81
68
  /**
82
- * Splices the array at a path, and records the splice for the next `Commit()`.
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[]): DiffSpliceResult<unknown>;
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(): void;
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`. It would record
10
- * the request/response batches too and send them again on the next `Commit()`.
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.DiffPath("results", computeResults());
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`; post copies, or construct the store with `cloneData`.
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 projections Optional map of derived-value projections, keyed by id. They run once
38
- * per `Commit()`, after the batch's changes.
38
+ * @param routes Optional route definitions. Projections run once per `Commit()`, after the
39
+ * batch's changes.
39
40
  */
40
- constructor(worker, keyFunc, projections) {
41
- super(keyFunc, projections);
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
- * Computes the diff between a new value and the current value at a path, and records it
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
- DiffPath(path, value) {
63
- if (this.uncommittedError)
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, and records the diff for the next
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
- if (this.uncommittedError)
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, and records the diff for the next `Commit()`.
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
- if (this.uncommittedError)
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, and records the splice for the next `Commit()`.
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
- if (this.uncommittedError)
136
- throw this.uncommittedError;
137
- try {
138
- const result = super.SplicePath(path, start, deleteCount, items);
139
- this.uncommitted.push({
140
- type: "splice",
141
- result
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
- const result = !this.uncommittedError ? super.RunProjections() : [];
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
- this.uncommitted.length > 0 && this.worker.postMessage({
111
+ diff = super.Commit();
112
+ diff.length > 0 && this.worker.postMessage({
175
113
  type: DIFF_TREE_EVENT,
176
- data: this.uncommitted
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
  }
@@ -1 +1,2 @@
1
1
  export * from "./diffTree.types";
2
+ export * from "./utils/diffTree.types";
@@ -1 +1,2 @@
1
1
  export * from "./diffTree.types";
2
+ export * from "./utils/diffTree.types";
@@ -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 {};