npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rimbu/deep

v2.0.5

Published

Tools to use handle plain JS objects as immutable objects

Downloads

16,665

Readme

npm version License Types Included Node Bun ESM + CJS

@rimbu/deep

Immutable, type-safe utilities for deeply patching, matching, and selecting from plain JavaScript objects.

@rimbu/deep gives you a set of composable tools – Patch, Match, Path, Selector, Protected, and Tuple – to treat plain objects as if they were immutable, deeply typed data structures. You can:

  • Apply immutable updates to nested structures using a flexible Patch notation.
  • Express rich match conditions over objects and arrays using Match.
  • Access nested properties safely using type‑checked string Paths.
  • Build typed projections from objects using Selector shapes.
  • Protect values at compile time from accidental mutation using Protected.
  • Work ergonomically with tuples and fixed‑length arrays via Tuple.

Use it whenever you want the convenience of plain objects, but with deep type safety, immutable semantics, and refactor‑friendly string paths.


Table of Contents

  1. Why @rimbu/deep?
  2. Feature Highlights
  3. Quick Start
  4. Core Concepts & Types
  5. Deep API Helpers
  6. Installation
  7. Ecosystem & Further Reading
  8. Contributing
  9. License

Why @rimbu/deep?

Plain objects are great, but they quickly become painful when:

  • You need to update nested fields immutably (e.g. in Redux‑style state).
  • You want type‑safe string paths like 'a.b.c[0]?.d' instead of ad‑hoc helpers.
  • You’d like to pattern‑match complex structures without a forest of if/switch checks.
  • You want to project and reshape data (e.g. API responses) into well‑typed views.

@rimbu/deep focuses on:

  • Type‑driven paths and selectorsPath and Selector are derived from your data types.
  • Immutable patchingPatch lets you describe updates declaratively and apply them in one go.
  • Expressive matchingMatch supports nested objects, arrays, tuples, and compound predicates.
  • Compile‑time protectionProtected<T> makes entire object graphs appear readonly to TypeScript.

If you find yourself writing a lot of manual cloning, deep property access, or matcher utilities, @rimbu/deep is a drop‑in improvement.


Feature Highlights

  • Deep immutable patching with patch and patchAt: describe updates using nested objects/arrays and functions instead of manual cloning.
  • Typed string paths with Path.Get and Path.Set: only valid paths for your data type compile.
  • Structured matching with match: supports nested objects, tuple/array traversal, and compound matchers (every, some, none, single).
  • Selection & projection with select and Selector: derive new shapes from existing data using path strings, functions, or nested selector objects.
  • Compile‑time protection with Protected and protect: make values deeply readonly at the type level while still using the underlying runtime value.
  • Tuple helpers with Tuple: ergonomics around fixed‑length tuples (construction, indexing, updates, etc.).

Quick Start

import { Deep } from '@rimbu/deep';

const input = { a: 1, b: { c: true, d: 'a' } } as const;

// Immutable deep patch
const updated = Deep.patch(input, [{ b: [{ c: (v) => !v }] }]);
// => { a: 1, b: { c: false, d: 'a' } }

// Type-safe nested get
const cValue = Deep.getAt(input, 'b.c'); // boolean

// Pattern matching
if (Deep.match(input, { b: { c: true } })) {
  // ...
}

// Selection / projection
const projected = Deep.select(input, { flag: 'b.c', label: 'b.d' });
// projected: { flag: boolean; label: string }

Try Rimbu (including @rimbu/deep) live in the browser using the Rimbu Sandbox on CodeSandbox.


Core Concepts & Types

Exported Types & Namespaces

From @rimbu/deep’s main entrypoint you have access to:

| Name | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------------ | | Patch<T, C = T> | Type describing allowed patch shapes for a value of type T. | | Match<T, C = Partial<T>> | Type describing allowed matchers for values of type T. | | Path | Namespace containing Path.Get<T>, Path.Set<T>, and Path.Result<T, P> utilities for string paths. | | Selector<T> | Type describing allowed selector shapes for values of type T. | | Protected<T> | Deeply readonly/“protected” view of T for compile‑time mutation safety. | | Tuple<T extends Tuple.Source> | Tuple wrapper with helper types and functions under the Tuple namespace. | | Deep | Convenience namespace exposing the main deep utilities (patch, match, getAt, select, etc.). |

See the Deep overview docs and API reference for the full surface.

Patching with patch and Patch

import { Deep, type Patch } from '@rimbu/deep';

type State = {
  count: number;
  user?: { name: string; active: boolean };
};

const state: State = { count: 1, user: { name: 'Ada', active: true } };

const patchItem: Patch<State> = [
  { count: (v) => v + 1 },
  { user: [{ active: false }] },
];

