dirvi-lib 0.8.0 → 0.8.2

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.
Files changed (178) hide show
  1. package/dist/app/app-api.d.ts +74 -0
  2. package/dist/app/app-api.d.ts.map +1 -0
  3. package/dist/app/app-api.js +11 -0
  4. package/dist/app/effect-to-action.d.ts +6 -0
  5. package/dist/app/effect-to-action.d.ts.map +1 -0
  6. package/dist/app/effect-to-action.js +74 -0
  7. package/dist/app/index.d.ts +6 -0
  8. package/dist/app/index.d.ts.map +1 -0
  9. package/dist/app/index.js +5 -0
  10. package/dist/app/reducer-action.d.ts +22 -0
  11. package/dist/app/reducer-action.d.ts.map +1 -0
  12. package/dist/app/reducer.d.ts +5 -0
  13. package/dist/app/reducer.d.ts.map +1 -0
  14. package/dist/app/reducer.js +79 -0
  15. package/dist/app/view-api.d.ts +11 -0
  16. package/dist/app/view-api.d.ts.map +1 -0
  17. package/dist/index.d.ts +4 -7
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -7
  20. package/dist/input/application/index.d.ts.map +1 -0
  21. package/dist/input/application/intent-to-effect-no-state.test.d.ts.map +1 -0
  22. package/dist/input/application/intent-to-effect.d.ts +14 -0
  23. package/dist/input/application/intent-to-effect.d.ts.map +1 -0
  24. package/dist/{tree-surfer-ui → input}/application/intent-to-effect.js +36 -17
  25. package/dist/input/application/parse-command.d.ts +14 -0
  26. package/dist/input/application/parse-command.d.ts.map +1 -0
  27. package/dist/{tree-surfer-ui → input}/application/parse-command.js +13 -1
  28. package/dist/input/application/user-input-to-intent.d.ts.map +1 -0
  29. package/dist/input/application/user-input-to-intent.test.d.ts.map +1 -0
  30. package/dist/input/domain/effect.d.ts +55 -0
  31. package/dist/input/domain/effect.d.ts.map +1 -0
  32. package/dist/input/domain/index.d.ts.map +1 -0
  33. package/dist/input/domain/input-mode-state.d.ts.map +1 -0
  34. package/dist/input/domain/intent.d.ts.map +1 -0
  35. package/dist/input/domain/user-input.d.ts.map +1 -0
  36. package/dist/trees/cursor.d.ts.map +1 -0
  37. package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts +10 -0
  38. package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts.map +1 -0
  39. package/dist/trees/fold-node/deprecated/fold-node.impl.js +248 -0
  40. package/dist/trees/fold-node/deprecated/fold-node.test.d.ts +2 -0
  41. package/dist/trees/fold-node/deprecated/fold-node.test.d.ts.map +1 -0
  42. package/dist/trees/fold-node/deprecated/fold-node.test.js +130 -0
  43. package/dist/{tree-surfer/fold-node → trees/fold-node/deprecated}/fold-node.types.d.ts +15 -76
  44. package/dist/trees/fold-node/deprecated/fold-node.types.d.ts.map +1 -0
  45. package/dist/trees/fold-node/fold-node.impl.d.ts +16 -0
  46. package/dist/trees/fold-node/fold-node.impl.d.ts.map +1 -0
  47. package/dist/trees/fold-node/fold-node.impl.js +451 -0
  48. package/dist/trees/fold-node/fold-node.model.d.ts +120 -0
  49. package/dist/trees/fold-node/fold-node.model.d.ts.map +1 -0
  50. package/dist/trees/fold-node/fold-node.model.js +80 -0
  51. package/dist/trees/fold-node/fold-node.types.d.ts +206 -0
  52. package/dist/trees/fold-node/fold-node.types.d.ts.map +1 -0
  53. package/dist/trees/fold-node/index.d.ts +4 -0
  54. package/dist/trees/fold-node/index.d.ts.map +1 -0
  55. package/dist/trees/fold-node/index.js +3 -0
  56. package/dist/trees/index.d.ts +9 -0
  57. package/dist/trees/index.d.ts.map +1 -0
  58. package/dist/trees/index.js +8 -0
  59. package/dist/trees/nav-node/nav-node.impl.d.ts +29 -0
  60. package/dist/trees/nav-node/nav-node.impl.d.ts.map +1 -0
  61. package/dist/{tree-surfer → trees}/nav-node/nav-node.impl.js +89 -42
  62. package/dist/trees/nav-node/nav-node.types.d.ts +103 -0
  63. package/dist/trees/nav-node/nav-node.types.d.ts.map +1 -0
  64. package/dist/trees/state/state.impl.d.ts +8 -0
  65. package/dist/trees/state/state.impl.d.ts.map +1 -0
  66. package/dist/trees/state/state.impl.js +128 -0
  67. package/dist/trees/state/state.schemas.d.ts +25 -0
  68. package/dist/trees/state/state.schemas.d.ts.map +1 -0
  69. package/dist/trees/state/state.schemas.js +17 -0
  70. package/dist/trees/state/state.types.d.ts +36 -0
  71. package/dist/trees/state/state.types.d.ts.map +1 -0
  72. package/dist/trees/state/state.types.js +1 -0
  73. package/dist/trees/tree-node/deprecated/tree-node.arb.d.ts.map +1 -0
  74. package/dist/trees/tree-node/deprecated/tree-node.arb.debug.test.d.ts.map +1 -0
  75. package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.debug.test.js +1 -1
  76. package/dist/trees/tree-node/deprecated/tree-node.impl.d.ts.map +1 -0
  77. package/dist/trees/tree-node/deprecated/tree-node.schemas.d.ts.map +1 -0
  78. package/dist/trees/tree-node/deprecated/tree-node.test.d.ts.map +1 -0
  79. package/dist/trees/tree-node/deprecated/tree-node.types.d.ts.map +1 -0
  80. package/dist/trees/tree-node/deprecated/tree-node.types.js +1 -0
  81. package/dist/trees/tree-node/index.d.ts +4 -0
  82. package/dist/trees/tree-node/index.d.ts.map +1 -0
  83. package/dist/trees/tree-node/index.js +3 -0
  84. package/dist/trees/tree-node/tree-node.arb.d.ts +104 -0
  85. package/dist/trees/tree-node/tree-node.arb.d.ts.map +1 -0
  86. package/dist/trees/tree-node/tree-node.arb.js +191 -0
  87. package/dist/trees/tree-node/tree-node.impl.d.ts +15 -0
  88. package/dist/trees/tree-node/tree-node.impl.d.ts.map +1 -0
  89. package/dist/trees/tree-node/tree-node.impl.js +286 -0
  90. package/dist/trees/tree-node/tree-node.model.d.ts +78 -0
  91. package/dist/trees/tree-node/tree-node.model.d.ts.map +1 -0
  92. package/dist/trees/tree-node/tree-node.model.js +37 -0
  93. package/dist/trees/tree-node/tree-node.types.d.ts +419 -0
  94. package/dist/trees/tree-node/tree-node.types.d.ts.map +1 -0
  95. package/dist/trees/tree-node/tree-node.types.js +1 -0
  96. package/package.json +1 -1
  97. package/dist/tree-surfer/cursor.d.ts.map +0 -1
  98. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts +0 -2
  99. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts.map +0 -1
  100. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts +0 -4
  101. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts.map +0 -1
  102. package/dist/tree-surfer/fold-node/fold-node.impl.js +0 -149
  103. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts +0 -5
  104. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts.map +0 -1
  105. package/dist/tree-surfer/fold-node/fold-node.schema.js +0 -9
  106. package/dist/tree-surfer/fold-node/fold-node.types.d.ts.map +0 -1
  107. package/dist/tree-surfer/fold-node/tree-node.test.d.ts.map +0 -1
  108. package/dist/tree-surfer/fold-node/tree-node.test.js +0 -90
  109. package/dist/tree-surfer/index.d.ts +0 -13
  110. package/dist/tree-surfer/index.d.ts.map +0 -1
  111. package/dist/tree-surfer/index.js +0 -12
  112. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts +0 -6
  113. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts.map +0 -1
  114. package/dist/tree-surfer/nav-node/nav-node.types.d.ts +0 -70
  115. package/dist/tree-surfer/nav-node/nav-node.types.d.ts.map +0 -1
  116. package/dist/tree-surfer/state/state.impl.d.ts +0 -7
  117. package/dist/tree-surfer/state/state.impl.d.ts.map +0 -1
  118. package/dist/tree-surfer/state/state.impl.js +0 -89
  119. package/dist/tree-surfer/state/state.schemas.d.ts +0 -9
  120. package/dist/tree-surfer/state/state.schemas.d.ts.map +0 -1
  121. package/dist/tree-surfer/state/state.schemas.js +0 -8
  122. package/dist/tree-surfer/state/state.types.d.ts +0 -21
  123. package/dist/tree-surfer/state/state.types.d.ts.map +0 -1
  124. package/dist/tree-surfer/tree-node/tree-node.arb.d.ts.map +0 -1
  125. package/dist/tree-surfer/tree-node/tree-node.arb.debug.test.d.ts.map +0 -1
  126. package/dist/tree-surfer/tree-node/tree-node.impl.d.ts.map +0 -1
  127. package/dist/tree-surfer/tree-node/tree-node.schemas.d.ts.map +0 -1
  128. package/dist/tree-surfer/tree-node/tree-node.test.d.ts +0 -2
  129. package/dist/tree-surfer/tree-node/tree-node.test.d.ts.map +0 -1
  130. package/dist/tree-surfer/tree-node/tree-node.types.d.ts.map +0 -1
  131. package/dist/tree-surfer-ui/application/index.d.ts.map +0 -1
  132. package/dist/tree-surfer-ui/application/intent-to-effect-no-state.test.d.ts.map +0 -1
  133. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts +0 -10
  134. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts.map +0 -1
  135. package/dist/tree-surfer-ui/application/parse-command.d.ts +0 -4
  136. package/dist/tree-surfer-ui/application/parse-command.d.ts.map +0 -1
  137. package/dist/tree-surfer-ui/application/user-input-to-intent.d.ts.map +0 -1
  138. package/dist/tree-surfer-ui/application/user-input-to-intent.test.d.ts.map +0 -1
  139. package/dist/tree-surfer-ui/domain/effect.d.ts +0 -43
  140. package/dist/tree-surfer-ui/domain/effect.d.ts.map +0 -1
  141. package/dist/tree-surfer-ui/domain/index.d.ts.map +0 -1
  142. package/dist/tree-surfer-ui/domain/input-mode-state.d.ts.map +0 -1
  143. package/dist/tree-surfer-ui/domain/intent.d.ts.map +0 -1
  144. package/dist/tree-surfer-ui/domain/user-input.d.ts.map +0 -1
  145. /package/dist/{tree-surfer-ui/domain/effect.js → app/reducer-action.js} +0 -0
  146. /package/dist/{tree-surfer-ui/domain/input-mode-state.js → app/view-api.js} +0 -0
  147. /package/dist/{tree-surfer-ui → input}/application/index.d.ts +0 -0
  148. /package/dist/{tree-surfer-ui → input}/application/index.js +0 -0
  149. /package/dist/{tree-surfer-ui → input}/application/intent-to-effect-no-state.test.d.ts +0 -0
  150. /package/dist/{tree-surfer-ui → input}/application/intent-to-effect-no-state.test.js +0 -0
  151. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.d.ts +0 -0
  152. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.js +0 -0
  153. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.test.d.ts +0 -0
  154. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.test.js +0 -0
  155. /package/dist/{tree-surfer-ui/domain/intent.js → input/domain/effect.js} +0 -0
  156. /package/dist/{tree-surfer-ui → input}/domain/index.d.ts +0 -0
  157. /package/dist/{tree-surfer-ui → input}/domain/index.js +0 -0
  158. /package/dist/{tree-surfer-ui → input}/domain/input-mode-state.d.ts +0 -0
  159. /package/dist/{tree-surfer-ui/domain/user-input.js → input/domain/input-mode-state.js} +0 -0
  160. /package/dist/{tree-surfer-ui → input}/domain/intent.d.ts +0 -0
  161. /package/dist/{tree-surfer/fold-node/fold-node.arb.js → input/domain/intent.js} +0 -0
  162. /package/dist/{tree-surfer-ui → input}/domain/user-input.d.ts +0 -0
  163. /package/dist/{tree-surfer/fold-node/fold-node.types.js → input/domain/user-input.js} +0 -0
  164. /package/dist/{tree-surfer → trees}/cursor.d.ts +0 -0
  165. /package/dist/{tree-surfer → trees}/cursor.js +0 -0
  166. /package/dist/{tree-surfer/nav-node/nav-node.types.js → trees/fold-node/deprecated/fold-node.types.js} +0 -0
  167. /package/dist/{tree-surfer/state/state.types.js → trees/fold-node/fold-node.types.js} +0 -0
  168. /package/dist/{tree-surfer/tree-node/tree-node.types.js → trees/nav-node/nav-node.types.js} +0 -0
  169. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.d.ts +0 -0
  170. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.debug.test.d.ts +0 -0
  171. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.js +0 -0
  172. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.impl.d.ts +0 -0
  173. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.impl.js +0 -0
  174. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.schemas.d.ts +0 -0
  175. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.schemas.js +0 -0
  176. /package/dist/{tree-surfer/fold-node → trees/tree-node/deprecated}/tree-node.test.d.ts +0 -0
  177. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.test.js +0 -0
  178. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.types.d.ts +0 -0
