fast-tree-builder 2.0.0-alpha.4 → 2.0.0-beta.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/README.md CHANGED
@@ -67,12 +67,12 @@ Builds a tree structure from an iterable list of items.
67
67
  ##### Optional
68
68
 
69
69
  - `nodeValueMapper`: Function to map an item to a custom value stored in the node. Optional.
70
- - `nodeValueKey`: Key where the item's data is stored in the output node. Set to `false` to inline the item directly into the node. Default: `'value'`.
71
- - `nodeParentKey`: Key where the node's parent reference is stored. Set to `false` to omit parent links. Default: `'parent'`.
72
- - `nodeChildrenKey`: Key where the node's children are stored. Default: `'children'`.
73
- - `withDepth`: When `true`, adds a `depth` property to each node indicating its depth in the tree. Also implies `validateTree`. Default: `false`.
74
- - `validateReferences`: When `true`, verifies all `parentId` or `childIds` resolve to real items. Errors are thrown on invalid references. Default: `false`.
75
- - `validateTree`: When `true`, verifies that the final structure is a valid tree (no cycles or nodes reachable via multipla paths). Errors are thrown if the check fails. Default: `false`.
70
+ - `nodeValueKey`: Key where the item is stored in the output node. Set to `false` to inline the item directly into the node. Defaults to `'value'`.
71
+ - `nodeParentKey`: Key where the node's parent reference is stored. Set to `false` to omit parent links. Defaults to `'parent'`.
72
+ - `nodeChildrenKey`: Key where the node's children are stored. Defaults to `'children'`.
73
+ - `nodeDepthKey`: Object key used to store the node's depth in the tree (root = 0). Set to `false` to omit depth values. Turns on `validateTree` when a string value is set here. Defaults to `false`.
74
+ - `validateReferences`: When `true`, verifies all `parentId` or `childIds` resolve to real items. Errors are thrown on invalid references. Defaults to `false`.
75
+ - `validateTree`: When `true`, verifies that the final structure is a valid tree (no cycles or nodes reachable via multipla paths). Errors are thrown if the check fails. Defaults to `false`.
76
76
 
77
77
  #### Returns
78
78
 
@@ -88,7 +88,7 @@ Builds a tree structure from an iterable list of items.
88
88
  - Missing required `id`, `parentId`/`childIds`, or `options` parameter
89
89
  - Duplicate item identifiers in input
90
90
  - Invalid reference (if `validateReferences` is enabled)
91
- - Cycle or structural error (if `validateTree` or `withDepth` is enabled)
91
+ - Cycle or structural error (if `validateTree` is enabled or `nodeDepthKey` is string)
92
92
 
93
93
 
94
94
  ## Usage
package/index.cjs CHANGED
@@ -33,7 +33,7 @@ function buildTree(items, options) {
33
33
  nodeValueKey = "value",
34
34
  nodeParentKey = "parent",
35
35
  nodeChildrenKey = "children",
36
- withDepth = false,
36
+ nodeDepthKey = false,
37
37
  validateTree = false,
38
38
  validateReferences = false
39
39
  } = options;
@@ -164,6 +164,7 @@ function buildTree(items, options) {
164
164
  roots.push(node);
165
165
  }
166
166
  }
