neotraverse@1.0.0
Major Changes
-
7f7d18a: # 1.0 — the functional API is now the default
neotraverse 1.0 makes the tree-shakeable functional API the main export and demotes the class-based API to a thin, deprecated, opt-in import. The functions, their behaviour, the security hardening, and the performance work from 0.7 are unchanged — the entry points moved.
⚠️ Breaking changes0.7 1.0 import traverse from 'neotraverse'(classic default)import traverse from 'neotraverse/legacy'import { Traverse } from 'neotraverse'import { Traverse } from 'neotraverse/modern'import { map, clone, … } from 'neotraverse/modern'import { map, clone, … } from 'neotraverse'- The root export (
neotraverse) is now functional-only —map,clone,merge,diff,get/set,walk,sanitize, and the rest of the helpers, plus theTraverseOptions/TraverseContext/TraverseNodeTypetypes. No default export, noTraverseclass. neotraverse/modernnow exports ONLY the deprecatedTraverseclass (and theTraverseContext/TraverseOptionstypes its signatures use). The functional helpers it used to re-export move to the root. The class is also trimmed to the same method set as the legacyTraverse(get/has/set/map/forEach/reduce/paths/nodes/clone) — a deprecated API should not gain new powers. It will be removed in v2.neotraverse/legacyis unchanged: the classictraverse-compatible drop-in (ES2015, CJS + ESM).require('neotraverse')(CommonJS) still resolves here. The legacy build intentionally will not receive the modern security/performance work — it stays byte-for-byte behaviour-compatible with the originaltraverse.- No more minified build. The package ships unminified ESM only; consumers minify in their own bundler. The export map is simpler as a result (no
production/developmentconditions, nodist/min).
Internal
- The functional implementation is split across small modules at the package root (
utils/clone/context/path/ops) instead of one large file; the legacy build lives undersrc/legacy/.dist/modern.jsreuses the root build's shared chunk instead of re-bundling the functional API. No change to what consumers import.
- The root export (
Minor Changes
-
7f7d18a: # neotraverse/safe: a stack-safe, memory-bounded traversal core
A new opt-in entry point,
neotraverse/safe. It is a companion to the default functional API (not a replacement), for input that is deep, untrusted, huge, or only partially consumed.The default
neotraversewalk is recursive, which is why it is fast, but a recursive walker overflows the call stack on deep enough input.neotraverse/saferuns on an iterative engine, so it traverses arbitrarily deep trees that crash a recursive walker. Measured: the default overflows past ~2,000 levels;/safehandles 200,000+. It is also lazy and copy-on-write.What it ships (twelve exports)
visit: a lazy iterator ofVisitrecords that composes with native ES2025 iterator helpers (.filter/.map/.find/.take/.toArray,Map.groupBy,for-of+break), prunes withv.skip(), and matches a globpattern.transform/transformAsync: copy-on-write rewriting. Untouched subtrees are shared with the input, andtransform(x, () => {}) === x. Edits are branded commands (replace/remove/skip/stop) returned from a destructurableeditfactory, including a pattern-keyed rules record form.get/set/has: one path family (dot string, JSON Pointer, or key array), template-literal typed, with copy-on-writeset.clone/equal/merge/diff/patch/resolveRefs: structural ops.mergeadds array strategies (concat/union/by) and per-patternatoverrides.
The honest trade-off
/safeis not a universal upgrade. On a full eager scan it runs at roughly 0.8x the default (still about 4x faster than the originaltraverse), and materializing a whole tree costs a little more memory. It wins on stack safety, on early-exit and streaming memory (about 6x less on afilterthentakechain), and on copy-on-write edits.Requires Node 22+ or evergreen browsers (it uses native ES2025 iterator helpers). See the guide: https://neotraverse.puruvj.dev/guide/safe