@@ -0,0 +1,419 @@
1
+ import type { SerializableKey, TreeNodeModel } from './tree-node.model.js';
2
+ /**
3
+ * A private type-level marker used to make {@link TreeNode} opaque.
4
+ *
5
+ * @remarks
6
+ *
7
+ * This declaration intentionally has no runtime value: `declare` causes it
8
+ * to be erased when TypeScript emits JavaScript.
9
+ *
10
+ * WARNING: Brand must not be exported.
11
+ *
12
+ * The marker gives `TreeNode` a property whose key cannot be named by callers.
13
+ * Consequently, callers cannot construct a `TreeNode` using an ordinary
14
+ * object literal. The implementation module is responsible for constructing
15
+ * valid runtime values and exposing them through the public API.
16
+ *
17
+ * This is a compile-time abstraction boundary, not a security boundary.
18
+ * Consumers can still deliberately bypass it with `as unknown as TreeNode<…>`,
19
+ * just as they can bypass other TypeScript checks with assertions.
20
+ */
21
+ declare const treeNodeBrand: unique symbol;
22
+ /**
23
+ * An immutable tree node with application-defined data.
24
+ *
25
+ * @remarks
26
+ *
27
+ * `TreeNode` is intentionally opaque. Callers may hold, compare by reference,
28
+ * pass, store, and return nodes, but should use {@link TreeNodeApi} operations
29
+ * to create, inspect, and update them.
30
+ *
31
+ * In particular, callers do not depend on:
32
+ *
33
+ * - the runtime property used to store the node ID;
34
+ * - the runtime property used to store application data;
35
+ * - whether a node is represented as a leaf or a branch object;
36
+ * - how loaded child nodes are stored; or
37
+ * - how unloaded child state is represented internally.
38
+ *
39
+ * This allows the implementation to change its internal representation without
40
+ * requiring ordinary consumers to change.
41
+ *
42
+ * `TreeNode` values are intended to be immutable. Tree update operations
43
+ * return replacement nodes and do not mutate their inputs. This type does not,
44
+ * however, recursively freeze `Value`; applications should define their own
45
+ * value types with `readonly` properties when that is required.
46
+ *
47
+ * @typeParam Id - The type used to identify a node among its siblings.
48
+ * @typeParam Value - Application-owned data stored at each node.
49
+ *
50
+ * @example
51
+ *
52
+ * ```ts
53
+ * type MenuItem = Readonly<{
54
+ * label: string;
55
+ * href?: string;
56
+ * requiredRole: 'guest' | 'member' | 'admin';
57
+ * }>;
58
+ *
59
+ * type MenuTree = TreeNode<MenuId, MenuItem>;
60
+ * ```
61
+ *
62
+ * @example
63
+ *
64
+ * ```ts
65
+ * type DirectoryEntry = Readonly<{
66
+ * kind: 'file' | 'directory';
67
+ * name: string;
68
+ * sizeBytes?: number;
69
+ * }>;
70
+ *
71
+ * type DirectoryTree = TreeNode<DirectoryId, DirectoryEntry>;
72
+ * ```
73
+ */
74
+ export type TreeNode<Id extends SerializableKey, Value> = {
75
+ /**
76
+ * A phantom type-level witness for `Id` and `Value`.
77
+ *
78
+ * @remarks
79
+ *
80
+ * This property is not part of the intended runtime representation. Its
81
+ * purpose is to preserve the generic relationship between a tree node, its
82
+ * ID type, and its application-value type while keeping the node opaque.
83
+ */
84
+ readonly [treeNodeBrand]: {
85
+ readonly id: Id;
86
+ readonly value: Value;
87
+ };
88
+ };
89
+ /**
90
+ * The semantic child kind of tree nodes.
91
+ *
92
+ * @remarks
93
+ *
94
+ * A node is exactly one of these:
95
+ *
96
+ * - `leaf`: The node cannot contain children.
97
+ * - `unloaded-branch`: The node may contain children, but its child collection
98
+ * has not been loaded and is therefore unavailable.
99
+ * - `loaded-branch`: The node has a loaded child collection. The collection may
100
+ * be empty.
101
+ *
102
+ * A loaded branch with zero children is distinct from a leaf:
103
+ *
104
+ * - a leaf can never contain children;
105
+ * - an empty loaded branch can contain children, but currently has none;
106
+ * - an unloaded branch may contain children, but they are not currently loaded.
107
+ */
108
+ export type TreeNodeKind = 'leaf' | 'unloaded-branch' | 'loaded-branch';
109
+ /**
110
+ * Case handlers for inspecting an opaque {@link TreeNode}.
111
+ *
112
+ * @remarks
113
+ *
114
+ * `TreeNodeMatch` provides exhaustive pattern matching over the semantic state
115
+ * of a node. It is the preferred operation when behavior differs between
116
+ * leaves, unloaded branches, and loaded branches:
117
+ *
118
+ * - `leaf` receives no `children`, because a leaf cannot have children;
119
+ * - `unloadedBranch` receives no `children`, because its children are not loaded;
120
+ * - `loadedBranch` receives an iterable of loaded direct child nodes. No
121
+ * iteration-order guarantee is made.
122
+ *
123
+ * Every handler must return the same `Result` type.
124
+ *
125
+ * @typeParam Id - The type used to identify nodes among siblings.
126
+ * @typeParam Value - The application-specific value stored at each node.
127
+ * @typeParam Result - The common result type returned by all case handlers.
128
+ *
129
+ * @example
130
+ *
131
+ * ```ts
132
+ * const label = api.match(node, {
133
+ * leaf: ({ value }) => value.label,
134
+ *
135
+ * unloadedBranch: ({ value }) => `${value.label} (loading…)`,
136
+ *
137
+ * loadedBranch: ({ value, children }) => {
138
+ * let childCount = 0;
139
+ *
140
+ * for (const _child of children) {
141
+ * childCount += 1;
142
+ * }
143
+ *
144
+ * return `${value.label} (${childCount})`;
145
+ * },
146
+ * });
147
+ * ```
148
+ */
149
+ export type TreeNodeMatch<Id extends SerializableKey, Value, Result> = Readonly<{
150
+ leaf: (node: Readonly<{
151
+ id: Id;
152
+ value: Value;
153
+ }>) => Result;
154
+ unloadedBranch: (node: Readonly<{
155
+ id: Id;
156
+ value: Value;
157
+ }>) => Result;
158
+ loadedBranch: (node: Readonly<{
159
+ id: Id;
160
+ value: Value;
161
+ children: Iterable<TreeNode<Id, Value>>;
162
+ }>) => Result;
163
+ }>;
164
+ /**
165
+ * Operations for constructing, inspecting, and immutably updating tree nodes.
166
+ *
167
+ * @remarks
168
+ *
169
+ * A `TreeNodeApi` is specialized to one ID type and one application-value
170
+ * type. It owns the structural tree rules: leaf/branch state, child loading
171
+ * state, sibling-ID uniqueness, and immutable ancestor reconstruction.
172
+ *
173
+ * Child collections are unordered. Although operations expose children as
174
+ * `Iterable` values, callers must not rely on iteration order being stable,
175
+ * meaningful, or related to insertion order. The implementation may use an
176
+ * array, map, set, indexed store, or another representation internally.
177
+ *
178
+ * Tree nodes are plain immutable values at runtime. Update operations do not
179
+ * mutate supplied nodes or their loaded child collections. When an update
180
+ * succeeds, only nodes and child collections on the updated path need to be
181
+ * recreated; unrelated parts of the tree may retain their original object
182
+ * identity.
183
+ *
184
+ * A path is an array of IDs relative to a supplied root node. For example,
185
+ * given this tree:
186
+ *
187
+ * ```text
188
+ * root
189
+ * └── child
190
+ * ```
191
+ *
192
+ * the root is addressed by `[]`, and `child` is addressed by `['child']`.
193
+ *
194
+ * Paths cannot pass through leaves or closed branches. A closed branch may be
195
+ * selected as the final node in a path, but its unloaded descendants cannot be
196
+ * addressed.
197
+ *
198
+ * @typeParam Id - The type used to identify nodes among siblings.
199
+ * @typeParam Value - The application-specific value stored at each node.
200
+ */
201
+ export type TreeNodeApi<Id extends SerializableKey, Value> = Readonly<{
202
+ /**
203
+ * Creates a leaf node.
204
+ *
205
+ * A leaf cannot contain children. To convert an existing node to a leaf while
206
+ * retaining its ID and value, use {@link TreeNodeApi.toLeaf}.
207
+ *
208
+ * @param id - The ID that identifies this node among its siblings.
209
+ * @param value - Application-owned data stored on the node.
210
+ * @returns A new leaf node.
211
+ */
212
+ createLeaf(id: Id, value: Value): TreeNode<Id, Value>;
213
+ /**
214
+ * Creates a branch whose children are not loaded.
215
+ *
216
+ * An unloaded branch may contain children, but its child collection is not
217
+ * currently available. It is distinct from both a leaf and a loaded branch
218
+ * with an empty child collection.
219
+ */
220
+ createUnloadedBranch(id: Id, value: Value): TreeNode<Id, Value>;
221
+ /**
222
+ * Creates a branch with a loaded child collection.
223
+ *
224
+ * An empty child collection creates a loaded branch with no current children;
225
+ * it does not create a leaf.
226
+ */
227
+ createLoadedBranch(id: Id, value: Value, children: Iterable<TreeNode<Id, Value>>): TreeNode<Id, Value> | undefined;
228
+ /**
229
+ * Returns a node's stable sibling ID.
230
+ *
231
+ * @param node - The node to inspect.
232
+ * @returns The ID identifying `node` among its siblings.
233
+ */
234
+ id(node: TreeNode<Id, Value>): Id;
235
+ /**
236
+ * Returns application-owned data stored on a node.
237
+ *
238
+ * The value is returned by reference and is not cloned. Callers must not
239
+ * mutate it if the application treats tree nodes as immutable values.
240
+ *
241
+ * @param node - The node to inspect.
242
+ * @returns The value associated with `node`.
243
+ */
244
+ value(node: TreeNode<Id, Value>): Value;
245
+ /**
246
+ * Returns the semantic kind of a node.
247
+ *
248
+ * Prefer {@link TreeNodeApi.match} when behavior differs by node kind and all
249
+ * three variants must be handled.
250
+ *
251
+ * @param node - The node to inspect.
252
+ * @returns Whether `node` is a leaf, unloaded branch, or loaded branch.
253
+ */
254
+ kind(node: TreeNode<Id, Value>): TreeNodeKind;
255
+ /**
256
+ * Inspects a node by handling its semantic state exhaustively.
257
+ *
258
+ * @param node - The node to inspect.
259
+ * @param handlers - Case handlers for leaf, closed-branch, and open-branch
260
+ * states.
261
+ * @returns The value returned by the selected case handler.
262
+ */
263
+ match<Result>(node: TreeNode<Id, Value>, handlers: TreeNodeMatch<Id, Value, Result>): Result;
264
+ /**
265
+ * Returns the loaded direct children of a loaded branch.
266
+ *
267
+ * The returned child nodes are existing values, not defensive clones. No
268
+ * iteration-order guarantee is made.
269
+ *
270
+ * Returns `undefined` when `node` is a leaf or an unloaded branch. Use
271
+ * {@link TreeNodeApi.kind} or {@link TreeNodeApi.match} when those cases need
272
+ * to be distinguished.
273
+ *
274
+ * @param node - The node whose direct children to inspect.
275
+ * @returns An iterable of loaded direct children, or `undefined` when children
276
+ * are unavailable because `node` is a leaf or closed branch.
277
+ */
278
+ getLoadedChildren(node: TreeNode<Id, Value>): Iterable<TreeNode<Id, Value>> | undefined;
279
+ /**
280
+ * Finds a loaded direct child by sibling ID.
281
+ *
282
+ * The search is performed only among direct children. It does not traverse
283
+ * descendants.
284
+ *
285
+ * @param node - The node whose loaded direct children to search.
286
+ * @param id - The sibling ID to find.
287
+ * @returns The existing matching child, or `undefined` when `node` is not an
288
+ * open branch or does not have a direct child with `id`.
289
+ */
290
+ findChildById(node: TreeNode<Id, Value>, id: Id): TreeNode<Id, Value> | undefined;
291
+ /**
292
+ * Returns an equivalent node with replacement application data.
293
+ *
294
+ * The returned node retains the original node's ID, semantic child state,
295
+ * and when applicable, existing child-node references. The supplied node is
296
+ * not mutated.
297
+ *
298
+ * @param node - The node to update.
299
+ * @param value - Replacement application-owned data.
300
+ * @returns A replacement node with `value`.
301
+ */
302
+ withValue(node: TreeNode<Id, Value>, value: Value): TreeNode<Id, Value>;
303
+ /**
304
+ * Converts a node to a leaf.
305
+ *
306
+ * The returned node retains the source node's ID and application value.
307
+ * Any existing children, loaded or unloaded, are discarded.
308
+ *
309
+ * @param node - The node to convert.
310
+ * @returns A leaf node with the source node's ID and value.
311
+ */
312
+ toLeaf(node: TreeNode<Id, Value>): TreeNode<Id, Value>;
313
+ /**
314
+ * Converts a node to a branch with unloaded children.
315
+ *
316
+ * The returned node retains the source node's ID and application value.
317
+ * Any currently loaded child collection is discarded.
318
+ *
319
+ * @param node - The node to convert.
320
+ * @returns A closed branch with the source node's ID and value.
321
+ */
322
+ toUnloadedBranch(node: TreeNode<Id, Value>): TreeNode<Id, Value>;
323
+ /**
324
+ * Converts a node to an open branch with replacement loaded children.
325
+ *
326
+ * The returned node retains the source node's ID and application value.
327
+ * Any previous child state is replaced.
328
+ *
329
+ * Child collections are unordered. Callers must not rely on the iteration order
330
+ * of the supplied children being retained or exposed by the resulting node.
331
+ *
332
+ * Direct siblings must have unique IDs. If two or more supplied children have
333
+ * the same ID, no replacement node is created and this method returns
334
+ * `undefined`.
335
+ *
336
+ * The supplied node and child collection are not mutated.
337
+ *
338
+ * @param node - The node to convert or update.
339
+ * @param children - Replacement loaded direct children. Their IDs must be
340
+ * unique among direct siblings.
341
+ * @returns An open branch with the source node's ID and value, or `undefined`
342
+ * when `children` contains duplicate sibling IDs.
343
+ */
344
+ withLoadedChildren(node: TreeNode<Id, Value>, children: Iterable<TreeNode<Id, Value>>): TreeNode<Id, Value> | undefined;
345
+ /**
346
+ * Finds the node addressed by a path relative to `root`.
347
+ *
348
+ * An empty path selects `root`. Resolution fails when a path refers to a
349
+ * missing child or attempts to pass through a leaf or unloaded branch.
350
+ *
351
+ * @param root - The root node relative to which `path` is interpreted.
352
+ * @param path - A sibling-ID path relative to `root`.
353
+ * @returns The existing node at `path`, or `undefined` when the path cannot
354
+ * be resolved.
355
+ */
356
+ findAtPath(root: TreeNode<Id, Value>, path: readonly Id[]): TreeNode<Id, Value> | undefined;
357
+ /**
358
+ * Applies a selector to the node addressed by a path relative to `root`.
359
+ *
360
+ * The selector runs only after the complete path has been resolved. The
361
+ * selected node is supplied as the existing node value and is not cloned.
362
+ *
363
+ * An empty path selects `root`. Resolution fails when a path refers to a
364
+ * missing child or attempts to pass through a leaf or a closed branch.
365
+ *
366
+ * @param root - The root node relative to which `path` is interpreted.
367
+ * @param path - A sibling-ID path relative to `root`.
368
+ * @param selector - A function applied to the resolved node.
369
+ * @returns The selector result, or `undefined` when the path cannot be
370
+ * resolved or when `selector` itself returns `undefined`.
371
+ */
372
+ selectAtPath<Result>(root: TreeNode<Id, Value>, path: readonly Id[], selector: (node: TreeNode<Id, Value>) => Result): Result | undefined;
373
+ /**
374
+ * Immutably replaces the node addressed by a path relative to `root`.
375
+ *
376
+ * The modifier runs only after the complete path has been resolved. Returning
377
+ * `undefined` aborts the operation and causes this method to return
378
+ * `undefined`.
379
+ *
380
+ * An empty path selects `root`. When a descendant is replaced, replacement
381
+ * nodes are created only along the path from `root` to that descendant. Nodes
382
+ * outside that path retain their original object identity.
383
+ *
384
+ * The modifier may return a node with a different ID. If it does, the
385
+ * replacement is subsequently addressed by its new ID rather than the old
386
+ * path segment.
387
+ *
388
+ * The update also fails when the replacement node's ID duplicates the ID of
389
+ * one of its new siblings. This can occur when `modifier` returns a node with
390
+ * a different ID.
391
+ *
392
+ * @param root - The root node relative to which `path` is interpreted.
393
+ * @param path - A sibling-ID path relative to `root`.
394
+ * @param update - Produces a replacement node, or `undefined` to abort.
395
+ * @returns A replacement root, or `undefined` when the path cannot be
396
+ * resolved, `modifier` aborts, or the replacement would create duplicate
397
+ * sibling IDs.
398
+ */
399
+ updateAtPath(root: TreeNode<Id, Value>, path: readonly Id[], update: (node: TreeNode<Id, Value>) => TreeNode<Id, Value> | undefined): TreeNode<Id, Value> | undefined;
400
+ /**
401
+ * Converts an opaque runtime node into a public structural model.
402
+ *
403
+ * The returned model is a fresh recursive snapshot. It is suitable for
404
+ * serialization when `Id` and `Value` are serializable.
405
+ */
406
+ toModel(node: TreeNode<Id, Value>): TreeNodeModel<Id, Value>;
407
+ /**
408
+ * Constructs an opaque tree from a public structural model.
409
+ *
410
+ * Returns `undefined` if any loaded child collection contains duplicate
411
+ * sibling IDs.
412
+ *
413
+ * This function assumes the model has already been validated as data of the
414
+ * expected shape. Use a schema/decoder at untrusted boundaries.
415
+ */
416
+ fromModel(model: TreeNodeModel<Id, Value>): TreeNode<Id, Value> | undefined;
417
+ }>;
418
+ export {};
419
+ //# sourceMappingURL=tree-node.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-node.types.d.ts","sourceRoot":"","sources":["../../../src/trees/tree-node/tree-node.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE3E;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,CAAC,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,MAAM,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI;IACxD;;;;;;;;OAQG;IACH,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE;QACxB,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;QAChB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;KACvB,CAAC;CACH,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,iBAAiB,GAAG,eAAe,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EAAE,MAAM,IAAI,QAAQ,CAAC;IAC9E,IAAI,EAAE,CACJ,IAAI,EAAE,QAAQ,CAAC;QACb,EAAE,EAAE,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC;KACd,CAAC,KACC,MAAM,CAAC;IAEZ,cAAc,EAAE,CACd,IAAI,EAAE,QAAQ,CAAC;QACb,EAAE,EAAE,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC;KACd,CAAC,KACC,MAAM,CAAC;IAEZ,YAAY,EAAE,CACZ,IAAI,EAAE,QAAQ,CAAC;QACb,EAAE,EAAE,EAAE,CAAC;QACP,KAAK,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;KACzC,CAAC,KACC,MAAM,CAAC;CACb,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,QAAQ,CAAC;IACpE;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAEtD;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAEhE;;;;;OAKG;IACH,kBAAkB,CAChB,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,GACtC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;;OAKG;IACH,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAElC;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC;IAExC;;;;;;;;OAQG;IACH,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC;IAE9C;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,EACV,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,QAAQ,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GACzC,MAAM,CAAC;IAEV;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CACf,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC;IAE7C;;;;;;;;;;OAUG;IACH,aAAa,CACX,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,EAAE,EAAE,EAAE,GACL,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAExE;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAEvD;;;;;;;;OAQG;IACH,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAEjE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,kBAAkB,CAChB,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,GACtC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;;;;;;;OAUG;IACH,UAAU,CACR,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,IAAI,EAAE,SAAS,EAAE,EAAE,GAClB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;;;;;;;;;;;OAcG;IACH,YAAY,CAAC,MAAM,EACjB,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,MAAM,GAC9C,MAAM,GAAG,SAAS,CAAC;IAEtB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,YAAY,CACV,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,EACzB,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,GACrE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAE7D;;;;;;;;OAQG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC;CAC7E,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dirvi-lib",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Dependency for dirvi apps.",
5
5
  "license": "LGPL-3.0-or-later",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/tree-surfer/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,MAAM,CAAC,EAAE,IAAI,SAAS,EAAE,EAAE,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,CAAC,CAAC,UAAU,EAC9D,QAAQ,EAAE,QAAQ,wBAGnB;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,EAAE,IAAI;IAC1B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IAEpD;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;IAElC;;;;;OAKG;IACH,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC;CAC/E,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,CAAC,CAcnD"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=fold-node.arb.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fold-node.arb.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/fold-node/fold-node.arb.ts"],"names":[],"mappings":""}