167
+ const withDepth = typeof nodeDepthKey === "string" || typeof nodeDepthKey === "symbol" || typeof nodeDepthKey === "number";
167
168
  if (validateTree || withDepth) {
168
169
  if (roots.length === 0 && nodes.size > 0) {
169
170
  throw new Error("Tree validation failed: detected a cycle.");
@@ -179,7 +180,7 @@ function buildTree(items, options) {
179
180
  }
180
181
  visited.add(node);
181
182
  if (withDepth) {
182
- node.depth = depth;
183
+ node[nodeDepthKey] = depth;
183
184
  }
184
185
  if (node[nodeChildrenKey]) {
185
186
  for (const child of node[nodeChildrenKey]) {
package/index.d.cts CHANGED
@@ -1,51 +1,31 @@
1
- type TreeNode<TValue, TValueKey extends string | number | symbol | false, TParentKey extends string | number | symbol | false, TChildrenKey extends string | number | symbol, TWithDepth extends boolean> = (TValueKey extends false ? (TParentKey extends false ? Omit<TValue, TChildrenKey> & {
2
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
3
- } : Omit<TValue, Exclude<TParentKey, false> | TChildrenKey> & {
4
- [k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>;
5
- } & {
6
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
7
- }) : (TParentKey extends false ? {
1
+ type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
8
2
  [k in Exclude<TValueKey, false>]: TValue;
9
- } & {
10
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
11
- } : {
12
- [k in Exclude<TValueKey, false>]: TValue;
13
- } & {
14
- [k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>;
15
- } & {
16
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
17
- })) & (TWithDepth extends true ? {
18
- depth: number;
3
+ }) & (TParentKey extends false ? {
4
+ [k in Exclude<TParentKey, false>]: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>;
5
+ } : {}) & {
6
+ [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
7
+ } & (TDepthKey extends false ? {
8
+ [k in Exclude<TDepthKey, false>]: number;
19
9
  } : {});
20
- type AccessorReturnType<T, P extends (keyof T) | ((item: T) => any)> = P extends ((item: T) => infer R) ? R : P extends (keyof T) ? T[P] : never;
21
- type IterableKeys<T> = {
10
+ type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends ((item: O) => infer R) ? R : P extends (keyof O) ? O[P] : never;
11
+ type ObjectKeysOfIterableProperties<T> = {
22
12
  [K in keyof T]: T[K] extends Iterable<unknown> & object ? K : never;
23
13
  }[keyof T];
24
- declare function buildTree<TInputValue extends (TIdAccessor extends (keyof TInputValue) ? object : (TNodeValueKey extends false ? object : unknown)), TIdAccessor extends (keyof TInputValue) | ((item: TInputValue) => unknown), TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue, TNodeValueKey extends string | number | symbol | false = 'value', TNodeParentKey extends string | number | symbol | false = 'parent', TNodeChildrenKey extends string | number | symbol = 'children', TWithDepth extends boolean = false, TTreeNode = TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TWithDepth>>(items: Iterable<TInputValue>, options: {
14
+ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TNodeValueKey extends PropertyKey | false = 'value', TNodeParentKey extends PropertyKey | false = 'parent', TNodeChildrenKey extends PropertyKey = 'children', TNodeDepthKey extends PropertyKey | false = false, TInputValue extends (TNodeValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
25
15
  /**
26
16
  * A string key or function used to get the item's unique identifier.
27
17
  */
28
18
  id: TIdAccessor;
29
- /**
30
- * A string key or function used to get the item's parent identifier.
31
- *
32
- * Either `parentId` or `childIds` must be provided.
33
- */
34
- parentId?: (keyof TInputValue) | ((item: TInputValue) => unknown);
35
- /**
36
- * A string key or function to retrieve a list of child identifiers from an item.
37
- *
38
- * Either `parentId` or `childIds` must be provided.
39
- */
40
- childIds?: IterableKeys<TInputValue> | ((item: TInputValue) => (Iterable<unknown> & object) | null | undefined);
41
19
  /**
42
20
  * Maps the input item to a different value stored in the resulting tree node.
43
21
  */
44
22
  nodeValueMapper?: {
45
- (item: TInputValue): TMappedValue;
23
+ (item: NoInfer<TInputValue>): TMappedValue;
46
24
  };
47
25
  /**
48
- * Object key used to store the mapped value in the output tree node.
26
+ * Object key used to store the input item in the output tree node.
27
+ *
28
+ * Set to `false` to merge the item into the node itself.
49
29
  *
50
30
  * Defaults to `'value'`.
51
31
  */
@@ -53,6 +33,8 @@ declare function buildTree<TInputValue extends (TIdAccessor extends (keyof TInpu
53
33
  /**
54
34
  * Object key used to store the reference to the parent node in the output tree node.
55
35
  *
36
+ * Set to `false` to omit parent links.
37
+ *
56
38
  * Defaults to `'parent'`.
57
39
  */
58
40
  nodeParentKey?: TNodeParentKey;
@@ -63,15 +45,17 @@ declare function buildTree<TInputValue extends (TIdAccessor extends (keyof TInpu
63
45
  */
64
46
  nodeChildrenKey?: TNodeChildrenKey;
65
47
  /**
66
- * When enabled, adds a `depth` property to each output node indicating its depth within the tree.
48
+ * Object key used to store a value indicating the node depth in the tree on each output node.
67
49
  * Root nodes have a depth of 0.
68
50
  *
69
- * This also implicitly enables `validateTree`, as depth assignment requires a valid tree structure,
70
- * and both operations share the same traversal logic.
51
+ * Set to `false` to omit depth values.
52
+ *
53
+ * Enables `validateTree`, as depth assignment requires a valid tree structure,
54
+ * and both operations also share the same traversal logic.
71
55
  *
72
56
  * Defaults to `false`.
73
57
  */
74
- withDepth?: TWithDepth;
58
+ nodeDepthKey?: TNodeDepthKey;
75
59
  /**
76
60
  * Validates that the final structure forms a tree.
77
61
  *
@@ -97,14 +81,34 @@ declare function buildTree<TInputValue extends (TIdAccessor extends (keyof TInpu
97
81
  */
98
82
  validateReferences?: boolean;
99
83
  } & ({
84
+ /**
85
+ * A string key or function used to get the item's parent identifier.
86
+ *
87
+ * Either `parentId` or `childIds` must be provided.
88
+ */
100
89
  parentId?: never;
101
- childIds: IterableKeys<TInputValue> | ((item: TInputValue) => (Iterable<unknown> & object) | null | undefined);
90
+ /**
91
+ * A string key or function to retrieve a list of child identifiers from an item.
92
+ *
93
+ * Either `parentId` or `childIds` must be provided.
94
+ */
95
+ childIds: ObjectKeysOfIterableProperties<NoInfer<TInputValue>> | ((item: NoInfer<TInputValue>) => (Iterable<unknown> & object) | null | undefined);
102
96
  } | {
103
- parentId: (keyof TInputValue) | ((item: TInputValue) => unknown);
97
+ /**
98
+ * A string key or function used to get the item's parent identifier.
99
+ *
100
+ * Either `parentId` or `childIds` must be provided.
101
+ */
102
+ parentId: (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown);
103
+ /**
104
+ * A string key or function to retrieve a list of child identifiers from an item.
105
+ *
106
+ * Either `parentId` or `childIds` must be provided.
107
+ */
104
108
  childIds?: never;
105
109
  })): {
106
- roots: TTreeNode[];
107
- nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TTreeNode>;
110
+ roots: TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>[];
111
+ nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>>;
108
112
  };
109
113
  declare const _default: { default: typeof buildTree };
110
114
  export = _default;
package/index.d.mts CHANGED
@@ -1,51 +1,31 @@
1
- type TreeNode<TValue, TValueKey extends string | number | symbol | false, TParentKey extends string | number | symbol | false, TChildrenKey extends string | number | symbol, TWithDepth extends boolean> = (TValueKey extends false ? (TParentKey extends false ? Omit<TValue, TChildrenKey> & {
2
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
3
- } : Omit<TValue, Exclude<TParentKey, false> | TChildrenKey> & {
4
- [k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>;
5
- } & {
6
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
7
- }) : (TParentKey extends false ? {
1
+ type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
8
2
  [k in Exclude<TValueKey, false>]: TValue;
9
- } & {
10
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
11
- } : {
12
- [k in Exclude<TValueKey, false>]: TValue;
13
- } & {
14
- [k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>;
15
- } & {
16
- [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TWithDepth>[];
17
- })) & (TWithDepth extends true ? {
18
- depth: number;
3
+ }) & (TParentKey extends false ? {
4
+ [k in Exclude<TParentKey, false>]: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>;
5
+ } : {}) & {
6
+ [k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
7
+ } & (TDepthKey extends false ? {
8
+ [k in Exclude<TDepthKey, false>]: number;
19
9
  } : {});
20
- type AccessorReturnType<T, P extends (keyof T) | ((item: T) => any)> = P extends ((item: T) => infer R) ? R : P extends (keyof T) ? T[P] : never;
21
- type IterableKeys<T> = {
10
+ type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends ((item: O) => infer R) ? R : P extends (keyof O) ? O[P] : never;
11
+ type ObjectKeysOfIterableProperties<T> = {
22
12
  [K in keyof T]: T[K] extends Iterable<unknown> & object ? K : never;
23
13
  }[keyof T];
24
- export default function buildTree<TInputValue extends (TIdAccessor extends (keyof TInputValue) ? object : (TNodeValueKey extends false ? object : unknown)), TIdAccessor extends (keyof TInputValue) | ((item: TInputValue) => unknown), TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue, TNodeValueKey extends string | number | symbol | false = 'value', TNodeParentKey extends string | number | symbol | false = 'parent', TNodeChildrenKey extends string | number | symbol = 'children', TWithDepth extends boolean = false, TTreeNode = TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TWithDepth>>(items: Iterable<TInputValue>, options: {
14
+ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TNodeValueKey extends PropertyKey | false = 'value', TNodeParentKey extends PropertyKey | false = 'parent', TNodeChildrenKey extends PropertyKey = 'children', TNodeDepthKey extends PropertyKey | false = false, TInputValue extends (TNodeValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
25
15
  /**
26
16
  * A string key or function used to get the item's unique identifier.
27
17
  */
28
18
  id: TIdAccessor;
29
- /**
30
- * A string key or function used to get the item's parent identifier.
31
- *
32
- * Either `parentId` or `childIds` must be provided.
33
- */
34
- parentId?: (keyof TInputValue) | ((item: TInputValue) => unknown);
35
- /**
36
- * A string key or function to retrieve a list of child identifiers from an item.
37
- *
38
- * Either `parentId` or `childIds` must be provided.
39
- */
40
- childIds?: IterableKeys<TInputValue> | ((item: TInputValue) => (Iterable<unknown> & object) | null | undefined);
41
19
  /**
42
20
  * Maps the input item to a different value stored in the resulting tree node.
43
21
  */
44
22
  nodeValueMapper?: {
45
- (item: TInputValue): TMappedValue;
23
+ (item: NoInfer<TInputValue>): TMappedValue;
46
24
  };
47
25
  /**
48
- * Object key used to store the mapped value in the output tree node.
26
+ * Object key used to store the input item in the output tree node.
27
+ *
28
+ * Set to `false` to merge the item into the node itself.
49
29
  *
50
30
  * Defaults to `'value'`.
51
31
  */
@@ -53,6 +33,8 @@ export default function buildTree<TInputValue extends (TIdAccessor extends (keyo
53
33
  /**
54
34
  * Object key used to store the reference to the parent node in the output tree node.
55
35
  *
36
+ * Set to `false` to omit parent links.
37
+ *
56
38
  * Defaults to `'parent'`.
57
39
  */
58
40
  nodeParentKey?: TNodeParentKey;
@@ -63,15 +45,17 @@ export default function buildTree<TInputValue extends (TIdAccessor extends (keyo
63
45
  */
64
46
  nodeChildrenKey?: TNodeChildrenKey;
65
47
  /**
66
- * When enabled, adds a `depth` property to each output node indicating its depth within the tree.
48
+ * Object key used to store a value indicating the node depth in the tree on each output node.
67
49
  * Root nodes have a depth of 0.
68
50
  *
69
- * This also implicitly enables `validateTree`, as depth assignment requires a valid tree structure,
70
- * and both operations share the same traversal logic.
51
+ * Set to `false` to omit depth values.
52
+ *
53
+ * Enables `validateTree`, as depth assignment requires a valid tree structure,
54
+ * and both operations also share the same traversal logic.
71
55
  *
72
56
  * Defaults to `false`.
73
57
  */
74
- withDepth?: TWithDepth;
58
+ nodeDepthKey?: TNodeDepthKey;
75
59
  /**
76
60
  * Validates that the final structure forms a tree.
77
61
  *
@@ -97,13 +81,33 @@ export default function buildTree<TInputValue extends (TIdAccessor extends (keyo
97
81
  */
98
82
  validateReferences?: boolean;
99
83
  } & ({
84
+ /**
85
+ * A string key or function used to get the item's parent identifier.
86
+ *
87
+ * Either `parentId` or `childIds` must be provided.
88
+ */
100
89
  parentId?: never;
101
- childIds: IterableKeys<TInputValue> | ((item: TInputValue) => (Iterable<unknown> & object) | null | undefined);
90
+ /**
91
+ * A string key or function to retrieve a list of child identifiers from an item.
92
+ *
93
+ * Either `parentId` or `childIds` must be provided.
94
+ */
95
+ childIds: ObjectKeysOfIterableProperties<NoInfer<TInputValue>> | ((item: NoInfer<TInputValue>) => (Iterable<unknown> & object) | null | undefined);
102
96
  } | {
103
- parentId: (keyof TInputValue) | ((item: TInputValue) => unknown);
97
+ /**
98
+ * A string key or function used to get the item's parent identifier.
99
+ *
100
+ * Either `parentId` or `childIds` must be provided.
101
+ */
102
+ parentId: (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown);
103
+ /**
104
+ * A string key or function to retrieve a list of child identifiers from an item.
105
+ *
106
+ * Either `parentId` or `childIds` must be provided.
107
+ */
104
108
  childIds?: never;
105
109
  })): {
106
- roots: TTreeNode[];
107
- nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TTreeNode>;
110
+ roots: TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>[];
111
+ nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>>;
108
112
  };
109
113
  export {};
package/index.mjs CHANGED
@@ -10,7 +10,7 @@ function buildTree(items, options) {
10
10
  nodeValueKey = "value",
11
11
  nodeParentKey = "parent",
12
12
  nodeChildrenKey = "children",
13
- withDepth = false,
13
+ nodeDepthKey = false,
14
14
  validateTree = false,
15
15
  validateReferences = false
16
16
  } = options;
@@ -141,6 +141,7 @@ function buildTree(items, options) {
141
141
  roots.push(node);
142
142
  }
143
143
  }
144
+ const withDepth = typeof nodeDepthKey === "string" || typeof nodeDepthKey === "symbol" || typeof nodeDepthKey === "number";
144
145
  if (validateTree || withDepth) {
145
146
  if (roots.length === 0 && nodes.size > 0) {
146
147
  throw new Error("Tree validation failed: detected a cycle.");
@@ -156,7 +157,7 @@ function buildTree(items, options) {
156
157
  }
157
158
  visited.add(node);
158
159
  if (withDepth) {
159
- node.depth = depth;
160
+ node[nodeDepthKey] = depth;
160
161
  }
161
162
  if (node[nodeChildrenKey]) {
162
163
  for (const child of node[nodeChildrenKey]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-tree-builder",
3
- "version": "2.0.0-alpha.4",
3
+ "version": "2.0.0-beta.0",
4
4
  "description": "Easily construct highly customizable bi-directional tree structures from iterable data.",
5
5
  "types": "./index.d.mts",
6
6
  "module": "./index.mjs",