Skip to content

Getting Started

Install

bash
npm install constancy

Requirements: Node.js ≥ 20. Zero dependencies. Ships as ESM + CommonJS.

typescript
// ESM
import { deepFreeze, immutableView, snapshot } from 'constancy';

// CommonJS
const { deepFreeze, immutableView, snapshot } = require('constancy');

Your first freeze

deepFreeze recursively freezes an object in place. No clone — same reference, now immutable.

typescript
import { deepFreeze } from 'constancy';

const config = deepFreeze({
  host: 'localhost',
  db: { port: 5432, name: 'mydb' },
});

config.host = 'evil';        // TypeError: Cannot assign to read only property
config.db.port = 9999;       // TypeError: Cannot assign to read only property

The object and all nested objects are frozen. The original reference is returned.

Try a view

immutableView wraps an object in a Proxy. Any mutation attempt through the proxy throws — but the original reference stays mutable.

typescript
import { immutableView } from 'constancy';

const original = { count: 0 };
const view = immutableView(original);

view.count = 1;     // TypeError: object is immutable
original.count = 1; // Works — original is still mutable
view.count;         // 1 — view reflects the mutation

Use immutableView when you control the original and want mutation prevention at a specific call site. If untrusted code could retain the original reference, use snapshot instead.

Try a snapshot

snapshot clones the object then deep-freezes the clone. The original is untouched; the snapshot is independent.

typescript
import { snapshot } from 'constancy';

const original = { user: { isVip: false } };
const snap = snapshot(original);

original.user.isVip = true;  // original mutated
snap.user.isVip;             // false — snapshot is independent
snap.user.isVip = true;      // TypeError: frozen

The clone uses structuredClone internally. Non-cloneable values (functions, DOM nodes, identity Symbols) throw at snapshot time.

Next steps

Released under the MIT License.