dirvi-lib 0.11.0-beta.6 → 0.11.0-beta.7
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/app/app-api.d.ts +7 -25
- package/dist/app/app-api.d.ts.map +1 -1
- package/dist/app/app-api.js +1 -11
- package/dist/app/index.d.ts +1 -0
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +1 -0
- package/dist/app/node-presentation.d.ts +13 -0
- package/dist/app/node-presentation.d.ts.map +1 -0
- package/dist/app/reducer/effect-to-action.d.ts +2 -2
- package/dist/app/reducer/effect-to-action.d.ts.map +1 -1
- package/dist/app/reducer/effect-to-action.js +87 -32
- package/dist/app/reducer/reducer-action.d.ts +12 -6
- package/dist/app/reducer/reducer-action.d.ts.map +1 -1
- package/dist/app/reducer/reducer.d.ts +4 -2
- package/dist/app/reducer/reducer.d.ts.map +1 -1
- package/dist/app/reducer/reducer.js +102 -68
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/input/application/intent-to-effect.d.ts +2 -2
- package/dist/input/application/intent-to-effect.d.ts.map +1 -1
- package/dist/input/application/intent-to-effect.js +8 -9
- package/dist/input/application/parse-ex-command.d.ts +1 -1
- package/dist/input/application/parse-ex-command.d.ts.map +1 -1
- package/dist/input/application/parse-normal-command.d.ts +1 -1
- package/dist/input/application/parse-normal-command.d.ts.map +1 -1
- package/dist/input/domain/effect.d.ts +1 -2
- package/dist/input/domain/effect.d.ts.map +1 -1
- package/dist/state-and-nav/cursor.d.ts +89 -0
- package/dist/state-and-nav/cursor.d.ts.map +1 -0
- package/dist/state-and-nav/cursor.js +84 -0
- package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.impl.d.ts +1 -1
- package/dist/state-and-nav/fold-node/deprecated/fold-node.impl.d.ts.map +1 -0
- package/dist/state-and-nav/fold-node/deprecated/fold-node.test.d.ts.map +1 -0
- package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.test.js +2 -2
- package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.types.d.ts +2 -2
- package/dist/state-and-nav/fold-node/deprecated/fold-node.types.d.ts.map +1 -0
- package/dist/{trees → state-and-nav}/fold-node/fold-node.impl.d.ts +3 -3
- package/dist/state-and-nav/fold-node/fold-node.impl.d.ts.map +1 -0
- package/dist/{trees → state-and-nav}/fold-node/fold-node.impl.js +1 -1
- package/dist/{trees → state-and-nav}/fold-node/fold-node.model.d.ts +1 -1
- package/dist/state-and-nav/fold-node/fold-node.model.d.ts.map +1 -0
- package/dist/{trees → state-and-nav}/fold-node/fold-node.types.d.ts +2 -2
- package/dist/state-and-nav/fold-node/fold-node.types.d.ts.map +1 -0
- package/dist/state-and-nav/fold-node/index.d.ts.map +1 -0
- package/dist/state-and-nav/index.d.ts +16 -0
- package/dist/state-and-nav/index.d.ts.map +1 -0
- package/dist/state-and-nav/index.js +6 -0
- package/dist/state-and-nav/nav/nav.ordered.impl.d.ts +12 -0
- package/dist/state-and-nav/nav/nav.ordered.impl.d.ts.map +1 -0
- package/dist/state-and-nav/nav/nav.ordered.impl.js +77 -0
- package/dist/state-and-nav/nav/nav.traversal.d.ts +11 -0
- package/dist/state-and-nav/nav/nav.traversal.d.ts.map +1 -0
- package/dist/state-and-nav/nav/nav.traversal.js +207 -0
- package/dist/{trees/nav-node/nav-node.types.d.ts → state-and-nav/nav/nav.types.d.ts} +32 -22
- package/dist/state-and-nav/nav/nav.types.d.ts.map +1 -0
- package/dist/state-and-nav/nav/nav.unordered.impl.d.ts +13 -0
- package/dist/state-and-nav/nav/nav.unordered.impl.d.ts.map +1 -0
- package/dist/state-and-nav/nav/nav.unordered.impl.js +77 -0
- package/dist/state-and-nav/state/state.impl.d.ts +9 -0
- package/dist/state-and-nav/state/state.impl.d.ts.map +1 -0
- package/dist/{trees → state-and-nav}/state/state.impl.js +62 -9
- package/dist/state-and-nav/state/state.schemas.d.ts +2 -0
- package/dist/state-and-nav/state/state.schemas.d.ts.map +1 -0
- package/dist/state-and-nav/state/state.schemas.js +39 -0
- package/dist/state-and-nav/state/state.types.d.ts +72 -0
- package/dist/state-and-nav/state/state.types.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.arb.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.arb.debug.test.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.impl.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.schemas.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.test.d.ts.map +1 -0
- package/dist/state-and-nav/tree/deprecated/tree-node.types.d.ts.map +1 -0
- package/dist/state-and-nav/tree/index.d.ts +7 -0
- package/dist/state-and-nav/tree/index.d.ts.map +1 -0
- package/dist/state-and-nav/tree/index.js +1 -0
- package/dist/state-and-nav/tree/tree-node.arb.d.ts +94 -0
- package/dist/state-and-nav/tree/tree-node.arb.d.ts.map +1 -0
- package/dist/state-and-nav/tree/tree-node.arb.debug.test.d.ts +2 -0
- package/dist/state-and-nav/tree/tree-node.arb.debug.test.d.ts.map +1 -0
- package/dist/state-and-nav/tree/tree-node.arb.debug.test.js +54 -0
- package/dist/state-and-nav/tree/tree-node.arb.js +188 -0
- package/dist/state-and-nav/tree/tree-node.model.d.ts +193 -0
- package/dist/state-and-nav/tree/tree-node.model.d.ts.map +1 -0
- package/dist/state-and-nav/tree/tree-node.model.js +110 -0
- package/dist/state-and-nav/tree/tree-node.test.d.ts +2 -0
- package/dist/state-and-nav/tree/tree-node.test.d.ts.map +1 -0
- package/dist/state-and-nav/tree/tree-node.test.js +234 -0
- package/dist/{trees/tree-node/tree-node.impl.d.ts → state-and-nav/tree/tree.impl.d.ts} +3 -3
- package/dist/state-and-nav/tree/tree.impl.d.ts.map +1 -0
- package/dist/{trees/tree-node/tree-node.impl.js → state-and-nav/tree/tree.impl.js} +19 -16
- package/dist/{trees/tree-node/tree-node.types.d.ts → state-and-nav/tree/tree.types.d.ts} +64 -44
- package/dist/state-and-nav/tree/tree.types.d.ts.map +1 -0
- package/dist/state-and-nav/tree/tree.types.js +1 -0
- package/package.json +1 -1
- package/dist/trees/cursor.d.ts +0 -59
- package/dist/trees/cursor.d.ts.map +0 -1
- package/dist/trees/cursor.js +0 -55
- package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts.map +0 -1
- package/dist/trees/fold-node/deprecated/fold-node.test.d.ts.map +0 -1
- package/dist/trees/fold-node/deprecated/fold-node.types.d.ts.map +0 -1
- package/dist/trees/fold-node/fold-node.impl.d.ts.map +0 -1
- package/dist/trees/fold-node/fold-node.model.d.ts.map +0 -1
- package/dist/trees/fold-node/fold-node.types.d.ts.map +0 -1
- package/dist/trees/fold-node/index.d.ts.map +0 -1
- package/dist/trees/index.d.ts +0 -9
- package/dist/trees/index.d.ts.map +0 -1
- package/dist/trees/index.js +0 -8
- package/dist/trees/nav-node/nav-node.impl.d.ts +0 -29
- package/dist/trees/nav-node/nav-node.impl.d.ts.map +0 -1
- package/dist/trees/nav-node/nav-node.impl.js +0 -223
- package/dist/trees/nav-node/nav-node.types.d.ts.map +0 -1
- package/dist/trees/state/state.impl.d.ts +0 -8
- package/dist/trees/state/state.impl.d.ts.map +0 -1
- package/dist/trees/state/state.schemas.d.ts +0 -25
- package/dist/trees/state/state.schemas.d.ts.map +0 -1
- package/dist/trees/state/state.schemas.js +0 -17
- package/dist/trees/state/state.types.d.ts +0 -36
- package/dist/trees/state/state.types.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.arb.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.arb.debug.test.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.impl.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.schemas.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.test.d.ts.map +0 -1
- package/dist/trees/tree-node/deprecated/tree-node.types.d.ts.map +0 -1
- package/dist/trees/tree-node/index.d.ts +0 -5
- package/dist/trees/tree-node/index.d.ts.map +0 -1
- package/dist/trees/tree-node/index.js +0 -2
- package/dist/trees/tree-node/tree-node.arb.d.ts +0 -104
- package/dist/trees/tree-node/tree-node.arb.d.ts.map +0 -1
- package/dist/trees/tree-node/tree-node.arb.js +0 -188
- package/dist/trees/tree-node/tree-node.impl.d.ts.map +0 -1
- package/dist/trees/tree-node/tree-node.model.d.ts +0 -78
- package/dist/trees/tree-node/tree-node.model.d.ts.map +0 -1
- package/dist/trees/tree-node/tree-node.model.js +0 -37
- package/dist/trees/tree-node/tree-node.types.d.ts.map +0 -1
- /package/dist/{trees/fold-node/deprecated/fold-node.types.js → app/node-presentation.js} +0 -0
- /package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.impl.js +0 -0
- /package/dist/{trees → state-and-nav}/fold-node/deprecated/fold-node.test.d.ts +0 -0
- /package/dist/{trees/fold-node → state-and-nav/fold-node/deprecated}/fold-node.types.js +0 -0
- /package/dist/{trees → state-and-nav}/fold-node/fold-node.model.js +0 -0
- /package/dist/{trees/nav-node/nav-node.types.js → state-and-nav/fold-node/fold-node.types.js} +0 -0
- /package/dist/{trees → state-and-nav}/fold-node/index.d.ts +0 -0
- /package/dist/{trees → state-and-nav}/fold-node/index.js +0 -0
- /package/dist/{trees/state/state.types.js → state-and-nav/nav/nav.types.js} +0 -0
- /package/dist/{trees/tree-node/deprecated/tree-node.types.js → state-and-nav/state/state.types.js} +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.debug.test.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.debug.test.js +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.arb.js +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.impl.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.impl.js +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.schemas.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.schemas.js +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.test.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.test.js +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree}/deprecated/tree-node.types.d.ts +0 -0
- /package/dist/{trees/tree-node → state-and-nav/tree/deprecated}/tree-node.types.js +0 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
function getStateData(state) {
|
|
2
|
+
return state;
|
|
3
|
+
}
|
|
4
|
+
function createState(data) {
|
|
5
|
+
return data;
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
8
|
* Creates operations for navigating and resynchronizing tree-surfer state.
|
|
3
9
|
*/
|
|
4
|
-
export function createStateApi(treeNodeApi, cursorApi) {
|
|
10
|
+
export function createStateApi(treeNodeApi, foldsApi, cursorApi) {
|
|
5
11
|
/**
|
|
6
12
|
* Reloads one node's direct children and recursively reloads descendant
|
|
7
13
|
* branches that were open in the old state.
|
|
@@ -102,27 +108,74 @@ export function createStateApi(treeNodeApi, cursorApi) {
|
|
|
102
108
|
* 5. If no entry cursor exists, use the root cursor.
|
|
103
109
|
*/
|
|
104
110
|
function resyncCursor(_oldState, _newRoot, _newFolds) {
|
|
105
|
-
return [];
|
|
111
|
+
return { path: [] };
|
|
106
112
|
}
|
|
107
113
|
return {
|
|
114
|
+
treeNodeApi,
|
|
115
|
+
foldsApi,
|
|
116
|
+
cursorApi,
|
|
117
|
+
create(root, folds, cursor) {
|
|
118
|
+
return treeNodeApi.kind(root) === 'loaded-branch'
|
|
119
|
+
? createState({ root, folds, cursor })
|
|
120
|
+
: undefined;
|
|
121
|
+
},
|
|
122
|
+
getRoot(state) {
|
|
123
|
+
return getStateData(state).root;
|
|
124
|
+
},
|
|
125
|
+
getFolds(state) {
|
|
126
|
+
return getStateData(state).folds;
|
|
127
|
+
},
|
|
128
|
+
getCursor(state) {
|
|
129
|
+
return getStateData(state).cursor;
|
|
130
|
+
},
|
|
131
|
+
update(state, changes) {
|
|
132
|
+
const data = getStateData(state);
|
|
133
|
+
const root = changes.root ?? data.root;
|
|
134
|
+
const folds = changes.folds ?? data.folds;
|
|
135
|
+
const cursor = changes.cursor ?? data.cursor;
|
|
136
|
+
if (treeNodeApi.kind(root) !== 'loaded-branch') {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
if (root === data.root &&
|
|
140
|
+
folds === data.folds &&
|
|
141
|
+
cursor === data.cursor) {
|
|
142
|
+
return state;
|
|
143
|
+
}
|
|
144
|
+
return createState({ root, folds, cursor });
|
|
145
|
+
},
|
|
108
146
|
getNodeAtCursor(state) {
|
|
109
|
-
const
|
|
110
|
-
|
|
147
|
+
const { root, cursor } = getStateData(state);
|
|
148
|
+
/*
|
|
149
|
+
* A fold cursor identifies a display item, not a single source node.
|
|
150
|
+
* Its representative path must not silently select its first child.
|
|
151
|
+
*/
|
|
152
|
+
if (!cursorApi.isNodeCursor(cursor)) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
return treeNodeApi.selectAtPath(root, cursor.path, (node) => node);
|
|
156
|
+
},
|
|
157
|
+
isCursorRoot(state) {
|
|
158
|
+
const { cursor } = getStateData(state);
|
|
159
|
+
return cursorApi.isNodeCursor(cursor) && cursor.path.length === 0;
|
|
160
|
+
},
|
|
161
|
+
cursorPath(state) {
|
|
162
|
+
const { cursor } = getStateData(state);
|
|
163
|
+
return cursorApi.getPath(cursor);
|
|
111
164
|
},
|
|
112
165
|
async resync(oldState, loadBranches) {
|
|
113
|
-
const
|
|
166
|
+
const oldStateData = getStateData(oldState);
|
|
167
|
+
const reloaded = await reload(oldStateData.root, [], loadBranches);
|
|
114
168
|
/*
|
|
115
169
|
* Fold-state resynchronization is not implemented yet. Keep the existing
|
|
116
170
|
* fold definitions while the source tree is reloaded.
|
|
117
171
|
*/
|
|
118
|
-
const folds =
|
|
172
|
+
const folds = oldStateData.folds;
|
|
119
173
|
const root = reloaded.root;
|
|
120
|
-
return {
|
|
121
|
-
...oldState,
|
|
174
|
+
return createState({
|
|
122
175
|
root,
|
|
123
176
|
folds,
|
|
124
177
|
cursor: resyncCursor(oldState, root, folds),
|
|
125
|
-
};
|
|
178
|
+
});
|
|
126
179
|
},
|
|
127
180
|
};
|
|
128
181
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.schemas.d.ts","sourceRoot":"","sources":["../../../src/state-and-nav/state/state.schemas.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export {};
|
|
2
|
+
// import { z } from 'zod';
|
|
3
|
+
// import type { Cursor } from './unordered-nav/cursor.js';
|
|
4
|
+
// import type { FoldsModel } from './fold-node/index.js';
|
|
5
|
+
// import type { SerializableKey, TreeNodeModel } from './tree/index.js';
|
|
6
|
+
//
|
|
7
|
+
// /**
|
|
8
|
+
// * Serializable persistence/interchange model for tree-surfer state.
|
|
9
|
+
// *
|
|
10
|
+
// * This is intentionally distinct from runtime `State`: its root and folds use
|
|
11
|
+
// * public structural models, while runtime state holds opaque TreeNode and
|
|
12
|
+
// * Folds values.
|
|
13
|
+
// */
|
|
14
|
+
// export type StateModel<Id extends SerializableKey, Value> = Readonly<{
|
|
15
|
+
// root: TreeNodeModel<Id, Value>;
|
|
16
|
+
// folds: FoldsModel<Id>;
|
|
17
|
+
// cursor: Cursor<Id>;
|
|
18
|
+
// }>;
|
|
19
|
+
//
|
|
20
|
+
// /**
|
|
21
|
+
// * Creates a schema for the serializable StateModel DTO.
|
|
22
|
+
// *
|
|
23
|
+
// * This validates only the persistence representation. Applications must decode
|
|
24
|
+
// * the validated root and folds models through their specialized TreeNodeApi and
|
|
25
|
+
// * FoldsApi before constructing opaque runtime State.
|
|
26
|
+
// */
|
|
27
|
+
// export function createStateModelSchema<Id extends SerializableKey, Value>(
|
|
28
|
+
// rootSchema: z.ZodType<TreeNodeModel<Id, Value>>,
|
|
29
|
+
// foldsSchema: z.ZodType<FoldsModel<Id>>,
|
|
30
|
+
// cursorSchema: z.ZodType<Cursor<Id>>,
|
|
31
|
+
// ): z.ZodType<StateModel<Id, Value>> {
|
|
32
|
+
// return z
|
|
33
|
+
// .object({
|
|
34
|
+
// root: rootSchema,
|
|
35
|
+
// folds: foldsSchema,
|
|
36
|
+
// cursor: cursorSchema,
|
|
37
|
+
// })
|
|
38
|
+
// .strict();
|
|
39
|
+
// }
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SerializableKey, TreeNode, TreeMethods } from '../tree/index.js';
|
|
2
|
+
import type { FoldMethods, Folds } from '../fold-node/index.js';
|
|
3
|
+
import type { Cursor, CursorMethods } from '../cursor.js';
|
|
4
|
+
declare const stateBrand: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* Opaque type for state of the tree, folds, and cursor.
|
|
7
|
+
*
|
|
8
|
+
* Used by consumers as React State.
|
|
9
|
+
*/
|
|
10
|
+
export type State<Id extends SerializableKey, Value> = Readonly<{
|
|
11
|
+
[stateBrand]: Readonly<{
|
|
12
|
+
id: Id;
|
|
13
|
+
value: Value;
|
|
14
|
+
}>;
|
|
15
|
+
}>;
|
|
16
|
+
export type StateApi<Id extends SerializableKey, Value> = Readonly<{
|
|
17
|
+
treeNodeApi: TreeMethods<Id, Value>;
|
|
18
|
+
foldsApi: FoldMethods<Id>;
|
|
19
|
+
cursorApi: CursorMethods<Id>;
|
|
20
|
+
/**
|
|
21
|
+
* Creates state whose root is a loaded branch.
|
|
22
|
+
*
|
|
23
|
+
* Returns undefined when root is a leaf or an unloaded branch.
|
|
24
|
+
*/
|
|
25
|
+
create(root: TreeNode<Id, Value>, folds: Folds<Id>, cursor: Cursor<Id>): State<Id, Value> | undefined;
|
|
26
|
+
getRoot(state: State<Id, Value>): TreeNode<Id, Value>;
|
|
27
|
+
getFolds(state: State<Id, Value>): Folds<Id>;
|
|
28
|
+
getCursor(state: State<Id, Value>): Cursor<Id>;
|
|
29
|
+
/**
|
|
30
|
+
* Immutably replaces supplied state components.
|
|
31
|
+
*
|
|
32
|
+
* Returns undefined when the replacement root is not a loaded branch.
|
|
33
|
+
* Returns the original state when all components retain their references.
|
|
34
|
+
*/
|
|
35
|
+
update(state: State<Id, Value>, changes: Readonly<{
|
|
36
|
+
root?: TreeNode<Id, Value>;
|
|
37
|
+
folds?: Folds<Id>;
|
|
38
|
+
cursor?: Cursor<Id>;
|
|
39
|
+
}>): State<Id, Value> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a mutable root-relative path.
|
|
42
|
+
*
|
|
43
|
+
* For a fold cursor, returns the path of its first hidden child.
|
|
44
|
+
* This representative path does not identify the fold marker itself.
|
|
45
|
+
*/
|
|
46
|
+
cursorPath(state: State<Id, Value>): Id[];
|
|
47
|
+
/**
|
|
48
|
+
* Tests whether the cursor is the root node cursor: { path: [] }.
|
|
49
|
+
*/
|
|
50
|
+
isCursorRoot(state: State<Id, Value>): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Resolves a node cursor against the source tree.
|
|
53
|
+
*
|
|
54
|
+
* Returns undefined for fold cursors or paths absent from the loaded tree.
|
|
55
|
+
* This does not check visibility in a navigation projection.
|
|
56
|
+
*/
|
|
57
|
+
getNodeAtCursor(state: State<Id, Value>): TreeNode<Id, Value> | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Reloads the root's children and all descendant branches that were loaded
|
|
60
|
+
* in the previous state.
|
|
61
|
+
*
|
|
62
|
+
* `loadBranches([])` loads the root's direct children.
|
|
63
|
+
* `loadBranches(['src'])` loads the children of `src`, where `src` is the
|
|
64
|
+
* ID of a root child.
|
|
65
|
+
*
|
|
66
|
+
* Returned nodes must be valid TreeNode values created or decoded through
|
|
67
|
+
* the specialized TreeNodeApi for `Id` and `Value`.
|
|
68
|
+
*/
|
|
69
|
+
resync(oldState: State<Id, Value>, loadBranches: (path: readonly Id[]) => Promise<readonly TreeNode<Id, Value>[]>): Promise<State<Id, Value>>;
|
|
70
|
+
}>;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=state.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.types.d.ts","sourceRoot":"","sources":["../../../src/state-and-nav/state/state.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,CAAC,MAAM,UAAU,EAAE,OAAO,MAAM,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,QAAQ,CAAC;IAC9D,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC;QACrB,EAAE,EAAE,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC;KACd,CAAC,CAAC;CACJ,CAAC,CAAC;AAEH,MAAM,MAAM,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,QAAQ,CAAC;IACjE,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACpC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC1B,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;IAE7B;;;;OAIG;IACH,MAAM,CACJ,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,EAChB,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,GACjB,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEhC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7C,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IAE/C;;;;;OAKG;IACH,MAAM,CACJ,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,EACvB,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC3B,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;KACrB,CAAC,GACD,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEhC;;;;;OAKG;IACH,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;IAC1C;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC;IAC/C;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAE1E;;;;;;;;;;OAUG;IACH,MAAM,CACJ,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,EAC1B,YAAY,EAAE,CACZ,IAAI,EAAE,SAAS,EAAE,EAAE,KAChB,OAAO,CAAC,SAAS,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,GAC3C,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.arb.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.arb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;GAIG;AACH,KAAK,UAAU,GAAG,MAAM,CAAC;AAOzB;;;GAGG;AACH,eAAO,MAAM,SAAS,sBAIpB,CAAC;AAEH;;GAEG;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAC1D,KAAK,IAAI,GAAG,UAAU,EAAE,CAAC;AAEzB,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,cAAc,EAAE,UAAU,EAAE,CAAC;CAC9B,CAAC;AAuEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,sCAEhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,sCAExC,CAAC;AAaF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC,SAAS,CAAC,sBAAsB,CA0C5D,CAAC;AAEP;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,wCAIjC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;EAK3B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;;EAQnC,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;EAYpC,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,cAAc,qBAE1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,4BAA8C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.arb.debug.test.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.arb.debug.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.impl.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.impl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,eAAe,EACf,QAAQ,EACR,WAAW,EACZ,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,KAC5B,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAqGzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.schemas.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEjE;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,iDAA6C,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;EAoBrD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.test.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.types.d.ts","sourceRoot":"","sources":["../../../../src/state-and-nav/tree/deprecated/tree-node.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,QAAQ,CAClB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B,YAAY,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AAEhD,+BAA+B;AAC/B,MAAM,MAAM,YAAY,CAAC,EAAE,SAAS,eAAe,IAAI;IACrD,EAAE,EAAE,EAAE,CAAC;IACP,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,cAAc,CACxB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;AAE5D,uDAAuD;AACvD,MAAM,MAAM,kBAAkB,CAC5B,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B;IACF,EAAE,EAAE,EAAE,CAAC;IACP,QAAQ,EAAE,IAAI,EAAE,CAAC;CAClB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,oBAAoB,CAAC,EAAE,SAAS,eAAe,IAAI;IAC7D,EAAE,EAAE,EAAE,CAAC;IACP,QAAQ,EAAE,IAAI,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,WAAW,CACrB,EAAE,SAAS,eAAe,EAC1B,IAAI,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,IAC7B;IACF,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IACpD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9D,cAAc,CACZ,IAAI,EAAE,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,GAC7B,IAAI,IAAI,oBAAoB,CAAC,EAAE,CAAC,CAAC;IACpC,YAAY,CACV,IAAI,EAAE,cAAc,CAAC,EAAE,EAAE,IAAI,CAAC,GAC7B,IAAI,IAAI,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAExC;;;;;;;;OAQG;IACH,WAAW,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;OAMG;IACH,YAAY,CAAC,IAAI,EAAE,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IAE3E;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,MAAM,EACd,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,GAC/B,MAAM,GAAG,SAAS,CAAC;IAEtB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CACV,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,SAAS,GACzC,IAAI,GAAG,SAAS,CAAC;CACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/state-and-nav/tree/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,mBAAmB,iBAAiB,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createTreeMethods } from './tree.impl.js';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as fc from 'fast-check';
|
|
2
|
+
import { TreeNodeModel } from './tree-node.model.js';
|
|
3
|
+
/**
|
|
4
|
+
* The ID and Values types used for the generated test trees.
|
|
5
|
+
*
|
|
6
|
+
* Short strings keep generated counterexamples readable. Values just have to
|
|
7
|
+
* be something easily checked for equality.
|
|
8
|
+
*/
|
|
9
|
+
type Id = string;
|
|
10
|
+
type Value = number;
|
|
11
|
+
type ModelAndPath = {
|
|
12
|
+
model: TreeNodeModel<Id, Value>;
|
|
13
|
+
path: Id[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A model and a pair of a model and its path for every reachable path in the
|
|
17
|
+
* model
|
|
18
|
+
*
|
|
19
|
+
* Includes this model at `[]` and every descendant reachable through loaded
|
|
20
|
+
* branches. Descendants behind unloaded branches are intentionally absent.
|
|
21
|
+
*/
|
|
22
|
+
type ModelWithReachableModels = {
|
|
23
|
+
model: TreeNodeModel<Id, Value>;
|
|
24
|
+
reachableModels: ModelAndPath[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Generates valid string IDs for test nodes. Note that the API just does
|
|
28
|
+
* comparisons on the IDs so we can safely restrict to any subset.
|
|
29
|
+
*/
|
|
30
|
+
export declare const idArb: fc.Arbitrary<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Generates serializable application values for test nodes.
|
|
33
|
+
*/
|
|
34
|
+
export declare const valueArb: fc.Arbitrary<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Generates a leaf model.
|
|
37
|
+
*/
|
|
38
|
+
export declare const leafModelWRMArb: fc.Arbitrary<ModelWithReachableModels>;
|
|
39
|
+
/**
|
|
40
|
+
* Generates an unloaded-branch model.
|
|
41
|
+
*/
|
|
42
|
+
export declare const unloadedBranchModelWRMArb: fc.Arbitrary<ModelWithReachableModels>;
|
|
43
|
+
/**
|
|
44
|
+
* Generates a leaf, an unloaded branch, or a loaded branch.
|
|
45
|
+
*
|
|
46
|
+
* Loaded branches have unique child IDs. Root IDs are made unique separately
|
|
47
|
+
* when generating a forest.
|
|
48
|
+
*
|
|
49
|
+
* The weighted choice gives recursive loaded branches a geometric drop off.
|
|
50
|
+
*/
|
|
51
|
+
export declare const modelWithReachableModelsArb: fc.Arbitrary<ModelWithReachableModels>;
|
|
52
|
+
/**
|
|
53
|
+
* Generates an array of generated models with unique sibling IDs.
|
|
54
|
+
*
|
|
55
|
+
* The result is suitable for use as the loaded children of a loaded branch.
|
|
56
|
+
*/
|
|
57
|
+
export declare const siblingModelsWRMArb: fc.Arbitrary<ModelWithReachableModels[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Generates root-level models and a path to one of their reachable models.
|
|
60
|
+
*
|
|
61
|
+
* Every generated path is valid for `getAtPath`; unreachable paths are not
|
|
62
|
+
* included.
|
|
63
|
+
*/
|
|
64
|
+
export declare const modelAndReachablePathArb: fc.Arbitrary<{
|
|
65
|
+
root: TreeNodeModel<string, number>;
|
|
66
|
+
path: string[];
|
|
67
|
+
expected: TreeNodeModel<string, number>;
|
|
68
|
+
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Generates a root model and a path to one of its reachable branch models.
|
|
71
|
+
*
|
|
72
|
+
* Both loaded and unloaded branches are included.
|
|
73
|
+
*/
|
|
74
|
+
export declare const modelAndBranchPathArb: fc.Arbitrary<{
|
|
75
|
+
root: TreeNodeModel<string, number>;
|
|
76
|
+
path: string[];
|
|
77
|
+
expected: TreeNodeModel<string, number>;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Generates a new array of loaded child models.
|
|
81
|
+
*
|
|
82
|
+
* The generated models have unique IDs, making the result suitable for use as
|
|
83
|
+
* the children of a loaded branch.
|
|
84
|
+
*/
|
|
85
|
+
export declare const childrenModelsArb: fc.Arbitrary<TreeNodeModel<string, number>[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Generates either a new array of loaded child models or `null`.
|
|
88
|
+
*
|
|
89
|
+
* An array represents a loaded branch, including a loaded branch with no
|
|
90
|
+
* children. `null` represents an unloaded branch.
|
|
91
|
+
*/
|
|
92
|
+
export declare const childrenStateModelArb: fc.Arbitrary<TreeNodeModel<string, number>[] | null>;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=tree-node.arb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.arb.d.ts","sourceRoot":"","sources":["../../../src/state-and-nav/tree/tree-node.arb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;GAKG;AACH,KAAK,EAAE,GAAG,MAAM,CAAC;AACjB,KAAK,KAAK,GAAG,MAAM,CAAC;AAEpB,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAChC,IAAI,EAAE,EAAE,EAAE,CAAC;CACZ,CAAC;AAEF;;;;;;GAMG;AACH,KAAK,wBAAwB,GAAG;IAC9B,KAAK,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAChC,eAAe,EAAE,YAAY,EAAE,CAAC;CACjC,CAAC;AAWF;;;GAGG;AACH,eAAO,MAAM,KAAK,sBAIhB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,sBAGnB,CAAC;AAyEH;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,SAAS,CAAC,wBAAwB,CAEhB,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,yBAAyB,EAAE,EAAE,CAAC,SAAS,CAAC,wBAAwB,CAGd,CAAC;AAUhE;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,EAAE,EAAE,CAAC,SAAS,CAAC,wBAAwB,CAyCtE,CAAC;AASV;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,0CAI9B,CAAC;AAIH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;EAOpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;EAc/B,CAAC;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+CAE7B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,sDAAiD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-node.arb.debug.test.d.ts","sourceRoot":"","sources":["../../../src/state-and-nav/tree/tree-node.arb.debug.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it } from 'vitest';
|
|
2
|
+
import * as fc from 'fast-check';
|
|
3
|
+
import { modelWithReachableModelsArb, siblingModelsWRMArb } from './tree-node.arb.js';
|
|
4
|
+
/**
|
|
5
|
+
* Manual inspection of generated models and their reachable paths.
|
|
6
|
+
*
|
|
7
|
+
* Remove `.skip` temporarily to run these inspections.
|
|
8
|
+
* These are debugging aids, not assertions about the arbitraries.
|
|
9
|
+
*/
|
|
10
|
+
describe.skip('inspect tree-node arbitraries', () => {
|
|
11
|
+
const seed = 12345;
|
|
12
|
+
it('prints generated models and reachable paths', () => {
|
|
13
|
+
const samples = fc.sample(modelWithReachableModelsArb, {
|
|
14
|
+
numRuns: 10,
|
|
15
|
+
seed,
|
|
16
|
+
});
|
|
17
|
+
for (const [index, generated] of samples.entries()) {
|
|
18
|
+
console.log(`\n--- model ${index + 1} ---`);
|
|
19
|
+
console.dir(generated.model, {
|
|
20
|
+
depth: Infinity,
|
|
21
|
+
colors: false,
|
|
22
|
+
});
|
|
23
|
+
console.log(`reachable nodes: ${generated.reachableModels.length}`, `maximum depth: ${Math.max(...generated.reachableModels.map(({ path }) => path.length))}`);
|
|
24
|
+
console.log('reachable paths:');
|
|
25
|
+
console.dir(generated.reachableModels.map(({ path, model }) => ({
|
|
26
|
+
path,
|
|
27
|
+
id: model.id,
|
|
28
|
+
kind: !('children' in model)
|
|
29
|
+
? 'leaf'
|
|
30
|
+
: model.children === null
|
|
31
|
+
? 'unloaded-branch'
|
|
32
|
+
: 'loaded-branch',
|
|
33
|
+
})), {
|
|
34
|
+
depth: Infinity,
|
|
35
|
+
colors: false,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
it('prints generated sibling collections', () => {
|
|
40
|
+
const samples = fc.sample(siblingModelsWRMArb, {
|
|
41
|
+
numRuns: 5,
|
|
42
|
+
seed,
|
|
43
|
+
});
|
|
44
|
+
for (const [index, generatedNodes] of samples.entries()) {
|
|
45
|
+
console.log(`\n--- sibling collection ${index + 1} ---`);
|
|
46
|
+
console.dir(generatedNodes.map(({ model }) => model), {
|
|
47
|
+
depth: Infinity,
|
|
48
|
+
colors: false,
|
|
49
|
+
});
|
|
50
|
+
console.log('sibling IDs:', generatedNodes.map(({ model }) => model.id));
|
|
51
|
+
console.log('total reachable nodes:', generatedNodes.reduce((count, generated) => count + generated.reachableModels.length, 0));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as fc from 'fast-check';
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// ID and Value Arbitraries
|
|
4
|
+
// =============================================================================
|
|
5
|
+
/**
|
|
6
|
+
* Characters used for readable generated node IDs.
|
|
7
|
+
*/
|
|
8
|
+
const smallLetterCharacters = Array.from('abcdefghijklmnopqrstuvwxyz');
|
|
9
|
+
/**
|
|
10
|
+
* Generates valid string IDs for test nodes. Note that the API just does
|
|
11
|
+
* comparisons on the IDs so we can safely restrict to any subset.
|
|
12
|
+
*/
|
|
13
|
+
export const idArb = fc.string({
|
|
14
|
+
unit: fc.constantFrom(...smallLetterCharacters),
|
|
15
|
+
minLength: 1,
|
|
16
|
+
maxLength: 8,
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Generates serializable application values for test nodes.
|
|
20
|
+
*/
|
|
21
|
+
export const valueArb = fc.integer({
|
|
22
|
+
min: -10_000,
|
|
23
|
+
max: 10_000,
|
|
24
|
+
});
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Generating Models with Reachable Models
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Helpers
|
|
29
|
+
const createLeafModel = (id, value) => {
|
|
30
|
+
const model = {
|
|
31
|
+
id,
|
|
32
|
+
value,
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
model,
|
|
36
|
+
reachableModels: [{ path: [], model }],
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
function createUnloadedBranchModel(id, value) {
|
|
40
|
+
const model = {
|
|
41
|
+
id,
|
|
42
|
+
value,
|
|
43
|
+
children: null,
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
model,
|
|
47
|
+
reachableModels: [{ path: [], model }],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates the metadata for a loaded branch and all descendants reachable
|
|
52
|
+
* through its loaded child collection.
|
|
53
|
+
*
|
|
54
|
+
* Child-array order is preserved, both in the model and in the enumeration
|
|
55
|
+
* of reachable descendants.
|
|
56
|
+
*/
|
|
57
|
+
function createLoadedBranchModel(id, value, children) {
|
|
58
|
+
const model = {
|
|
59
|
+
id,
|
|
60
|
+
value,
|
|
61
|
+
children: children.map(({ model: child }) => child),
|
|
62
|
+
};
|
|
63
|
+
const reachableDescendants = children.flatMap(({ model: childModel, reachableModels }) => reachableModels.map(({ path, model: reachableModel }) => ({
|
|
64
|
+
path: [childModel.id, ...path],
|
|
65
|
+
model: reachableModel,
|
|
66
|
+
})));
|
|
67
|
+
return {
|
|
68
|
+
model,
|
|
69
|
+
reachableModels: [{ path: [], model }, ...reachableDescendants],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Arbs
|
|
73
|
+
/**
|
|
74
|
+
* Generates a leaf model.
|
|
75
|
+
*/
|
|
76
|
+
export const leafModelWRMArb = fc
|
|
77
|
+
.tuple(idArb, valueArb)
|
|
78
|
+
.map(([id, value]) => createLeafModel(id, value));
|
|
79
|
+
/**
|
|
80
|
+
* Generates an unloaded-branch model.
|
|
81
|
+
*/
|
|
82
|
+
export const unloadedBranchModelWRMArb = fc
|
|
83
|
+
.tuple(idArb, valueArb)
|
|
84
|
+
.map(([id, value]) => createUnloadedBranchModel(id, value));
|
|
85
|
+
/**
|
|
86
|
+
* Generates either kind of 'terminal' model.
|
|
87
|
+
*
|
|
88
|
+
* Both leaves and unloaded branches terminate path traversal.
|
|
89
|
+
*/
|
|
90
|
+
const terminalModelWRMArb = fc.oneof(leafModelWRMArb, unloadedBranchModelWRMArb);
|
|
91
|
+
/**
|
|
92
|
+
* Generates a leaf, an unloaded branch, or a loaded branch.
|
|
93
|
+
*
|
|
94
|
+
* Loaded branches have unique child IDs. Root IDs are made unique separately
|
|
95
|
+
* when generating a forest.
|
|
96
|
+
*
|
|
97
|
+
* The weighted choice gives recursive loaded branches a geometric drop off.
|
|
98
|
+
*/
|
|
99
|
+
export const modelWithReachableModelsArb = fc.letrec((tie) => {
|
|
100
|
+
const generatedChildModelArb = tie('node');
|
|
101
|
+
/**
|
|
102
|
+
* Generates the loaded children of a loaded branch.
|
|
103
|
+
*
|
|
104
|
+
* Sibling IDs must be unique so that child lookup is unambiguous. The
|
|
105
|
+
* maximum length also limits the branching factor of recursive examples.
|
|
106
|
+
*/
|
|
107
|
+
const generatedLoadedChildrenArb = fc.uniqueArray(generatedChildModelArb, {
|
|
108
|
+
minLength: 0,
|
|
109
|
+
maxLength: 4,
|
|
110
|
+
size: 'medium',
|
|
111
|
+
selector: ({ model }) => model.id,
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Generates a loaded branch and its reachable descendants.
|
|
115
|
+
*/
|
|
116
|
+
const generatedLoadedBranchModelArb = fc
|
|
117
|
+
.tuple(idArb, valueArb, generatedLoadedChildrenArb)
|
|
118
|
+
.map(([id, value, children]) => createLoadedBranchModel(id, value, children));
|
|
119
|
+
return {
|
|
120
|
+
node: fc
|
|
121
|
+
.integer({
|
|
122
|
+
min: 0,
|
|
123
|
+
max: 9999,
|
|
124
|
+
})
|
|
125
|
+
.chain((choice) =>
|
|
126
|
+
// 5046 works for getAtPath tests, but too heavy for modifyAtPath tests
|
|
127
|
+
choice >= 5050
|
|
128
|
+
? generatedLoadedBranchModelArb
|
|
129
|
+
: terminalModelWRMArb),
|
|
130
|
+
};
|
|
131
|
+
}).node;
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Generating Models
|
|
134
|
+
// =============================================================================
|
|
135
|
+
// Prepares inputs for particular tests.
|
|
136
|
+
// Generating Replacement Child State
|
|
137
|
+
/**
|
|
138
|
+
* Generates an array of generated models with unique sibling IDs.
|
|
139
|
+
*
|
|
140
|
+
* The result is suitable for use as the loaded children of a loaded branch.
|
|
141
|
+
*/
|
|
142
|
+
export const siblingModelsWRMArb = fc.uniqueArray(modelWithReachableModelsArb, {
|
|
143
|
+
minLength: 0,
|
|
144
|
+
size: 'small',
|
|
145
|
+
selector: (generated) => generated.model.id,
|
|
146
|
+
});
|
|
147
|
+
// Generating Models with a Selected Reachable Target
|
|
148
|
+
/**
|
|
149
|
+
* Generates root-level models and a path to one of their reachable models.
|
|
150
|
+
*
|
|
151
|
+
* Every generated path is valid for `getAtPath`; unreachable paths are not
|
|
152
|
+
* included.
|
|
153
|
+
*/
|
|
154
|
+
export const modelAndReachablePathArb = modelWithReachableModelsArb.chain(({ model: root, reachableModels }) => fc.constantFrom(...reachableModels).map(({ path, model }) => ({
|
|
155
|
+
root,
|
|
156
|
+
path,
|
|
157
|
+
expected: model,
|
|
158
|
+
})));
|
|
159
|
+
/**
|
|
160
|
+
* Generates a root model and a path to one of its reachable branch models.
|
|
161
|
+
*
|
|
162
|
+
* Both loaded and unloaded branches are included.
|
|
163
|
+
*/
|
|
164
|
+
export const modelAndBranchPathArb = modelWithReachableModelsArb
|
|
165
|
+
.map(({ model: root, reachableModels }) => ({
|
|
166
|
+
root,
|
|
167
|
+
branchPathEntries: reachableModels.filter(({ model }) => 'children' in model),
|
|
168
|
+
}))
|
|
169
|
+
.filter(({ branchPathEntries }) => branchPathEntries.length > 0)
|
|
170
|
+
.chain(({ root, branchPathEntries }) => fc.constantFrom(...branchPathEntries).map(({ path, model }) => ({
|
|
171
|
+
root,
|
|
172
|
+
path,
|
|
173
|
+
expected: model,
|
|
174
|
+
})));
|
|
175
|
+
/**
|
|
176
|
+
* Generates a new array of loaded child models.
|
|
177
|
+
*
|
|
178
|
+
* The generated models have unique IDs, making the result suitable for use as
|
|
179
|
+
* the children of a loaded branch.
|
|
180
|
+
*/
|
|
181
|
+
export const childrenModelsArb = siblingModelsWRMArb.map((generatedNodes) => generatedNodes.map(({ model }) => model));
|
|
182
|
+
/**
|
|
183
|
+
* Generates either a new array of loaded child models or `null`.
|
|
184
|
+
*
|
|
185
|
+
* An array represents a loaded branch, including a loaded branch with no
|
|
186
|
+
* children. `null` represents an unloaded branch.
|
|
187
|
+
*/
|
|
188
|
+
export const childrenStateModelArb = fc.oneof(childrenModelsArb, fc.constant(null));
|