const next = Deep.patch(state, patchItem);
// => { count: 2, user: { name: 'Ada', active: false } }

Patches can be:

  • Direct replacement values (T).
  • Functions (current, parent, root) => newValue.
  • Nested objects / arrays describing which fields or tuple indices to update.

Matching with match and Match

import { Deep, type Match } from '@rimbu/deep';

type Item = { id: number; tags: string[] };

const items: Item[] = [
  { id: 1, tags: ['a', 'b'] },
  { id: 2, tags: ['b'] },
];

const matcher: Match<Item> = {
  tags: { someItem: (tag) => tag === 'a' },
};

const result = items.filter((item) => Deep.match(item, matcher));
// => only items containing tag 'a'

Match supports:

  • Plain object matchers ({ a: 1, b: { c: true } }).
  • Function matchers (value, parent, root) => boolean | matcher.
  • Array/tuple matchers and traversal helpers such as someItem, everyItem, noneItem, singleItem.
  • Compound matchers like ['every', matcher1, matcher2].

Paths with Path.Get, Path.Set and getAt / patchAt

import { Deep, type Path } from '@rimbu/deep';

type Model = { a: { b: { c: number }[] } };
const value: Model = { a: { b: { c: [5, 6] } } as any };

// Typed paths
const path: Path.Get<Model> = 'a.b.c[1]?.d'; // compile-time checked

// Reading
const result = Deep.getAt(value, 'a.b.c[0]'); // number | undefined

// Patching at a path
const updated = Deep.patchAt(value, 'a.b.c', (arr) => [...arr, 7]);

Path.Result<T, P> gives you the resulting type at a given path P in T.

Selection with Selector and select

import { Deep, type Selector } from '@rimbu/deep';

type Source = {
  a: { b: number; c: string };
  meta: { createdAt: string };
};

const source: Source = {
  a: { b: 1, c: 'x' },
  meta: { createdAt: '2024-01-01' },
};

const selector: Selector<Source> = {
  value: 'a.b',
  label: 'a.c',
  created: 'meta.createdAt',
};

const view = Deep.select(source, selector);
// view: { value: number; label: string; created: string }

Selectors can be:

  • String paths.
  • Functions (value: Protected<T>) => any.
  • Arrays or objects composed of other selectors.

Protection with Protected and protect

import { Deep, type Protected } from '@rimbu/deep';

type Data = { a: { b: number[] } };

const data: Data = { a: { b: [1, 2] } };
const protectedData: Protected<Data> = Deep.protect(data);

// protectedData.a.b.push(3); // TypeScript error – `b` is readonly

Protected<T> is a type‑level construct: it does not freeze the value at runtime, but helps prevent accidental mutations in your code.


Deep API Helpers

All top‑level utilities are also available through the Deep namespace:

import { Deep } from '@rimbu/deep';

// Functional helpers
const incCount = Deep.patchWith<{ count: number }>([{ count: (v) => v + 1 }]);
const onlyActive = Deep.matchWith({ active: true });
const getName = Deep.getAtWith<{ user: { name: string } }>('user.name');

// Typed, curried API with withType
const s = { a: 1, b: { c: 'a', d: true } };
const api = Deep.withType<typeof s>();

const next = api.patchWith([{ b: [{ d: (v) => !v }] }])(s);
// => { a: 1, b: { c: 'a', d: false } }

The Deep namespace mirrors the main exports:

  • Deep.patch, Deep.patchAt, Deep.patchWith, Deep.patchAtWith
  • Deep.match, Deep.matchAt, Deep.matchWith, Deep.matchAtWith
  • Deep.getAt, Deep.getAtWith
  • Deep.select, Deep.selectAt, Deep.selectWith, Deep.selectAtWith
  • Deep.withType<T>() for creating a typed, curried API.

Installation

Node / Bun / npm / Yarn

npm install @rimbu/deep
# or
yarn add @rimbu/deep
# or
bun add @rimbu/deep
# or
deno add npm:@rimbu/deep

Then:

import { Deep } from '@rimbu/deep/mod.ts';

Browser / ESM

@rimbu/deep ships both ESM and CJS builds. Use it with any modern bundler (Vite, Webpack, esbuild, Bun, etc.) or directly in Node ESM projects.


Ecosystem & Further Reading

  • Part of the broader Rimbu ecosystem – interoperates with @rimbu/core, @rimbu/collection-types, and other collection packages.
  • Ideal for modelling immutable application state, selectors, and matchers in complex domains.
  • Learn more in the Deep overview docs and the Deep API reference.

Contributing

We welcome contributions! See the Contributing guide for details.

Made with contributors-img.


License

MIT © Rimbu contributors. See LICENSE for details.