treenode.ts 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -3
- package/dist/index.js +415 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +390 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +25 -16
- package/dist/IParseable.d.ts +0 -8
- package/dist/IParseable.d.ts.map +0 -1
- package/dist/IParseable.js +0 -3
- package/dist/IParseable.js.map +0 -1
- package/dist/TreeNode.d.ts +0 -89
- package/dist/TreeNode.d.ts.map +0 -1
- package/dist/TreeNode.js +0 -271
- package/dist/TreeNode.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,417 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
TreeNode: () => TreeNode
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/TreeNode.ts
|
|
28
|
+
var TreeNode = class _TreeNode {
|
|
29
|
+
constructor(model, parent = null, children = []) {
|
|
30
|
+
this.model = model;
|
|
31
|
+
this.parent = parent;
|
|
32
|
+
this._index = 0;
|
|
33
|
+
this._children = [];
|
|
34
|
+
this._children = children;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the children of the node as a readonly array.
|
|
38
|
+
* Use add(), drop(), or swap() to modify children.
|
|
39
|
+
*/
|
|
40
|
+
get children() {
|
|
41
|
+
return this._children;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parses object into a tree and returns the root node.
|
|
45
|
+
*/
|
|
46
|
+
static parse(tree) {
|
|
47
|
+
const node = new _TreeNode(tree.model);
|
|
48
|
+
tree.children.forEach((child) => node.add(_TreeNode.parse(child)));
|
|
49
|
+
return node;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Index of the node among its siblings.
|
|
53
|
+
*/
|
|
54
|
+
get index() {
|
|
55
|
+
return this._index;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Indices from the root to the node.
|
|
59
|
+
*/
|
|
60
|
+
get indices() {
|
|
61
|
+
const indices = [];
|
|
62
|
+
let node = this;
|
|
63
|
+
while (node.parent) {
|
|
64
|
+
indices.push(node.index);
|
|
65
|
+
node = node.parent;
|
|
66
|
+
}
|
|
67
|
+
return indices.reverse();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Compressed path key: positive = run of first-children, negative = child index.
|
|
71
|
+
* e.g. [0,0,0,1,0,0] -> "3,-1,2"
|
|
72
|
+
*/
|
|
73
|
+
get pathKey() {
|
|
74
|
+
const indices = this.indices;
|
|
75
|
+
if (indices.length === 0) return "";
|
|
76
|
+
const parts = [];
|
|
77
|
+
let zeroCount = 0;
|
|
78
|
+
for (const idx of indices) {
|
|
79
|
+
if (idx === 0) {
|
|
80
|
+
zeroCount++;
|
|
81
|
+
} else {
|
|
82
|
+
if (zeroCount > 0) {
|
|
83
|
+
parts.push(zeroCount);
|
|
84
|
+
zeroCount = 0;
|
|
85
|
+
}
|
|
86
|
+
parts.push(-idx);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (zeroCount > 0) {
|
|
90
|
+
parts.push(zeroCount);
|
|
91
|
+
}
|
|
92
|
+
return parts.join(",");
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Returns true if the node has children.
|
|
96
|
+
*/
|
|
97
|
+
get hasChildren() {
|
|
98
|
+
return this._children.length > 0;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Returns true if the node is the root (has no parent).
|
|
102
|
+
*/
|
|
103
|
+
get isRoot() {
|
|
104
|
+
return this.parent === null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Returns true if the node is a leaf (has no children).
|
|
108
|
+
*/
|
|
109
|
+
get isLeaf() {
|
|
110
|
+
return !this.hasChildren;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Returns the root node of the tree.
|
|
114
|
+
*/
|
|
115
|
+
get root() {
|
|
116
|
+
let node = this;
|
|
117
|
+
while (node.parent) {
|
|
118
|
+
node = node.parent;
|
|
119
|
+
}
|
|
120
|
+
return node;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Returns the depth of the node (root is 0).
|
|
124
|
+
*/
|
|
125
|
+
get depth() {
|
|
126
|
+
return this.parent ? this.parent.depth + 1 : 0;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Returns siblings of this node (excluding self).
|
|
130
|
+
*/
|
|
131
|
+
get siblings() {
|
|
132
|
+
if (this.isRoot) return [];
|
|
133
|
+
return this.parent.children.filter((child) => child !== this);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Add node as a child.
|
|
137
|
+
* @param child - The node to add
|
|
138
|
+
* @param index - Optional index to insert at. If omitted, appends to end.
|
|
139
|
+
* @throws RangeError if index is out of bounds (negative or > children.length)
|
|
140
|
+
*/
|
|
141
|
+
add(child, index) {
|
|
142
|
+
child.parent = this;
|
|
143
|
+
if (index === void 0) {
|
|
144
|
+
child._index = this._children.length;
|
|
145
|
+
this._children.push(child);
|
|
146
|
+
} else {
|
|
147
|
+
if (index < 0 || index > this._children.length) {
|
|
148
|
+
throw new RangeError(`Invalid index: ${index}. Valid range: 0 to ${this._children.length}`);
|
|
149
|
+
}
|
|
150
|
+
this._children.splice(index, 0, child);
|
|
151
|
+
for (let i = index; i < this._children.length; i++) {
|
|
152
|
+
this._children[i]._index = i;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return child;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Swap children at indices i and j.
|
|
159
|
+
* @param i - First index
|
|
160
|
+
* @param j - Second index
|
|
161
|
+
*/
|
|
162
|
+
swap(i, j) {
|
|
163
|
+
if (i < 0 || j < 0 || i >= this._children.length || j >= this._children.length) {
|
|
164
|
+
throw new RangeError(`Invalid indices: ${i}, ${j}. Children length: ${this._children.length}`);
|
|
165
|
+
}
|
|
166
|
+
if (i === j) return;
|
|
167
|
+
const temp = this._children[i];
|
|
168
|
+
this._children[i] = this._children[j];
|
|
169
|
+
this._children[j] = temp;
|
|
170
|
+
this._children[i]._index = i;
|
|
171
|
+
this._children[j]._index = j;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Add model as a child.
|
|
175
|
+
* @param model - The model to add
|
|
176
|
+
* @param index - Optional index to insert at. If omitted, appends to end.
|
|
177
|
+
*/
|
|
178
|
+
addModel(model, index) {
|
|
179
|
+
return this.add(new _TreeNode(model), index);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Remove current node and its children from the tree and return.
|
|
183
|
+
*/
|
|
184
|
+
drop() {
|
|
185
|
+
if (!this.isRoot) {
|
|
186
|
+
const idx = this._index;
|
|
187
|
+
const parentChildren = this.parent._children;
|
|
188
|
+
parentChildren.splice(idx, 1);
|
|
189
|
+
for (let i = idx; i < parentChildren.length; i++) {
|
|
190
|
+
parentChildren[i]._index = i;
|
|
191
|
+
}
|
|
192
|
+
this.parent = null;
|
|
193
|
+
this._index = 0;
|
|
194
|
+
}
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Returns a deep copy of structure, shallow copy of model.
|
|
199
|
+
*/
|
|
200
|
+
clone() {
|
|
201
|
+
const node = new _TreeNode(this.model);
|
|
202
|
+
node._children = this._children.map((child, i) => {
|
|
203
|
+
const newChild = child.clone();
|
|
204
|
+
newChild.parent = node;
|
|
205
|
+
newChild._index = i;
|
|
206
|
+
return newChild;
|
|
207
|
+
});
|
|
208
|
+
return node;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Returns a node given a list of indices
|
|
212
|
+
*/
|
|
213
|
+
fetch(indices) {
|
|
214
|
+
let node = this;
|
|
215
|
+
for (const i of indices) {
|
|
216
|
+
node = node.children[i];
|
|
217
|
+
if (!node) return null;
|
|
218
|
+
}
|
|
219
|
+
return node;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns a node given a pathKey string.
|
|
223
|
+
* @see pathKey
|
|
224
|
+
*/
|
|
225
|
+
fetchByPathKey(pathKey) {
|
|
226
|
+
if (pathKey === "") return this;
|
|
227
|
+
const indices = [];
|
|
228
|
+
const parts = pathKey.split(",").map(Number);
|
|
229
|
+
for (const part of parts) {
|
|
230
|
+
if (part >= 0) {
|
|
231
|
+
for (let i = 0; i < part; i++) {
|
|
232
|
+
indices.push(0);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
indices.push(-part);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return this.fetch(indices);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Returns list of nodes to the root.
|
|
242
|
+
*/
|
|
243
|
+
path() {
|
|
244
|
+
const path = [];
|
|
245
|
+
let node = this;
|
|
246
|
+
while (node) {
|
|
247
|
+
path.push(node);
|
|
248
|
+
node = node.parent;
|
|
249
|
+
}
|
|
250
|
+
return path.reverse();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Iterates over a node's children and returns a new root node.
|
|
254
|
+
*/
|
|
255
|
+
map(callback) {
|
|
256
|
+
const node = new _TreeNode(callback(this));
|
|
257
|
+
node._children = this._children.map((child, i) => {
|
|
258
|
+
const newChild = child.map(callback);
|
|
259
|
+
newChild.parent = node;
|
|
260
|
+
newChild._index = i;
|
|
261
|
+
return newChild;
|
|
262
|
+
});
|
|
263
|
+
return node;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Iterates over a node's children and returns a new root node.
|
|
267
|
+
*/
|
|
268
|
+
async mapAsync(callback, parent) {
|
|
269
|
+
const node = new _TreeNode(await callback(this, parent));
|
|
270
|
+
node._children = await Promise.all(this._children.map(async (child, i) => {
|
|
271
|
+
const newChild = await child.mapAsync(callback, node);
|
|
272
|
+
newChild.parent = node;
|
|
273
|
+
newChild._index = i;
|
|
274
|
+
return newChild;
|
|
275
|
+
}));
|
|
276
|
+
return node;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Breadth-first search, return true in the callback to end iteration.
|
|
280
|
+
*/
|
|
281
|
+
breadth(callback) {
|
|
282
|
+
const queue = [this];
|
|
283
|
+
let head = 0;
|
|
284
|
+
while (head < queue.length) {
|
|
285
|
+
const node = queue[head++];
|
|
286
|
+
if (callback(node)) return node;
|
|
287
|
+
for (let i = 0, childCount = node.children.length; i < childCount; i++) {
|
|
288
|
+
queue.push(node.children[i]);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Depth-first pre-order search, return true in the callback to end iteration.
|
|
295
|
+
*/
|
|
296
|
+
pre(callback) {
|
|
297
|
+
if (callback(this)) return this;
|
|
298
|
+
for (let i = 0, childCount = this.children.length; i < childCount; i++) {
|
|
299
|
+
const node = this.children[i].pre(callback);
|
|
300
|
+
if (node) return node;
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Depth-first post-order search, return true in the callback to end iteration.
|
|
306
|
+
*/
|
|
307
|
+
post(callback) {
|
|
308
|
+
for (let i = 0, childCount = this.children.length; i < childCount; i++) {
|
|
309
|
+
const node = this.children[i].post(callback);
|
|
310
|
+
if (node) return node;
|
|
311
|
+
}
|
|
312
|
+
if (callback(this)) return this;
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Find the first node matching the predicate.
|
|
317
|
+
*/
|
|
318
|
+
find(predicate, method = "pre") {
|
|
319
|
+
return this[method]((node) => predicate(node));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Find all nodes matching the predicate.
|
|
323
|
+
*/
|
|
324
|
+
findAll(predicate, method = "pre") {
|
|
325
|
+
const results = [];
|
|
326
|
+
this[method]((node) => {
|
|
327
|
+
if (predicate(node)) results.push(node);
|
|
328
|
+
});
|
|
329
|
+
return results;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Returns a list of nodes.
|
|
333
|
+
*/
|
|
334
|
+
flatten(method) {
|
|
335
|
+
const list = [];
|
|
336
|
+
this[method].call(this, (node) => {
|
|
337
|
+
list.push(node);
|
|
338
|
+
});
|
|
339
|
+
return list;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Returns a nested array representation of the tree.
|
|
343
|
+
* - Leaf -> value
|
|
344
|
+
* - Single child -> [model, ...childResult]
|
|
345
|
+
* - Multiple leaf children -> [model, [leaves...]]
|
|
346
|
+
* - Multiple mixed children -> [model, child1Result, child2Result, ...]
|
|
347
|
+
*/
|
|
348
|
+
toNestedArray() {
|
|
349
|
+
if (this.isLeaf) {
|
|
350
|
+
return this.model;
|
|
351
|
+
}
|
|
352
|
+
if (this.children.length === 1) {
|
|
353
|
+
const childResult = this.children[0].toNestedArray();
|
|
354
|
+
if (Array.isArray(childResult)) {
|
|
355
|
+
return [this.model, ...childResult];
|
|
356
|
+
}
|
|
357
|
+
return [this.model, childResult];
|
|
358
|
+
}
|
|
359
|
+
const allLeaves = this.children.every((c) => c.isLeaf);
|
|
360
|
+
if (allLeaves) {
|
|
361
|
+
return [this.model, this.children.map((c) => c.model)];
|
|
362
|
+
}
|
|
363
|
+
return [this.model, ...this.children.map((c) => c.toNestedArray())];
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Creates a tree from a nested array representation.
|
|
367
|
+
* @see toNestedArray
|
|
368
|
+
*/
|
|
369
|
+
static fromNestedArray(input) {
|
|
370
|
+
if (!Array.isArray(input)) {
|
|
371
|
+
return new _TreeNode(input);
|
|
372
|
+
}
|
|
373
|
+
const [model, ...rest] = input;
|
|
374
|
+
const node = new _TreeNode(model);
|
|
375
|
+
if (rest.length === 0) {
|
|
376
|
+
return node;
|
|
377
|
+
}
|
|
378
|
+
if (rest.length === 1 && Array.isArray(rest[0])) {
|
|
379
|
+
const inner = rest[0];
|
|
380
|
+
const hasArrays2 = inner.some((x) => Array.isArray(x));
|
|
381
|
+
if (!hasArrays2) {
|
|
382
|
+
for (const leaf of inner) {
|
|
383
|
+
node.addModel(leaf);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
node.add(_TreeNode.fromNestedArray(inner));
|
|
387
|
+
}
|
|
388
|
+
return node;
|
|
389
|
+
}
|
|
390
|
+
const hasArrays = rest.some((x) => Array.isArray(x));
|
|
391
|
+
if (!hasArrays) {
|
|
392
|
+
let current = node;
|
|
393
|
+
for (const val of rest) {
|
|
394
|
+
current = current.addModel(val);
|
|
395
|
+
}
|
|
396
|
+
return node;
|
|
397
|
+
}
|
|
398
|
+
for (const childResult of rest) {
|
|
399
|
+
node.add(_TreeNode.fromNestedArray(childResult));
|
|
400
|
+
}
|
|
401
|
+
return node;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Returns an object representation of the tree.
|
|
405
|
+
*/
|
|
406
|
+
toObject() {
|
|
407
|
+
return {
|
|
408
|
+
model: this.model,
|
|
409
|
+
children: this.children.map((child) => child.toObject())
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
414
|
+
0 && (module.exports = {
|
|
415
|
+
TreeNode
|
|
416
|
+
});
|
|
6
417
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uCAAsE;AAA7D,oGAAA,QAAQ,OAAA"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/TreeNode.ts"],"sourcesContent":["export { TreeNode, SearchCallback, SearchStrategy, NestedArray } from './TreeNode';\nexport { IParseable } from './IParseable';\n","import { IParseable } from \"./IParseable\";\n\n/**\n * Search callback passed to `.pre`, `.post`, and `.breadth`.\n * @public\n */\nexport type SearchCallback<T> = (node: TreeNode<T>) => boolean | void;\n\n/**\n * Search methods on TreeNode, passed to `.flatten`.\n * @public\n */\nexport type SearchStrategy = \"pre\" | \"post\" | \"breadth\";\n\n/**\n * Nested array representation of a tree.\n * @public\n */\nexport type NestedArray<T> = T | [T, ...(NestedArray<T> | T[])[]];\n\n/**\n * @public\n */\nexport class TreeNode<T> {\n private _index: number = 0;\n private _children: TreeNode<T>[] = [];\n\n constructor(\n public model: T,\n public parent: TreeNode<T> | null = null,\n children: TreeNode<T>[] = []\n ) {\n this._children = children;\n }\n\n /**\n * Returns the children of the node as a readonly array.\n * Use add(), drop(), or swap() to modify children.\n */\n get children(): readonly TreeNode<T>[] {\n return this._children;\n }\n\n /**\n * Parses object into a tree and returns the root node.\n */\n static parse<T>(tree: IParseable<T>): TreeNode<T> {\n const node = new TreeNode(tree.model);\n tree.children.forEach((child) => node.add(TreeNode.parse(child)));\n return node;\n }\n\n /**\n * Index of the node among its siblings.\n */\n get index(): number {\n return this._index;\n }\n\n /**\n * Indices from the root to the node.\n */\n get indices(): number[] {\n const indices: number[] = [];\n let node: TreeNode<T> | null = this;\n while (node.parent) {\n indices.push(node.index);\n node = node.parent;\n }\n return indices.reverse();\n }\n\n /**\n * Compressed path key: positive = run of first-children, negative = child index.\n * e.g. [0,0,0,1,0,0] -> \"3,-1,2\"\n */\n get pathKey(): string {\n const indices = this.indices;\n if (indices.length === 0) return '';\n\n const parts: number[] = [];\n let zeroCount = 0;\n\n for (const idx of indices) {\n if (idx === 0) {\n zeroCount++;\n } else {\n if (zeroCount > 0) {\n parts.push(zeroCount);\n zeroCount = 0;\n }\n parts.push(-idx);\n }\n }\n\n if (zeroCount > 0) {\n parts.push(zeroCount);\n }\n\n return parts.join(',');\n }\n\n /**\n * Returns true if the node has children.\n */\n get hasChildren(): boolean {\n return this._children.length > 0;\n }\n\n /**\n * Returns true if the node is the root (has no parent).\n */\n get isRoot(): boolean {\n return this.parent === null;\n }\n\n /**\n * Returns true if the node is a leaf (has no children).\n */\n get isLeaf(): boolean {\n return !this.hasChildren;\n }\n\n /**\n * Returns the root node of the tree.\n */\n get root(): TreeNode<T> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n let node: TreeNode<T> = this;\n while (node.parent) {\n node = node.parent;\n }\n return node;\n }\n\n /**\n * Returns the depth of the node (root is 0).\n */\n get depth(): number {\n return this.parent ? this.parent.depth + 1 : 0;\n }\n\n /**\n * Returns siblings of this node (excluding self).\n */\n get siblings(): TreeNode<T>[] {\n if (this.isRoot) return [];\n return this.parent!.children.filter((child) => child !== this);\n }\n\n /**\n * Add node as a child.\n * @param child - The node to add\n * @param index - Optional index to insert at. If omitted, appends to end.\n * @throws RangeError if index is out of bounds (negative or > children.length)\n */\n add(child: TreeNode<T>, index?: number): TreeNode<T> {\n child.parent = this;\n if (index === undefined) {\n // Append at end\n child._index = this._children.length;\n this._children.push(child);\n } else {\n // Validate index\n if (index < 0 || index > this._children.length) {\n throw new RangeError(`Invalid index: ${index}. Valid range: 0 to ${this._children.length}`);\n }\n // Insert at specific index\n this._children.splice(index, 0, child);\n // Update indices for inserted and subsequent siblings\n for (let i = index; i < this._children.length; i++) {\n this._children[i]._index = i;\n }\n }\n return child;\n }\n\n /**\n * Swap children at indices i and j.\n * @param i - First index\n * @param j - Second index\n */\n swap(i: number, j: number): void {\n if (i < 0 || j < 0 || i >= this._children.length || j >= this._children.length) {\n throw new RangeError(`Invalid indices: ${i}, ${j}. Children length: ${this._children.length}`);\n }\n if (i === j) return;\n const temp = this._children[i];\n this._children[i] = this._children[j];\n this._children[j] = temp;\n this._children[i]._index = i;\n this._children[j]._index = j;\n }\n\n /**\n * Add model as a child.\n * @param model - The model to add\n * @param index - Optional index to insert at. If omitted, appends to end.\n */\n addModel(model: T, index?: number): TreeNode<T> {\n return this.add(new TreeNode<T>(model), index);\n }\n\n /**\n * Remove current node and its children from the tree and return.\n */\n drop(): TreeNode<T> {\n if (!this.isRoot) {\n const idx = this._index;\n const parentChildren = (this.parent as TreeNode<T>)._children;\n parentChildren.splice(idx, 1);\n // Update indices of subsequent siblings\n for (let i = idx; i < parentChildren.length; i++) {\n parentChildren[i]._index = i;\n }\n this.parent = null;\n this._index = 0;\n }\n return this;\n }\n\n /**\n * Returns a deep copy of structure, shallow copy of model.\n */\n clone(): TreeNode<T> {\n const node = new TreeNode<T>(this.model);\n node._children = this._children.map((child, i) => {\n const newChild = child.clone();\n newChild.parent = node;\n newChild._index = i;\n return newChild;\n });\n return node;\n }\n\n /**\n * Returns a node given a list of indices\n */\n fetch(indices: number[]): TreeNode<T> | null {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n let node: TreeNode<T> = this;\n for (const i of indices) {\n node = node.children[i];\n if (!node) return null;\n }\n return node;\n }\n\n /**\n * Returns a node given a pathKey string.\n * @see pathKey\n */\n fetchByPathKey(pathKey: string): TreeNode<T> | null {\n if (pathKey === '') return this;\n\n const indices: number[] = [];\n const parts = pathKey.split(',').map(Number);\n\n for (const part of parts) {\n if (part >= 0) {\n for (let i = 0; i < part; i++) {\n indices.push(0);\n }\n } else {\n indices.push(-part);\n }\n }\n\n return this.fetch(indices);\n }\n\n /**\n * Returns list of nodes to the root.\n */\n path(): TreeNode<T>[] {\n const path: TreeNode<T>[] = [];\n let node: TreeNode<T> | null = this;\n while (node) {\n path.push(node);\n node = node.parent;\n }\n return path.reverse();\n }\n\n /**\n * Iterates over a node's children and returns a new root node.\n */\n map<U>(callback: (node: TreeNode<T>) => U): TreeNode<U> {\n const node = new TreeNode<U>(callback(this));\n node._children = this._children.map((child, i) => {\n const newChild = child.map(callback);\n newChild.parent = node;\n newChild._index = i;\n return newChild;\n });\n return node;\n }\n\n /**\n * Iterates over a node's children and returns a new root node.\n */\n async mapAsync<U>(callback: (node: TreeNode<T>, parent: TreeNode<U> | undefined) => Promise<U>, parent?: TreeNode<U>): Promise<TreeNode<U>>{\n const node = new TreeNode<U>(await callback(this, parent));\n node._children = await Promise.all(this._children.map(async (child, i) => {\n const newChild = await child.mapAsync(callback, node);\n newChild.parent = node;\n newChild._index = i;\n return newChild;\n }));\n return node;\n }\n\n /**\n * Breadth-first search, return true in the callback to end iteration.\n */\n breadth(callback: SearchCallback<T>): TreeNode<T> | null {\n const queue: TreeNode<T>[] = [this];\n let head = 0;\n\n while (head < queue.length) {\n const node = queue[head++];\n if (callback(node)) return node;\n for (let i = 0, childCount = node.children.length; i < childCount; i++) {\n queue.push(node.children[i]);\n }\n }\n\n return null;\n }\n\n /**\n * Depth-first pre-order search, return true in the callback to end iteration.\n */\n pre(callback: SearchCallback<T>): TreeNode<T> | null {\n if (callback(this)) return this;\n\n for (let i = 0, childCount = this.children.length; i < childCount; i++) {\n const node = this.children[i].pre(callback);\n if (node) return node;\n }\n\n return null;\n }\n\n /**\n * Depth-first post-order search, return true in the callback to end iteration.\n */\n post(callback: SearchCallback<T>): TreeNode<T> | null {\n for (let i = 0, childCount = this.children.length; i < childCount; i++) {\n const node = this.children[i].post(callback);\n if (node) return node;\n }\n\n if (callback(this)) return this;\n\n return null;\n }\n\n /**\n * Find the first node matching the predicate.\n */\n find(predicate: (node: TreeNode<T>) => boolean, method: SearchStrategy = \"pre\"): TreeNode<T> | null {\n return this[method]((node) => predicate(node));\n }\n\n /**\n * Find all nodes matching the predicate.\n */\n findAll(predicate: (node: TreeNode<T>) => boolean, method: SearchStrategy = \"pre\"): TreeNode<T>[] {\n const results: TreeNode<T>[] = [];\n this[method]((node) => {\n if (predicate(node)) results.push(node);\n });\n return results;\n }\n\n /**\n * Returns a list of nodes.\n */\n flatten(method: SearchStrategy): TreeNode<T>[] {\n const list: TreeNode<T>[] = [];\n this[method].call(this, (node) => {\n list.push(node);\n });\n return list;\n }\n\n /**\n * Returns a nested array representation of the tree.\n * - Leaf -> value\n * - Single child -> [model, ...childResult]\n * - Multiple leaf children -> [model, [leaves...]]\n * - Multiple mixed children -> [model, child1Result, child2Result, ...]\n */\n toNestedArray(): NestedArray<T> {\n if (this.isLeaf) {\n return this.model;\n }\n\n if (this.children.length === 1) {\n const childResult = this.children[0].toNestedArray();\n if (Array.isArray(childResult)) {\n return [this.model, ...childResult];\n }\n return [this.model, childResult];\n }\n\n // Multiple children\n const allLeaves = this.children.every((c) => c.isLeaf);\n if (allLeaves) {\n return [this.model, this.children.map((c) => c.model)];\n }\n return [this.model, ...this.children.map((c) => c.toNestedArray())];\n }\n\n /**\n * Creates a tree from a nested array representation.\n * @see toNestedArray\n */\n static fromNestedArray<T>(input: NestedArray<T>): TreeNode<T> {\n if (!Array.isArray(input)) {\n return new TreeNode<T>(input);\n }\n\n const [model, ...rest] = input;\n const node = new TreeNode<T>(model);\n\n if (rest.length === 0) {\n return node;\n }\n\n if (rest.length === 1 && Array.isArray(rest[0])) {\n const inner = rest[0];\n const hasArrays = inner.some((x) => Array.isArray(x));\n\n if (!hasArrays) {\n // Multiple leaf children\n for (const leaf of inner as T[]) {\n node.addModel(leaf);\n }\n } else {\n // Single non-leaf child\n node.add(TreeNode.fromNestedArray<T>(inner as NestedArray<T>));\n }\n return node;\n }\n\n const hasArrays = rest.some((x) => Array.isArray(x));\n\n if (!hasArrays) {\n // Chain: [model, a, b, c] = model -> a -> b -> c\n let current = node;\n for (const val of rest as T[]) {\n current = current.addModel(val);\n }\n return node;\n }\n\n // Multiple children with at least one non-leaf\n for (const childResult of rest) {\n node.add(TreeNode.fromNestedArray<T>(childResult as NestedArray<T>));\n }\n return node;\n }\n\n /**\n * Returns an object representation of the tree.\n */\n toObject(): IParseable<T> {\n return {\n model: this.model,\n children: this.children.map((child) => child.toObject()),\n };\n }\n}\n"],"mappings":"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","names":["hasArrays"]}
|