| name | use-deep6 |
| description | Use the deep6 library for deep equality, deep cloning, unification, and pattern matching with logical variables in a JavaScript/TypeScript project. Use when adding structural comparison, structured clone with circular references, extensible pattern matching with extraction, or unification-based logic to a project that depends on `deep6`. |
Use deep6
deep6 is a zero-dependency ES-modules mini-library for advanced deep
equivalence, deep cloning, unification, and pattern matching with logical
variables and wildcards. It handles circular references, Map, Set, URL,
typed arrays, Date, RegExp, symbols, and property descriptors. It runs in
Node.js, Deno, Bun, and browsers.
When in doubt, the canonical references are node_modules/deep6/llms.txt
(concise) and node_modules/deep6/llms-full.txt (complete with examples).
Read them before guessing.
When to reach for deep6
Pick deep6 when the problem involves structural comparison or transformation
of arbitrary JavaScript values:
- Deep equality beyond what
=== or JSON.stringify can express —
including Map, Set, Date, RegExp, typed arrays, symbol keys, and
cycles.
- Deep cloning of values that
structuredClone can't or won't handle the
way you want (custom types, non-enumerable props, symbol keys, selectable
options).
- Pattern matching with extraction — match a shape and pull bound values
out via logical variables, rather than hand-rolling
if/destructuring.
- Unification — bidirectional matching where either side may contain
variables (the building block for rule engines, type inference, constraint
search; this is what
yopl is built on).
- Custom type support — register your own comparators / cloners via the
extensible registry instead of forking a library.
Do not reach for deep6 for shallow comparisons, primitive equality, or
cases where structuredClone is sufficient.
Installation
npm install deep6
deep6 requires Node 18+ (or modern Deno / Bun) and "type": "module" (or
.mjs files). It has zero runtime dependencies.
Imports
import equal, {clone, match, any, _} from 'deep6';
import unify, {Variable, variable, Unifier, open, soft} from 'deep6/unify.js';
import walk from 'deep6/traverse/walk.js';
import deepClone from 'deep6/traverse/clone.js';
import preprocess from 'deep6/traverse/preprocess.js';
import assemble from 'deep6/traverse/assemble.js';
import deref from 'deep6/traverse/deref.js';
import matchString from 'deep6/unifiers/matchString.js';
import matchTypeOf from 'deep6/unifiers/matchTypeOf.js';
import matchInstanceOf from 'deep6/unifiers/matchInstanceOf.js';
import matchCondition from 'deep6/unifiers/matchCondition.js';
import ref from 'deep6/unifiers/ref.js';
import replaceVars from 'deep6/utils/replaceVars.js';
Deep equality
import equal from 'deep6';
equal({a: 1, b: [2, 3]}, {b: [2, 3], a: 1});
equal(new Date('2020-01-01'), new Date('2020-01-01'));
equal(/abc/gi, /abc/gi);
equal(new Map([['a', 1]]), new Map([['a', 1]]));
const a = {};
a.self = a;
const b = {};
b.self = b;
equal(a, b);
Useful options:
circular: true — handle cycles (default true).
symbols: true — also compare symbol-keyed properties.
loose: true — use == for primitives.
ignoreFunctions: true — ignore function-valued properties.
signedZero: true — distinguish +0 from -0.
Deep cloning
import {clone} from 'deep6';
const x = {a: 1, b: [2, {c: 3}], d: new Map([['k', 'v']])};
const y = clone(x);
const r = {a: 1};
Object.defineProperty(r, 'b', {value: 2, enumerable: false});
const q = clone(r, {allProps: true, symbols: true});
Prefer deep6's clone over structuredClone when you need symbol keys,
non-enumerable properties, custom registered types, or fine-grained options.
Pattern matching
match(object, pattern) does open matching by default — extra keys in
the object are fine, missing keys in the pattern are fine.
import {match, any, _} from 'deep6';
import matchString from 'deep6/unifiers/matchString.js';
import matchTypeOf from 'deep6/unifiers/matchTypeOf.js';
match({a: 1, b: 2, c: 3}, {a: 1});
match({a: 1, b: {c: 2}}, {a: 1, b: any});
match({a: 'hello'}, {a: matchString(/^h/i)});
match({n: 42}, {n: matchTypeOf('number')});
any (alias _) is the wildcard. Use the unifiers/* modules for
regex-based, type-based, instance-based, or predicate-based matching.
Unification with logical variables
When you need to extract values from a match, drop down to unify.
import unify, {variable} from 'deep6/unify.js';
import assemble from 'deep6/traverse/assemble.js';
const X = variable('X');
const Y = variable('Y');
const env = unify({user: {name: X, age: Y}}, {user: {name: 'Ada', age: 36}});
if (env) {
assemble(X, env);
assemble(Y, env);
}
unify returns an Env (a binding environment) on success and null on
failure. Use assemble(variable, env) to walk the bindings and produce a
plain JavaScript value (deeply, including nested terms).
Variables can appear on either side and can be unified against each
other:
const X = variable('X'),
Y = variable('Y');
const env = unify({a: X, b: 2}, {a: 1, b: Y});
assemble(X, env);
assemble(Y, env);
Open / soft matching
By default unify does strict structural matching. For pattern-style
matching where extra keys are allowed:
import unify, {open, soft} from 'deep6/unify.js';
unify(open({a: 1}), {a: 1, b: 2});
unify(soft({a: 1}), {a: 1, b: 2});
match(obj, pattern) is essentially unify(open(pattern), obj) followed by
a success check.
Extending the registries
Both unify and clone use extensible registries keyed by constructor.
import unify from 'deep6/unify.js';
import clone from 'deep6/traverse/clone.js';
class Money {
constructor(amount, currency) {
this.amount = amount;
this.currency = currency;
}
}
unify.registry.push(Money, (l, r, env) => (l.currency === r.currency && l.amount === r.amount ? env : null));
clone.registry.push(Money, (val, ctx) => new Money(val.amount, val.currency));
Register once at startup. Both registries fall through in insertion order, so
more specific types should be registered last (or before a less-specific
catch-all).
Common patterns
- Equality with custom options — toggle
symbols, loose,
ignoreFunctions, signedZero on equal rather than writing wrappers.
- Snapshot comparison in tests —
equal(actual, expected) is a drop-in
replacement for assertion libraries' deepEqual with broader type support.
- Pattern + extract — combine
unify + variable + assemble instead
of nested destructuring with optional chaining.
- Custom guards —
matchCondition(v => typeof v === 'number' && v > 0)
for ad-hoc predicates inside a pattern.
- Cross-field constraints — use
ref() to require two positions in a
pattern to be equal.
Pitfalls
match is open by default. Missing pattern keys are not failures.
Use full unify (without open) when you need strict shape matching.
- Circular references are on by default. That's almost always what you
want, but it costs a
WeakMap. Disable with {circular: false} for hot
paths over known-acyclic data.
- Symbols are off by default in both
equal and clone. Pass
{symbols: true} if symbol keys are part of identity.
- Variables are not values. A bare
Variable instance is a placeholder;
always go through assemble(v, env) (or v.get(env) for a shallow read)
after a successful unify.
- Registry order matters. Entries are tried in insertion order; register
specific types after generic ones.
clone follows the registry. A type with no registered cloner falls
back to generic structural cloning, which may not preserve class identity.
Register a cloner if you need instanceof to keep working.
Picking an entry point
| Need | Use |
|---|
| "Are these two values structurally equal?" | equal (default export of deep6) |
| "Give me a deep copy" | clone from deep6 |
| "Does this value match this shape?" | match + any / unifiers/* |
| "Match and extract bound values" | unify + variable + assemble |
| "Walk a structure with my own visitor" | deep6/traverse/walk.js |
| "Pre-process a pattern (intern shared subs)" | deep6/traverse/preprocess.js |
| "Replace variables in a structure with binds" | deep6/utils/replaceVars.js |
| "Add support for my own class" | unify.registry.push / clone.registry.push |
Where to look next
node_modules/deep6/llms.txt — concise API reference.
node_modules/deep6/llms-full.txt — full API reference with examples.
node_modules/deep6/AGENTS.md — rules, conventions, architecture quick ref.
node_modules/deep6/ARCHITECTURE.md — module map and algorithm details.
- Wiki — per-module documentation.