dirvi-lib 0.8.1 → 0.9.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.
Files changed (194) 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 +120 -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 +40 -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 +115 -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/feedback/feedback.impl.d.ts +9 -0
  18. package/dist/feedback/feedback.impl.d.ts.map +1 -0
  19. package/dist/feedback/feedback.impl.js +34 -0
  20. package/dist/feedback/feedback.types.d.ts +24 -0
  21. package/dist/feedback/feedback.types.d.ts.map +1 -0
  22. package/dist/feedback/index.d.ts +3 -0
  23. package/dist/feedback/index.d.ts.map +1 -0
  24. package/dist/feedback/index.js +1 -0
  25. package/dist/index.d.ts +5 -7
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -7
  28. package/dist/input/application/index.d.ts.map +1 -0
  29. package/dist/input/application/intent-to-effect-no-state.test.d.ts.map +1 -0
  30. package/dist/input/application/intent-to-effect.d.ts +14 -0
  31. package/dist/input/application/intent-to-effect.d.ts.map +1 -0
  32. package/dist/{tree-surfer-ui → input}/application/intent-to-effect.js +66 -42
  33. package/dist/input/application/parse-command.d.ts +14 -0
  34. package/dist/input/application/parse-command.d.ts.map +1 -0
  35. package/dist/input/application/parse-command.js +92 -0
  36. package/dist/input/application/parse-normal-command.d.ts +12 -0
  37. package/dist/input/application/parse-normal-command.d.ts.map +1 -0
  38. package/dist/input/application/parse-normal-command.js +89 -0
  39. package/dist/input/application/tokenize-normal-command.d.ts +24 -0
  40. package/dist/input/application/tokenize-normal-command.d.ts.map +1 -0
  41. package/dist/input/application/tokenize-normal-command.js +110 -0
  42. package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.d.ts +4 -2
  43. package/dist/input/application/user-input-to-intent.d.ts.map +1 -0
  44. package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.js +9 -6
  45. package/dist/input/application/user-input-to-intent.test.d.ts.map +1 -0
  46. package/dist/input/domain/effect.d.ts +84 -0
  47. package/dist/input/domain/effect.d.ts.map +1 -0
  48. package/dist/input/domain/index.d.ts.map +1 -0
  49. package/dist/input/domain/input-mode-state.d.ts.map +1 -0
  50. package/dist/input/domain/intent.d.ts.map +1 -0
  51. package/dist/input/domain/user-input.d.ts.map +1 -0
  52. package/dist/trees/cursor.d.ts.map +1 -0
  53. package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts +10 -0
  54. package/dist/trees/fold-node/deprecated/fold-node.impl.d.ts.map +1 -0
  55. package/dist/trees/fold-node/deprecated/fold-node.impl.js +248 -0
  56. package/dist/trees/fold-node/deprecated/fold-node.test.d.ts +2 -0
  57. package/dist/trees/fold-node/deprecated/fold-node.test.d.ts.map +1 -0
  58. package/dist/trees/fold-node/deprecated/fold-node.test.js +130 -0
  59. package/dist/{tree-surfer/fold-node → trees/fold-node/deprecated}/fold-node.types.d.ts +15 -76
  60. package/dist/trees/fold-node/deprecated/fold-node.types.d.ts.map +1 -0
  61. package/dist/trees/fold-node/fold-node.impl.d.ts +16 -0
  62. package/dist/trees/fold-node/fold-node.impl.d.ts.map +1 -0
  63. package/dist/trees/fold-node/fold-node.impl.js +472 -0
  64. package/dist/trees/fold-node/fold-node.model.d.ts +120 -0
  65. package/dist/trees/fold-node/fold-node.model.d.ts.map +1 -0
  66. package/dist/trees/fold-node/fold-node.model.js +80 -0
  67. package/dist/trees/fold-node/fold-node.types.d.ts +214 -0
  68. package/dist/trees/fold-node/fold-node.types.d.ts.map +1 -0
  69. package/dist/trees/fold-node/index.d.ts +4 -0
  70. package/dist/trees/fold-node/index.d.ts.map +1 -0
  71. package/dist/trees/fold-node/index.js +3 -0
  72. package/dist/trees/index.d.ts +9 -0
  73. package/dist/trees/index.d.ts.map +1 -0
  74. package/dist/trees/index.js +8 -0
  75. package/dist/trees/nav-node/nav-node.impl.d.ts +29 -0
  76. package/dist/trees/nav-node/nav-node.impl.d.ts.map +1 -0
  77. package/dist/{tree-surfer → trees}/nav-node/nav-node.impl.js +89 -42
  78. package/dist/trees/nav-node/nav-node.types.d.ts +103 -0
  79. package/dist/trees/nav-node/nav-node.types.d.ts.map +1 -0
  80. package/dist/trees/nav-node/nav-node.types.js +1 -0
  81. package/dist/trees/state/state.impl.d.ts +8 -0
  82. package/dist/trees/state/state.impl.d.ts.map +1 -0
  83. package/dist/trees/state/state.impl.js +128 -0
  84. package/dist/trees/state/state.schemas.d.ts +25 -0
  85. package/dist/trees/state/state.schemas.d.ts.map +1 -0
  86. package/dist/trees/state/state.schemas.js +17 -0
  87. package/dist/trees/state/state.types.d.ts +36 -0
  88. package/dist/trees/state/state.types.d.ts.map +1 -0
  89. package/dist/trees/state/state.types.js +1 -0
  90. package/dist/trees/tree-node/deprecated/tree-node.arb.d.ts.map +1 -0
  91. package/dist/trees/tree-node/deprecated/tree-node.arb.debug.test.d.ts.map +1 -0
  92. package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.debug.test.js +1 -1
  93. package/dist/trees/tree-node/deprecated/tree-node.impl.d.ts.map +1 -0
  94. package/dist/trees/tree-node/deprecated/tree-node.schemas.d.ts.map +1 -0
  95. package/dist/trees/tree-node/deprecated/tree-node.test.d.ts.map +1 -0
  96. package/dist/trees/tree-node/deprecated/tree-node.types.d.ts.map +1 -0
  97. package/dist/trees/tree-node/deprecated/tree-node.types.js +1 -0
  98. package/dist/trees/tree-node/index.d.ts +5 -0
  99. package/dist/trees/tree-node/index.d.ts.map +1 -0
  100. package/dist/trees/tree-node/index.js +2 -0
  101. package/dist/trees/tree-node/tree-node.arb.d.ts +104 -0
  102. package/dist/trees/tree-node/tree-node.arb.d.ts.map +1 -0
  103. package/dist/trees/tree-node/tree-node.arb.js +191 -0
  104. package/dist/trees/tree-node/tree-node.impl.d.ts +15 -0
  105. package/dist/trees/tree-node/tree-node.impl.d.ts.map +1 -0
  106. package/dist/trees/tree-node/tree-node.impl.js +286 -0
  107. package/dist/trees/tree-node/tree-node.model.d.ts +78 -0
  108. package/dist/trees/tree-node/tree-node.model.d.ts.map +1 -0
  109. package/dist/trees/tree-node/tree-node.model.js +37 -0
  110. package/dist/trees/tree-node/tree-node.types.d.ts +419 -0
  111. package/dist/trees/tree-node/tree-node.types.d.ts.map +1 -0
  112. package/dist/trees/tree-node/tree-node.types.js +1 -0
  113. package/package.json +1 -1
  114. package/dist/tree-surfer/cursor.d.ts.map +0 -1
  115. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts +0 -2
  116. package/dist/tree-surfer/fold-node/fold-node.arb.d.ts.map +0 -1
  117. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts +0 -4
  118. package/dist/tree-surfer/fold-node/fold-node.impl.d.ts.map +0 -1
  119. package/dist/tree-surfer/fold-node/fold-node.impl.js +0 -149
  120. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts +0 -5
  121. package/dist/tree-surfer/fold-node/fold-node.schema.d.ts.map +0 -1
  122. package/dist/tree-surfer/fold-node/fold-node.schema.js +0 -9
  123. package/dist/tree-surfer/fold-node/fold-node.types.d.ts.map +0 -1
  124. package/dist/tree-surfer/fold-node/tree-node.test.d.ts.map +0 -1
  125. package/dist/tree-surfer/fold-node/tree-node.test.js +0 -90
  126. package/dist/tree-surfer/index.d.ts +0 -13
  127. package/dist/tree-surfer/index.d.ts.map +0 -1
  128. package/dist/tree-surfer/index.js +0 -12
  129. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts +0 -6
  130. package/dist/tree-surfer/nav-node/nav-node.impl.d.ts.map +0 -1
  131. package/dist/tree-surfer/nav-node/nav-node.types.d.ts +0 -70
  132. package/dist/tree-surfer/nav-node/nav-node.types.d.ts.map +0 -1
  133. package/dist/tree-surfer/state/state.impl.d.ts +0 -7
  134. package/dist/tree-surfer/state/state.impl.d.ts.map +0 -1
  135. package/dist/tree-surfer/state/state.impl.js +0 -89
  136. package/dist/tree-surfer/state/state.schemas.d.ts +0 -9
  137. package/dist/tree-surfer/state/state.schemas.d.ts.map +0 -1
  138. package/dist/tree-surfer/state/state.schemas.js +0 -8
  139. package/dist/tree-surfer/state/state.types.d.ts +0 -21
  140. package/dist/tree-surfer/state/state.types.d.ts.map +0 -1
  141. package/dist/tree-surfer/tree-node/tree-node.arb.d.ts.map +0 -1
  142. package/dist/tree-surfer/tree-node/tree-node.arb.debug.test.d.ts.map +0 -1
  143. package/dist/tree-surfer/tree-node/tree-node.impl.d.ts.map +0 -1
  144. package/dist/tree-surfer/tree-node/tree-node.schemas.d.ts.map +0 -1
  145. package/dist/tree-surfer/tree-node/tree-node.test.d.ts +0 -2
  146. package/dist/tree-surfer/tree-node/tree-node.test.d.ts.map +0 -1
  147. package/dist/tree-surfer/tree-node/tree-node.types.d.ts.map +0 -1
  148. package/dist/tree-surfer-ui/application/index.d.ts.map +0 -1
  149. package/dist/tree-surfer-ui/application/intent-to-effect-no-state.test.d.ts.map +0 -1
  150. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts +0 -10
  151. package/dist/tree-surfer-ui/application/intent-to-effect.d.ts.map +0 -1
  152. package/dist/tree-surfer-ui/application/parse-command.d.ts +0 -4
  153. package/dist/tree-surfer-ui/application/parse-command.d.ts.map +0 -1
  154. package/dist/tree-surfer-ui/application/parse-command.js +0 -48
  155. package/dist/tree-surfer-ui/application/user-input-to-intent.d.ts.map +0 -1
  156. package/dist/tree-surfer-ui/application/user-input-to-intent.test.d.ts.map +0 -1
  157. package/dist/tree-surfer-ui/domain/effect.d.ts +0 -43
  158. package/dist/tree-surfer-ui/domain/effect.d.ts.map +0 -1
  159. package/dist/tree-surfer-ui/domain/index.d.ts.map +0 -1
  160. package/dist/tree-surfer-ui/domain/input-mode-state.d.ts.map +0 -1
  161. package/dist/tree-surfer-ui/domain/intent.d.ts.map +0 -1
  162. package/dist/tree-surfer-ui/domain/user-input.d.ts.map +0 -1
  163. /package/dist/{tree-surfer-ui/domain/effect.js → app/reducer-action.js} +0 -0
  164. /package/dist/{tree-surfer-ui/domain/input-mode-state.js → app/view-api.js} +0 -0
  165. /package/dist/{tree-surfer-ui/domain/intent.js → feedback/feedback.types.js} +0 -0
  166. /package/dist/{tree-surfer-ui → input}/application/index.d.ts +0 -0
  167. /package/dist/{tree-surfer-ui → input}/application/index.js +0 -0
  168. /package/dist/{tree-surfer-ui → input}/application/intent-to-effect-no-state.test.d.ts +0 -0
  169. /package/dist/{tree-surfer-ui → input}/application/intent-to-effect-no-state.test.js +0 -0
  170. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.test.d.ts +0 -0
  171. /package/dist/{tree-surfer-ui → input}/application/user-input-to-intent.test.js +0 -0
  172. /package/dist/{tree-surfer-ui/domain/user-input.js → input/domain/effect.js} +0 -0
  173. /package/dist/{tree-surfer-ui → input}/domain/index.d.ts +0 -0
  174. /package/dist/{tree-surfer-ui → input}/domain/index.js +0 -0
  175. /package/dist/{tree-surfer-ui → input}/domain/input-mode-state.d.ts +0 -0
  176. /package/dist/{tree-surfer/fold-node/fold-node.arb.js → input/domain/input-mode-state.js} +0 -0
  177. /package/dist/{tree-surfer-ui → input}/domain/intent.d.ts +0 -0
  178. /package/dist/{tree-surfer/fold-node/fold-node.types.js → input/domain/intent.js} +0 -0
  179. /package/dist/{tree-surfer-ui → input}/domain/user-input.d.ts +0 -0
  180. /package/dist/{tree-surfer/nav-node/nav-node.types.js → input/domain/user-input.js} +0 -0
  181. /package/dist/{tree-surfer → trees}/cursor.d.ts +0 -0
  182. /package/dist/{tree-surfer → trees}/cursor.js +0 -0
  183. /package/dist/{tree-surfer/state/state.types.js → trees/fold-node/deprecated/fold-node.types.js} +0 -0
  184. /package/dist/{tree-surfer/tree-node/tree-node.types.js → trees/fold-node/fold-node.types.js} +0 -0
  185. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.d.ts +0 -0
  186. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.debug.test.d.ts +0 -0
  187. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.arb.js +0 -0
  188. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.impl.d.ts +0 -0
  189. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.impl.js +0 -0
  190. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.schemas.d.ts +0 -0
  191. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.schemas.js +0 -0
  192. /package/dist/{tree-surfer/fold-node → trees/tree-node/deprecated}/tree-node.test.d.ts +0 -0
  193. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.test.js +0 -0
  194. /package/dist/{tree-surfer/tree-node → trees/tree-node/deprecated}/tree-node.types.d.ts +0 -0
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Crosses from the opaque public type into the private representation.
3
+ *
4
+ * This cast is intentionally centralized here. Public callers should never
5
+ * need to know that tree nodes currently use `{ id, value, children }`.
6
+ */
7
+ function toData(node) {
8
+ return node;
9
+ }
10
+ /**
11
+ * Crosses from the private representation into the opaque public type.
12
+ */
13
+ function fromData(data) {
14
+ return data;
15
+ }
16
+ function isLeafData(node) {
17
+ return !('children' in node);
18
+ }
19
+ function isUnloadedBranchData(node) {
20
+ return 'children' in node && node.children === null;
21
+ }
22
+ function isLoadedBranchData(node) {
23
+ return 'children' in node && node.children !== null;
24
+ }
25
+ /**
26
+ * Returns a reusable iterable over child nodes without exposing the internal
27
+ * child-array representation.
28
+ *
29
+ * The implementation currently stores children in an array, but callers only
30
+ * receive an `Iterable` and must not rely on iteration order.
31
+ */
32
+ function asChildrenIterable(children) {
33
+ return Object.freeze({
34
+ *[Symbol.iterator]() {
35
+ yield* children;
36
+ },
37
+ });
38
+ }
39
+ /**
40
+ * Copies an arbitrary child iterable into the current private representation
41
+ * and verifies sibling-ID uniqueness.
42
+ *
43
+ * Returning `undefined` represents an invalid child collection rather than an
44
+ * exceptional implementation failure. This matches the public API contract
45
+ * for `createLoadedBranch` and `withLoadedChildren`.
46
+ */
47
+ function copyUniqueChildren(children, getId) {
48
+ const copiedChildren = [...children];
49
+ const seenIds = new Set();
50
+ for (const child of copiedChildren) {
51
+ const id = getId(child);
52
+ if (seenIds.has(id)) {
53
+ return undefined;
54
+ }
55
+ seenIds.add(id);
56
+ }
57
+ return Object.freeze(copiedChildren);
58
+ }
59
+ function createLeafData(id, value) {
60
+ return Object.freeze({
61
+ id,
62
+ value,
63
+ });
64
+ }
65
+ function createUnloadedBranchData(id, value) {
66
+ return Object.freeze({
67
+ id,
68
+ value,
69
+ children: null,
70
+ });
71
+ }
72
+ function createLoadedBranchData(id, value, children) {
73
+ return Object.freeze({
74
+ id,
75
+ value,
76
+ children,
77
+ });
78
+ }
79
+ /**
80
+ * Creates an API for constructing, inspecting, and immutably updating opaque
81
+ * tree nodes.
82
+ *
83
+ * The returned nodes are ordinary JavaScript objects at runtime. Their
84
+ * structural representation is private to this module; consumers interact
85
+ * with them only through the returned API.
86
+ *
87
+ * Tree updates preserve object identity for unrelated nodes. New ancestor
88
+ * nodes and child collections are created only along an updated path.
89
+ */
90
+ export function createTreeNodeApi() {
91
+ function getOpenChildren(node) {
92
+ const data = toData(node);
93
+ return isLoadedBranchData(data) ? data.children : undefined;
94
+ }
95
+ const api = {
96
+ createLeaf(id, value) {
97
+ return fromData(createLeafData(id, value));
98
+ },
99
+ createUnloadedBranch(id, value) {
100
+ return fromData(createUnloadedBranchData(id, value));
101
+ },
102
+ createLoadedBranch(id, value, children) {
103
+ const copiedChildren = copyUniqueChildren(children, api.id);
104
+ if (copiedChildren === undefined) {
105
+ return undefined;
106
+ }
107
+ return fromData(createLoadedBranchData(id, value, copiedChildren));
108
+ },
109
+ id(node) {
110
+ return toData(node).id;
111
+ },
112
+ value(node) {
113
+ return toData(node).value;
114
+ },
115
+ kind(node) {
116
+ const data = toData(node);
117
+ if (isLeafData(data)) {
118
+ return 'leaf';
119
+ }
120
+ if (isUnloadedBranchData(data)) {
121
+ return 'unloaded-branch';
122
+ }
123
+ return 'loaded-branch';
124
+ },
125
+ match(node, handlers) {
126
+ const data = toData(node);
127
+ if (isLeafData(data)) {
128
+ return handlers.leaf({
129
+ id: data.id,
130
+ value: data.value,
131
+ });
132
+ }
133
+ if (isUnloadedBranchData(data)) {
134
+ return handlers.unloadedBranch({
135
+ id: data.id,
136
+ value: data.value,
137
+ });
138
+ }
139
+ return handlers.loadedBranch({
140
+ id: data.id,
141
+ value: data.value,
142
+ children: asChildrenIterable(data.children),
143
+ });
144
+ },
145
+ getLoadedChildren(node) {
146
+ const children = getOpenChildren(node);
147
+ return children === undefined ? undefined : asChildrenIterable(children);
148
+ },
149
+ findChildById(node, id) {
150
+ const children = getOpenChildren(node);
151
+ if (children === undefined) {
152
+ return undefined;
153
+ }
154
+ for (const child of children) {
155
+ if (api.id(child) === id) {
156
+ return child;
157
+ }
158
+ }
159
+ return undefined;
160
+ },
161
+ withValue(node, value) {
162
+ const data = toData(node);
163
+ if (isLeafData(data)) {
164
+ return fromData(createLeafData(data.id, value));
165
+ }
166
+ if (isUnloadedBranchData(data)) {
167
+ return fromData(createUnloadedBranchData(data.id, value));
168
+ }
169
+ return fromData(createLoadedBranchData(data.id, value, data.children));
170
+ },
171
+ toLeaf(node) {
172
+ const data = toData(node);
173
+ return fromData(createLeafData(data.id, data.value));
174
+ },
175
+ toUnloadedBranch(node) {
176
+ const data = toData(node);
177
+ return fromData(createUnloadedBranchData(data.id, data.value));
178
+ },
179
+ withLoadedChildren(node, children) {
180
+ const copiedChildren = copyUniqueChildren(children, api.id);
181
+ if (copiedChildren === undefined) {
182
+ return undefined;
183
+ }
184
+ const data = toData(node);
185
+ return fromData(createLoadedBranchData(data.id, data.value, copiedChildren));
186
+ },
187
+ findAtPath(root, path) {
188
+ return api.selectAtPath(root, path, (node) => node);
189
+ },
190
+ selectAtPath(root, path, selector) {
191
+ let node = root;
192
+ for (const id of path) {
193
+ const child = api.findChildById(node, id);
194
+ if (child === undefined) {
195
+ return undefined;
196
+ }
197
+ node = child;
198
+ }
199
+ return selector(node);
200
+ },
201
+ updateAtPath(root, path, update) {
202
+ if (path.length === 0) {
203
+ return update(root);
204
+ }
205
+ return modifyChildAtPath(root, path, update);
206
+ },
207
+ toModel(node) {
208
+ const data = toData(node);
209
+ if (isLeafData(data)) {
210
+ return {
211
+ id: data.id,
212
+ value: data.value,
213
+ };
214
+ }
215
+ if (isUnloadedBranchData(data)) {
216
+ return {
217
+ id: data.id,
218
+ value: data.value,
219
+ children: null,
220
+ };
221
+ }
222
+ return {
223
+ id: data.id,
224
+ value: data.value,
225
+ children: data.children.map(api.toModel),
226
+ };
227
+ },
228
+ fromModel(model) {
229
+ if (!('children' in model)) {
230
+ return api.createLeaf(model.id, model.value);
231
+ }
232
+ if (model.children === null) {
233
+ return api.createUnloadedBranch(model.id, model.value);
234
+ }
235
+ const children = [];
236
+ for (const childModel of model.children) {
237
+ const child = api.fromModel(childModel);
238
+ if (child === undefined) {
239
+ return undefined;
240
+ }
241
+ children.push(child);
242
+ }
243
+ return api.createLoadedBranch(model.id, model.value, children);
244
+ }
245
+ };
246
+ function modifyChildAtPath(node, path, modifier) {
247
+ const childId = path[0];
248
+ const children = getOpenChildren(node);
249
+ if (childId === undefined || children === undefined) {
250
+ return undefined;
251
+ }
252
+ let childIndex = -1;
253
+ for (const [index, child] of children.entries()) {
254
+ if (api.id(child) === childId) {
255
+ childIndex = index;
256
+ break;
257
+ }
258
+ }
259
+ if (childIndex === -1) {
260
+ return undefined;
261
+ }
262
+ const child = children[childIndex];
263
+ if (child === undefined) {
264
+ return undefined;
265
+ }
266
+ const updatedChild = path.length === 1
267
+ ? modifier(child)
268
+ : modifyChildAtPath(child, path.slice(1), modifier);
269
+ if (updatedChild === undefined) {
270
+ return undefined;
271
+ }
272
+ /*
273
+ * The current representation happens to use an array, so replacing one
274
+ * child is efficient internally. The public API makes no ordering promise:
275
+ * this is solely an implementation detail.
276
+ */
277
+ const updatedChildren = [...children];
278
+ updatedChildren[childIndex] = updatedChild;
279
+ /*
280
+ * `withChildren` revalidates sibling-ID uniqueness. This matters when
281
+ * `modifier` replaces a node with another node having a different ID.
282
+ */
283
+ return api.withLoadedChildren(node, updatedChildren);
284
+ }
285
+ return api;
286
+ }
@@ -0,0 +1,78 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * A serializable primitive suitable for use as a stable tree-node ID.
4
+ *
5
+ * @remarks
6
+ *
7
+ * A node ID identifies a node among its direct siblings. It is not required
8
+ * to be globally unique across the whole tree: two nodes in different
9
+ * branches may have the same ID.
10
+ *
11
+ * Numeric IDs must be finite. TypeScript cannot represent the exclusion of
12
+ * `NaN`, `Infinity`, and `-Infinity` from `number`, so values received from
13
+ * an untrusted source should be validated at an application boundary, for
14
+ * example with `serializableKeySchema`.
15
+ *
16
+ * Applications should normally define a narrower domain-specific ID type.
17
+ * Branded ID types prevent IDs from unrelated domains from being mixed
18
+ * accidentally, even when both use strings at runtime.
19
+ *
20
+ * @example
21
+ *
22
+ * ```ts
23
+ * const menuIdSchema = z
24
+ * .string()
25
+ * .min(1)
26
+ * .brand<'MenuId'>();
27
+ *
28
+ * type MenuId = z.output<typeof menuIdSchema>;
29
+ *
30
+ * type MenuItem = Readonly<{
31
+ * label: string;
32
+ * href?: string;
33
+ * }>;
34
+ *
35
+ * type MenuTree = TreeNode<MenuId, MenuItem>;
36
+ * ```
37
+ */
38
+ export type SerializableKey = string | number;
39
+ /**
40
+ * Validates a serializable primitive suitable for use as a stable tree-node ID.
41
+ *
42
+ * Strings are always accepted. Numbers must be finite: `NaN`, `Infinity`, and
43
+ * `-Infinity` are rejected.
44
+ */
45
+ export declare const serializableKeySchema: z.ZodType<SerializableKey>;
46
+ /**
47
+ * A structural, serializable snapshot of a tree node.
48
+ *
49
+ * This is a public persistence and interchange model for `TreeNode`; it is
50
+ * not the runtime representation of an opaque `TreeNode` value.
51
+ *
52
+ * Loaded children are represented by an array because JSON has no native
53
+ * unordered collection type. The array encodes an unordered collection of
54
+ * sibling-unique nodes: its iteration order has no semantic meaning and is
55
+ * not guaranteed to be preserved by `TreeNodeApi.fromModel`,
56
+ * `TreeNodeApi.toModel`, or other tree operations.
57
+ *
58
+ * Applications that need visual or navigation order should derive ordered UI
59
+ * rows from the tree using an explicit ordering policy.
60
+ */
61
+ export type TreeNodeModel<Id extends SerializableKey, Value> = LeafTreeNodeModel<Id, Value> | UnloadedBranchTreeNodeModel<Id, Value> | LoadedBranchTreeNodeModel<Id, Value>;
62
+ export type LeafTreeNodeModel<Id extends SerializableKey, Value> = Readonly<{
63
+ id: Id;
64
+ value: Value;
65
+ children?: never;
66
+ }>;
67
+ export type UnloadedBranchTreeNodeModel<Id extends SerializableKey, Value> = Readonly<{
68
+ id: Id;
69
+ value: Value;
70
+ children: null;
71
+ }>;
72
+ export type LoadedBranchTreeNodeModel<Id extends SerializableKey, Value> = Readonly<{
73
+ id: Id;
74
+ value: Value;
75
+ children: readonly TreeNodeModel<Id, Value>[];
76
+ }>;
77
+ export declare function createTreeNodeModelSchema<Id extends SerializableKey, Value>(idSchema: z.ZodType<Id>, valueSchema: z.ZodType<Value>): z.ZodType<TreeNodeModel<Id, Value>>;
78
+ //# sourceMappingURL=tree-node.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-node.model.d.ts","sourceRoot":"","sources":["../../../src/trees/tree-node/tree-node.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAG3D,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IACvD,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,GAC5B,2BAA2B,CAAC,EAAE,EAAE,KAAK,CAAC,GACtC,yBAAyB,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAEzC,MAAM,MAAM,iBAAiB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,IAAI,QAAQ,CAAC;IAC1E,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,2BAA2B,CACrC,EAAE,SAAS,eAAe,EAC1B,KAAK,IACH,QAAQ,CAAC;IACX,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,IAAI,CAAC;CAChB,CAAC,CAAC;AAEH,MAAM,MAAM,yBAAyB,CACnC,EAAE,SAAS,eAAe,EAC1B,KAAK,IACH,QAAQ,CAAC;IACX,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,SAAS,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;CAC/C,CAAC,CAAC;AAEH,wBAAgB,yBAAyB,CAAC,EAAE,SAAS,eAAe,EAAE,KAAK,EACzE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EACvB,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAC5B,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CA+BrC"}
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Validates a serializable primitive suitable for use as a stable tree-node ID.
4
+ *
5
+ * Strings are always accepted. Numbers must be finite: `NaN`, `Infinity`, and
6
+ * `-Infinity` are rejected.
7
+ */
8
+ export const serializableKeySchema = z.union([
9
+ z.string(),
10
+ z.number(),
11
+ ]);
12
+ export function createTreeNodeModelSchema(idSchema, valueSchema) {
13
+ let nodeSchema;
14
+ nodeSchema = z.lazy(() => z.union([
15
+ z
16
+ .object({
17
+ id: idSchema,
18
+ value: valueSchema,
19
+ })
20
+ .strict(),
21
+ z
22
+ .object({
23
+ id: idSchema,
24
+ value: valueSchema,
25
+ children: z.null(),
26
+ })
27
+ .strict(),
28
+ z
29
+ .object({
30
+ id: idSchema,
31
+ value: valueSchema,
32
+ children: z.array(nodeSchema),
33
+ })
34
+ .strict(),
35
+ ]));
36
+ return nodeSchema;
37
+ }