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

react-context-compressor

v0.2.1

Published

Mechanical, zero-dependency client-side compressor + sanitizer that turns React/JS app state into a minimal, safe payload for LLMs.

Readme

react-context-compressor

npm version CI minzipped size zero dependencies license

A lightweight, zero-dependency JS/TS utility that mechanically strips non-essential UI data and deep nesting from your application state and sanitizes sensitive fields (tokens, credentials, private IDs) — producing a minimal, safe payload to send to an LLM. It cuts token costs and avoids context-window overflows.

It is 100% mechanical: no network calls, no models, no AI summarization (see Non-goals). A framework-agnostic core (.) plus a thin React bindings layer (./react).

Before: 17,763 chars  →  After: 402 chars   (97.7% smaller, secrets redacted)

(from examples/demo.mjs — run it yourself.)

Why

Sending application state to an LLM is expensive (you pay per token) and fragile (oversized blobs overflow the context window). State is also full of data the model doesn't need — UI flags, caches, deep view-models — and sometimes carries secrets that must never leave the client. react-context-compressor does the shrinking and the sanitizing in one mechanical, deterministic pass.

Install

npm install react-context-compressor

react is an optional peer dependency (>= 17) — only needed for the ./react entry. The core has zero runtime dependencies.

Quick start

Core (any JS/TS — React not required)

import { compress } from "react-context-compressor";

const payload = compress(appState, {
  maxDepth: 3,
  maxArrayLength: 5,
  dropEmpty: true,
  strip: [/^_/], // drop internal/private keys
});

// `payload` is a minimal, secret-free plain object — JSON.stringify and send it.

React

import { useCompressedContext } from "react-context-compressor/react";

function useLlmContext(state: AppState) {
  // Memoized: recomputes only when `state` or the options content changes.
  return useCompressedContext(state, {
    maxDepth: 3,
    maxArrayLength: 5,
    dropEmpty: true,
  });
}

Works with any state source — Redux, Zustand, Context, useState. The hook is pure (no DOM, no side effects), so it's safe under SSR and React Server Components, on React 17 / 18 / 19.

What it does

| Transform | Option | Behavior | | ------------ | -------------------------------- | --------------------------------------------------------------------------------------- | | Cap depth | maxDepth | Nodes deeper than the cap become "[Object]" / "[Array]". Default 100. | | Cap arrays | maxArrayLength | Longer arrays are truncated and a "[+N more]" marker appended. Default unlimited. | | Strip keys | strip | Remove keys by exact string or RegExp, at any depth. | | Drop empties | dropEmpty | Drop null / undefined / "" / [] / {}. Default false. | | Sanitize | sanitize, defaultSanitize, … | Redact/remove sensitive fields (see below). |

It also handles awkward inputs predictably: circular references"[Circular]", a throwing getter"[Getter]" (never crashes), Date → a copy, Map → plain object, Set / TypedArray → array, RegExp → its source string, Error{ name, message }, functions/symbols dropped, BigInt → string. The input object is never mutated, and output is deterministic.

Sanitization (data safety)

By default, common sensitive field names are redacted to "[REDACTED]" before their value is ever read:

compress({ user: "ada", apiKey: "sk-live-123", nested: { password: "p@ss" } });
// → { user: "ada", apiKey: "[REDACTED]", nested: { password: "[REDACTED]" } }

The built-in deny-list covers (case-insensitive): password, secret, token (and accessToken/authToken/…), apiKey/accessKey/privateKey/signingKey, jwt, authorization, bearer, credentials, cookie, sessionId, otp/totp/mfa, mnemonic/seedPhrase, recovery/backup codes, hmac, signature, connectionString/dsn/db URLs, ssn, creditCard/cardNumber, cvv, pin, iban, routing/account numbers, passport, taxId. It's tuned to avoid false positives like author, dashboard, secretary, tokenCount, or promptTokens.

// Extend the deny-list, or remove instead of redact:
compress(state, {
  sanitize: ["employeeId", /internal/i],
  sanitizeMode: "remove",
});

// Replace the built-ins entirely:
compress(state, { defaultSanitize: false, sanitize: [/^secret_/] });

Security note & limits

Sanitization is key-name-driven and mechanical. It is intentionally not:

  • Value-based — a secret stored under an innocuous key, or as a bare array / Set element (no key), is not detected. Match it explicitly via sanitize.
  • A defense against deliberate homoglyph keys — keys are NFKC-normalized and zero-width-stripped (defeating fullwidth/zero-width evasion), but not Cyrillic look-alikes.

Only own enumerable string keys are processed; Symbol-keyed and non-enumerable properties are dropped (never emitted). Treat the deny-list as a strong default, and extend it for your domain.

API

compress(state, options?) → unknown

Mechanically compress + sanitize state. Pure, deterministic, never mutates input.

useCompressedContext(state, options?) → unknown (./react)

Memoized React hook wrapping compress. Recomputes only when the state reference or the options content changes (an inline options literal is fine).

CompressOptions

| Option | Type | Default | Notes | | ----------------- | ------------------------- | -------------- | --------------------------------------------------------------------------- | | maxDepth | number | 100 | Infinity to disable. | | maxArrayLength | number | Infinity | | | strip | Array<string \| RegExp> | [] | String = exact (case-sensitive) key; RegExp = pattern. | | dropEmpty | boolean | false | | | sanitize | Array<string \| RegExp> | [] | String = case-insensitive exact; RegExp = pattern. Adds to the deny-list. | | defaultSanitize | boolean | true | Toggle the built-in deny-list. | | sanitizeMode | "redact" \| "remove" | "redact" | | | redactedValue | string | "[REDACTED]" | Used when redacting. |

Non-goals

This library will never perform semantic / AI-powered summarization, make network calls, or run a local model. It is a mechanical, zero-cost, client-side object parser — that's the whole point (it saves you money before the network layer). See SPLIT-PLAN §2 (out of scope).

Contributing & releasing

Development uses Git Flow: feature branches → develop (PR required), releases promote developmain.

npm install
npm test          # vitest
npm run typecheck && npm run lint && npm run build && npm run size

Each change adds a changeset (npm run changeset). To cut a release: branch release/X.Y.Z off develop, run npx changeset version (bumps the version + updates the changelog), open a PR to main, merge, then tag vX.Y.Z. Pushing the tag triggers .github/workflows/release.yml, which publishes to npm with provenance (once an NPM_TOKEN secret is configured).

License

MIT