@@ -1,4 +0,0 @@
1
- import type { FoldNodeApi, FoldNodeService } from './fold-node.types.js';
2
- import type { SerializableKey } from '../tree-node/tree-node.types.js';
3
- export declare function createFoldNodeService<Id extends SerializableKey>(foldNodeApi: FoldNodeApi<Id>): FoldNodeService<Id>;
4
- //# sourceMappingURL=fold-node.impl.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fold-node.impl.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/fold-node/fold-node.impl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EACX,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAgBvE,wBAAgB,qBAAqB,CAAC,EAAE,SAAS,eAAe,EAC9D,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,GAC3B,eAAe,CAAC,EAAE,CAAC,CAwNrB"}
@@ -1,149 +0,0 @@
1
- function getFoldedChildById(node, id) {
2
- return node.foldedChildren.find((child) => child.id === id);
3
- }
4
- function hasFoldedChildWithId(node, id) {
5
- return getFoldedChildById(node, id) !== undefined;
6
- }
7
- export function createFoldNodeService(foldNodeApi) {
8
- function createEmptyNode(id) {
9
- return {
10
- id,
11
- children: [],
12
- foldedChildren: [],
13
- };
14
- }
15
- return {
16
- createEmptyNode,
17
- getIfEntryFoldedAtPath(rootNode, path, entryId) {
18
- const node = foldNodeApi.getAtPath(rootNode, path, (candidate) => candidate);
19
- return node !== undefined && hasFoldedChildWithId(node, entryId);
20
- },
21
- foldedEntriesAtPath(rootNode, path) {
22
- const node = foldNodeApi.getAtPath(rootNode, path, (candidate) => candidate);
23
- return node?.foldedChildren.map((child) => child.id);
24
- },
25
- addFoldedEntryAtPath(rootNode, path, entryId) {
26
- const rootWithPath = ensurePath(rootNode, path);
27
- if (rootWithPath === undefined) {
28
- return undefined;
29
- }
30
- return foldNodeApi.modifyAtPath(rootWithPath, path, (node) => addFoldedEntry(node, entryId));
31
- },
32
- removeFoldedEntryAtPath(rootNode, path, entryId) {
33
- return foldNodeApi.modifyAtPath(rootNode, path, (node) => removeFoldedEntry(node, entryId));
34
- },
35
- clearFoldedEntriesAtPath(rootNode, path) {
36
- return foldNodeApi.modifyAtPath(rootNode, path, (node) => clearFoldedEntries(node));
37
- },
38
- };
39
- // Node-local operations.
40
- //
41
- // These functions update only the selected node. Path traversal and immutable
42
- // ancestor updates are handled by FoldNodeApi.
43
- function addFoldedEntry(node, entryId) {
44
- const existingFoldedChild = getFoldedChildById(node, entryId);
45
- if (existingFoldedChild !== undefined) {
46
- return node;
47
- }
48
- const childIndex = node.children.findIndex((child) => child.id === entryId);
49
- const child = childIndex === -1 ? createEmptyNode(entryId) : node.children[childIndex];
50
- const children = childIndex === -1
51
- ? node.children
52
- : node.children.filter((_, index) => index !== childIndex);
53
- return {
54
- ...node,
55
- children,
56
- foldedChildren: [...node.foldedChildren, child],
57
- };
58
- }
59
- function removeFoldedEntry(node, entryId) {
60
- const foldedChildIndex = node.foldedChildren.findIndex((child) => child.id === entryId);
61
- if (foldedChildIndex === -1) {
62
- return node;
63
- }
64
- const foldedChild = node.foldedChildren[foldedChildIndex];
65
- const foldedChildren = node.foldedChildren.filter((_, index) => index !== foldedChildIndex);
66
- const hasNestedFoldState = foldedChild.children.length > 0 || foldedChild.foldedChildren.length > 0;
67
- return {
68
- ...node,
69
- children: hasNestedFoldState
70
- ? [...node.children, foldedChild]
71
- : node.children,
72
- foldedChildren,
73
- };
74
- }
75
- function clearFoldedEntries(node) {
76
- if (node.foldedChildren.length === 0) {
77
- return node;
78
- }
79
- const children = [...node.children];
80
- for (const foldedChild of node.foldedChildren) {
81
- const hasNestedFoldState = foldedChild.children.length > 0 ||
82
- foldedChild.foldedChildren.length > 0;
83
- if (hasNestedFoldState) {
84
- children.push(foldedChild);
85
- }
86
- }
87
- return {
88
- ...node,
89
- children,
90
- foldedChildren: [],
91
- };
92
- }
93
- /**
94
- * Ensures that the requested path exists in the fold-state tree.
95
- *
96
- * The path is relative to `rootNode`; an empty path selects the root.
97
- * This creates fold nodes only and does not modify the buffer tree.
98
- */
99
- function ensurePath(rootNode, path) {
100
- if (path.length === 0) {
101
- return rootNode;
102
- }
103
- const children = ensureChildPath(rootNode, path, 0);
104
- if (children === undefined) {
105
- return undefined;
106
- }
107
- if (children === rootNode.children) {
108
- return rootNode;
109
- }
110
- return {
111
- ...rootNode,
112
- children,
113
- };
114
- }
115
- function ensureChildPath(parent, path, pathIndex) {
116
- const id = path[pathIndex];
117
- if (id === undefined) {
118
- return parent.children;
119
- }
120
- if (hasFoldedChildWithId(parent, id)) {
121
- return undefined;
122
- }
123
- const children = parent.children;
124
- const childIndex = children.findIndex((child) => child.id === id);
125
- const child = childIndex === -1 ? createEmptyNode(id) : children[childIndex];
126
- let updatedChild = child;
127
- if (pathIndex < path.length - 1) {
128
- const updatedChildren = ensureChildPath(child, path, pathIndex + 1);
129
- if (updatedChildren === undefined) {
130
- return undefined;
131
- }
132
- if (updatedChildren !== child.children) {
133
- updatedChild = {
134
- ...child,
135
- children: updatedChildren,
136
- };
137
- }
138
- }
139
- if (childIndex === -1) {
140
- return [...children, updatedChild];
141
- }
142
- if (updatedChild === child) {
143
- return children;
144
- }
145
- const result = [...children];
146
- result[childIndex] = updatedChild;
147
- return result;
148
- }
149
- }
@@ -1,5 +0,0 @@
1
- import { z } from 'zod';
2
- import type { FoldNode } from './fold-node.types.js';
3
- import { SerializableKey } from '../tree-node/tree-node.types.js';
4
- export declare function createFoldNodeSchemas<Id extends SerializableKey>(idSchema: z.ZodType<Id>): z.ZodType<FoldNode<Id>>;
5
- //# sourceMappingURL=fold-node.schema.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fold-node.schema.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/fold-node/fold-node.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,wBAAgB,qBAAqB,CAAC,EAAE,SAAS,eAAe,EAC9D,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,GACtB,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAYzB"}
@@ -1,9 +0,0 @@
1
- import { z } from 'zod';
2
- export function createFoldNodeSchemas(idSchema) {
3
- const foldNodeSchema = z.lazy(() => z.object({
4
- id: idSchema,
5
- children: z.array(foldNodeSchema),
6
- foldedChildren: z.array(foldNodeSchema),
7
- }));
8
- return foldNodeSchema;
9
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"fold-node.types.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/fold-node/fold-node.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,MAAM,QAAQ,CAAC,EAAE,SAAS,eAAe,IAAI;IACjD,EAAE,EAAE,EAAE,CAAC;IACP,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IACzB,cAAc,EAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,SAAS,eAAe,IAAI,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,eAAe,CAAC,EAAE,SAAS,eAAe,IAAI;IACxD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,EAAE,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEtC;;;;;;;;;;;;;;OAcG;IACH,sBAAsB,CACpB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,OAAO,EAAE,EAAE,GACV,OAAO,CAAC;IAEX;;;;OAIG;IACH,mBAAmB,CACjB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,GAClB,EAAE,EAAE,GAAG,SAAS,CAAC;IAEpB;;;;;;;;;;;;;;;;;OAiBG;IACH,oBAAoB,CAClB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,OAAO,EAAE,EAAE,GACV,QAAQ,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IAE5B;;;;;;;;;;;;;;;;;OAiBG;IACH,uBAAuB,CACrB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,EACnB,OAAO,EAAE,EAAE,GACV,QAAQ,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IAE5B;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAAwB,CACtB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,GAClB,QAAQ,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;IAC5B,wBAAwB,CACtB,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,EACtB,IAAI,EAAE,SAAS,EAAE,EAAE,GAClB,QAAQ,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;CAC7B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tree-node.test.d.ts","sourceRoot":"","sources":["../../../src/tree-surfer/fold-node/tree-node.test.ts"],"names":[],"mappings":""}