feat!: v4.0 breaking API cleanup + explicitApi

BREAKING changes to the core:
- treeIterator is now a read-only `val`; added `iterator(order)` and use `asSequence(order)`.
- removeChild() only removes a direct child of the receiver; added `detach()` to unhook a node.
- addChild() rejects re-parenting and cycles (throws TreeNodeException); detach() first to move.
- clear() no longer nulls the receiver's own parent; only removes descendants.
- path() returns List<TreeNode<T>>? (null) instead of throwing.

Also:
- Enable strict explicitApi() across core + both modules; add explicit `public` modifiers.
- Update tests for the new contracts + add TreeNodeV4Test; refresh .api baselines.
- README + CHANGELOG (with migration notes); bump version to 4.0.0.

47 JVM tests green.
This commit is contained in:
2026-06-07 18:47:40 +02:00
parent c9bbea59b0
commit 69d19f89e3
22 changed files with 262 additions and 97 deletions

View File

@@ -104,9 +104,8 @@ World
val root = TreeNode("root")
// ... build your tree
// Choose iteration order (default is PreOrder)
root.treeIterator = TreeNodeIterators.PostOrder
for (node in root) println(node.value)
// Choose iteration order per call (the default order is set in the constructor and is read-only)
for (node in root.asSequence(TreeNodeIterators.PostOrder)) println(node.value)
// Utilities
root.nodeCount() // number of descendants
@@ -114,10 +113,10 @@ root.height() // longest path to a leaf (in edges)
root.depth() // distance from current node to the root
val path = root.path(root.children.first()) // nodes from descendant up to root
// Mutations
// Mutations — removeChild removes a *direct* child; detach() unhooks a node from wherever it lives
val child = root.children.first()
root.removeChild(child)
root.clear() // remove entire subtree
root.removeChild(child) // child is now detached from root
root.clear() // remove all descendants of root
```
### Lazy traversal with Sequence