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

@aforemendude/diff

v1.1.2

Published

A small, typed line and Unicode text diff library.

Readme

Diff

A small, typed text diff library for Node.js and browsers. It provides line-level and Unicode grapheme-level diffs, plus optional Diff Match Patch-style semantic and efficiency cleanup.

Requirements

  • Node.js 20 or newer when running on Node.js
  • Intl.Segmenter in the target Node.js or browser runtime

Installation

npm install @aforemendude/diff

Browser usage

The library also works in browser applications when included through a bundler. It has no runtime dependencies and its runtime code does not use Node.js-only APIs, so it can be imported from browser application code in the same way as in the examples below. Target browsers must provide Intl.Segmenter.

Quick start

import { cleanupSemantic } from '@aforemendude/diff/cleanup';
import { DELETE, EQUAL, INSERT, diffGraphemes } from '@aforemendude/diff/grapheme';

const changes = cleanupSemantic(diffGraphemes('The cat sat.', 'The dog sat.'));
[
  [EQUAL,  ['T', 'h', 'e', ' ']],
  [DELETE, ['c', 'a', 't']],
  [INSERT, ['d', 'o', 'g']],
  [EQUAL,  [' ', 's', 'a', 't', '.']],
]

Diffs use compact tuples. The first item is an operation and the second is the affected array of tokens:

export const DELETE = -1;
export const EQUAL = 0;
export const INSERT = 1;

export type DiffOperation = -1 | 0 | 1;
export type Diff = readonly [operation: DiffOperation, tokens: readonly string[]];

Entries with an empty token array are omitted, and adjacent entries with the same operation are merged. An empty string can still be a valid token - for example, it represents a blank line in a line diff.

API

The package exposes feature-specific subpaths and deliberately has no root entry point. Importing one diff engine does not load either cleanup implementation or the other diff engine:

| Subpath | Runtime exports | | ----------------------------- | ------------------------------------------------------------------- | | @aforemendude/diff/line | diffLines, DELETE, EQUAL, INSERT | | @aforemendude/diff/grapheme | diffGraphemes, DELETE, EQUAL, INSERT | | @aforemendude/diff/cleanup | cleanupSemantic, cleanupEfficiency, DELETE, EQUAL, INSERT |

Each subpath supports both ESM import and CommonJS require. ESM consumers receive native side-effect-free modules, allowing bundlers to remove unused named exports. Each entry also exports the Diff and DiffOperation types; feature-specific option types are exported from the subpath that uses them, and both diff subpaths export the shared DiffAlgorithm type.

export type DiffAlgorithm = 'adaptive' | 'myers' | 'sparse';
export type LineEnding = '\r' | '\n' | '\r\n';

export function diffLines(
  before: string,
  after: string,
  options?: {
    readonly algorithm?: DiffAlgorithm;
    readonly lineEnding?: LineEnding;
    readonly optimizeTrivialCases?: boolean;
  },
): readonly Diff[];

export function diffGraphemes(
  before: string,
  after: string,
  options?: {
    readonly algorithm?: DiffAlgorithm;
    readonly locale?: Intl.LocalesArgument;
    readonly optimizeTrivialCases?: boolean;
  },
): readonly Diff[];

export function cleanupSemantic(
  diffs: readonly Diff[],
  options?: { readonly locale?: Intl.LocalesArgument },
): readonly Diff[];

export function cleanupEfficiency(diffs: readonly Diff[], options?: { readonly editCost?: number }): readonly Diff[];

Mutation and ownership guarantees

The public methods never mutate their arguments. This includes option objects and, for the cleanup methods, the input diff array, its tuples, and their token arrays.

Every call returns a freshly allocated result. The top-level array, every tuple, and every token array are distinct from the arguments, from arrays elsewhere in the same result, and from arrays returned by other calls. Consequently, even if the TypeScript readonly types are bypassed, changing one returned array cannot change an input, another entry in that result, or the result of another call.

diffLines(before, after, options?)

Computes a line-level diff using one exact line-ending sequence as the separator throughout both inputs. The supported line endings are \r, \n, and \r\n; the default is \n. Other newline characters remain part of the surrounding line content.

Line tokens never contain the selected ending. Tokenization splits on the separator and removes exactly one trailing empty segment. It does not remove every trailing empty segment, because preceding empty segments represent real blank lines. With the default \n, tokenization is:

| Input | Tokens | | --------- | ----------- | | '' | [] | | 'a' | ['a'] | | 'a\n' | ['a'] | | 'a\n\n' | ['a', ''] | | '\n' | [''] |

Consequently, adding one selected ending after a nonempty final line is insignificant, while an additional ending represents a blank line:

import { EQUAL, INSERT, diffLines } from '@aforemendude/diff/line';

diffLines('a', 'a\n');
diffLines('a\n', 'a\n\n');

The results are, respectively:

[[EQUAL, ['a']]]

[
  [EQUAL, ['a']],
  [INSERT, ['']],
]

Pass the ending explicitly for CRLF or CR text. For example, diffLines('a', 'a\r\n', { lineEnding: '\r\n' }) returns [[EQUAL, ['a']]].

Removing insertion entries reconstructs the first input's canonical line-token stream; removing deletion entries reconstructs the second. The original separators cannot be reconstructed from a line diff because they are delimiters, not tokens.

diffGraphemes(before, after, options?)

Computes a raw grapheme-level diff using Intl.Segmenter with granularity: 'grapheme'. Every item in each token array is one extended grapheme cluster. Combining sequences, emoji ZWJ sequences, flags, skin-tone sequences, and other clusters are never split into partial edits.

import { DELETE, INSERT, diffGraphemes } from '@aforemendude/diff/grapheme';

const thumbsUpSign = '\u{1F44D}';
const lightSkinTone = '\u{1F3FB}';
const mediumSkinTone = '\u{1F3FD}';
const before = thumbsUpSign + lightSkinTone;
const after = thumbsUpSign + mediumSkinTone;

diffGraphemes(before, after);
// [[DELETE, [before]], [INSERT, [after]]]

The library does not Unicode-normalize either input. Canonically equivalent but byte-distinct strings can therefore remain different. Removing insertion entries and joining the remaining tokens reconstructs the first input; removing deletion entries and joining reconstructs the second.

The optional locale is passed to Intl.Segmenter:

import { diffGraphemes } from '@aforemendude/diff/grapheme';

diffGraphemes(before, after, { locale: 'th' });

Its type is Intl.LocalesArgument; when omitted, the runtime's default locale selection is used.

Algorithm selection

Both diff functions accept algorithm: 'adaptive' | 'myers' | 'sparse'. The default is adaptive. All three settings retain the shared common-prefix, common-suffix, empty-side, containment, and one-token shortcuts; the setting selects the exact engine used for a remaining nontrivial token range.

  • adaptive estimates the relative work and peak workspace of both engines. It selects sparse-match LCS only when the estimated work advantage is substantial and its estimated workspace remains within a conservative multiple of the Myers frontier; otherwise it uses Myers. Once adaptive mode prefers Myers, that choice is retained for the rest of the call so child ranges do not repeatedly build match indexes.
  • myers always uses Myers bisection after the shared shortcuts.
  • sparse always uses exact Hunt-Szymanski sparse-match LCS after the shared shortcuts. This can be much faster for disjoint inputs, reversed unique tokens, and other high-distance ranges with few matching position pairs, but it can require substantial time and memory for repetitive inputs.

All modes produce a normalized shortest insertion/deletion script. When more than one shortest script exists, the public API does not guarantee which matching tokens or tuple placements an algorithm will select. Placements may differ between algorithms or implementation versions, and cleanup results derived from different valid raw diffs can likewise differ.

Trivial-case optimizations

Both diff functions offer opt-in shortcuts for workloads that frequently compare identical strings or inputs where one side is empty. Set optimizeTrivialCases to true to detect those cases before tokenizing both strings. Identical text is tokenized once and returned as one equality (or an empty diff for two empty strings). When exactly one input is empty, only the nonempty text is tokenized and returned as one insertion or deletion. diffLines also tokenizes only the shorter input when the strings differ solely by one insignificant selected terminal delimiter:

diffLines(text, text, { optimizeTrivialCases: true });
diffLines('a', 'a\n', { optimizeTrivialCases: true });
diffGraphemes('', text, { locale: 'en', optimizeTrivialCases: true });

The option defaults to false, so the library does not add up-front whole-string checks to workloads where trivial inputs may be uncommon. The terminal-delimiter shortcut requires a nonempty shorter string that does not already end in the selected delimiter, and the longer string must be exactly that shorter string plus the delimiter. A nonempty line-ending string still represents one blank-line token, so diffLines('', '\n', { optimizeTrivialCases: true }) returns an insertion of ['']; likewise, 'a\n' versus 'a\n\n' remains a real blank-line edit. After enforcing the combined input-size limit described below, diffGraphemes constructs the requested Intl.Segmenter before taking any shortcut, so invalid locales continue to throw for admitted inputs.

cleanupSemantic(diffs, options?)

Applies Diff Match Patch-style semantic cleanup to a grapheme-token diff and returns a new, normalized tuple array without mutating the input. Each supplied token must be one complete grapheme cluster. Cleanup moves and combines whole tokens only, so it never splits a token.

Word boundaries are detected with Intl.Segmenter using the optional locale:

import { cleanupSemantic } from '@aforemendude/diff/cleanup';

const cleaned = cleanupSemantic(changes, { locale: 'ja' });

Word boundaries guide the cleanup rather than constrain it. The algorithm may keep a smaller, partial-word edit when moving the edit to a whole-word boundary would be less useful.

Semantic cleanup is not guaranteed to be idempotent. Applying cleanupSemantic again to its output may produce further changes, so callers should not rely on repeated calls returning the same diff.

To compute and clean up a grapheme-level diff, compose the two operations explicitly:

import { cleanupSemantic } from '@aforemendude/diff/cleanup';
import { diffGraphemes } from '@aforemendude/diff/grapheme';

const options = { locale: ['zh-Hant', 'zh'] };
const changes = cleanupSemantic(diffGraphemes(before, after, options), options);

cleanupEfficiency(diffs, options?)

Applies Diff Match Patch-style efficiency cleanup to a grapheme-token diff and returns a new, normalized tuple array without mutating the input. Each supplied token must be one complete grapheme cluster. Short equalities are folded into surrounding edits when retaining them would cost more than expanding those edits.

The optional editCost is the cost of starting a new edit, measured in tokens. It defaults to 4; larger values produce more aggressive cleanup. It must be a finite, non-negative number, otherwise cleanupEfficiency throws a RangeError. Equalities exactly at a cost threshold are retained.

import { cleanupEfficiency } from '@aforemendude/diff/cleanup';
import { diffGraphemes } from '@aforemendude/diff/grapheme';

const changes = cleanupEfficiency(diffGraphemes(before, after), { editCost: 5 });

Runtime argument handling

The package assumes callers supply values that satisfy its exported TypeScript types. Apart from the supported algorithm, combined string length, and finite, non-negative editCost checks described here, it does not validate argument types, diff operation values, tuple or token-array shapes, supported line-ending values, or option shapes at runtime. Passing out-of-contract values from JavaScript, any, or type assertions is unsupported and may produce incorrect results or errors.

An unsupported algorithm value throws a RangeError after the combined input-size check but before tokenization, trivial-case shortcuts, or Intl.Segmenter construction.

Underlying platform APIs can still reject values themselves. For example, Intl.Segmenter may throw for an invalid locale.

Input size and complexity

The combined length of before and after may not exceed 4,294,967,294 UTF-16 code units. diffLines and diffGraphemes throw a RangeError above that limit before tokenization, trivial-case shortcuts, or Intl.Segmenter construction. This worst-case bound keeps every token coordinate representable in the compact Myers frontier even when each token is one UTF-16 code unit.

Within that bound, the implementation works directly with token sequences. It does not encode line identifiers into UTF-16 characters, impose a fixed line-count ceiling, or stop at an internal timeout. In particular, there is no 40,000-line ceiling. Available memory and processing time are the practical limits; adversarial inputs can still be expensive.

Myers mode has output-sensitive O((N + M)D) time for N and M input tokens and edit distance D, with quadratic worst cases. Its bisection frontier uses O(D) space, bounded by O(N + M). Let S = min(N, M). Sparse mode has O(N + M + r log L) time and O(S + r + L) workspace, where r is the number of strict-equality matching position pairs and L is the LCS length. It is linear for disjoint ranges but can approach quadratic storage on repetitive inputs.

Sparse mode builds its occurrence index over the shorter remaining token range. One compact link is retained per indexed position, while bucket heads and counts are retained only per distinct indexed token. Adaptive mode builds the same index and counts r. A relative memory estimate includes that index, the LIS frontier, and all predecessor records; it is compared with the peak compact-frontier allocation implied by Myers' geometric growth. The deliberately optimistic Myers estimate can omit its final search layer, so uncertain ranges favor Myers. Ranges admitted by the memory gate receive a length-only LIS probe. Sparse is selected only when its full estimated workspace is at most four times the Myers estimate and its estimated work is at least eight times lower. The estimates use saturating arithmetic and are selection policy rather than a loss of exactness: both candidate engines still compute a shortest script without a deadline or heuristic edit limit.

Tokenization is linear in input size, and cleanup adds passes over the produced diff. Tokens are line contents without the selected line ending for diffLines and grapheme clusters for the grapheme APIs.

Benchmark results

The benchmark suite has one focused entry point for each public workflow. npm run benchmark runs four representative 1,000-call schedules. npm run benchmark:adversarial runs four opt-in calibrated worst-case public-workflow schedules, one two-call unbalanced sparse-index schedule, eleven short diffLines schedules around adaptive-selection boundaries, and direct cleanupSemantic and cleanupEfficiency stress cases at several scales. Timed callbacks invoke only the public entry points. Every benchmark and preflight passes a complete options object containing adaptive selection, LF line endings, disabled trivial-case shortcuts, the pinned en locale, and an edit cost of 4. These are the documented defaults except for the locale pin, which avoids host-default differences between benchmark environments. Forced algorithms and other non-default options remain correctness-test inputs rather than benchmark scores. Fixture generation and correctness preflight are outside the timed regions, and neither command enforces a machine-specific performance threshold.

The following calibration results were measured on 2026-08-22 with Node.js 24.19.0, npm 11.17.0, Vitest 4.1.10, Linux 7.0.0 on x86-64, and a four-core Intel N95.

Runtime

Times are arithmetic means per measured schedule from the regular runtime commands. RME is Vitest's reported relative margin of error.

Representative workloads

| Workflow | Schedule | Calls | Mean (ms) | RME | Samples | | ------------------------------------- | ------------------------------------------ | ----: | --------: | -------- | ------: | | diffLines | Representative size/edit mix | 1,000 | 1,015.40 | +/-1.22% | 3 | | diffGraphemes | Representative prose and mixed-Unicode mix | 1,000 | 2,057.85 | +/-1.18% | 3 | | diffGraphemes + cleanupSemantic | Scaled representative grapheme mix | 1,000 | 2,004.82 | +/-1.23% | 3 | | diffGraphemes + cleanupEfficiency | Scaled representative grapheme mix | 1,000 | 2,040.89 | +/-8.24% | 3 |

Adversarial workloads

Each row below is a distinct result reported by npm run benchmark:adversarial. Multi-call diffLines schedules run each listed size or orientation once per measured sample; all other schedules run one workload per sample.

Diff workflows

| Workflow | Schedule | Calls | Mean (ms) | RME | Samples | | --------------- | -------------------------------------------------------------------------------------------- | ----: | --------: | --------- | ------: | | diffLines | 9,500 disjoint unique lines per side | 1 | 2.8138 | +/-14.97% | 3 | | diffLines | 128 repeated lines and 500,000 disjoint unique lines, in both orientations | 2 | 74.9019 | +/-31.84% | 3 | | diffLines | Reversed unique lines at 256, 512, 1,024, and 2,048 lines per side | 4 | 1.2739 | +/-1.16% | 3 | | diffLines | Myers side of the relative work crossover at 100 lines per side | 1 | 0.2161 | +/-4.40% | 3 | | diffLines | Sparse side of the relative work crossover at 101 lines per side | 1 | 0.0452 | +/-45.50% | 3 | | diffLines | Disjoint unique lines at 256, 512, 1,024, and 2,048 lines per side | 4 | 0.8296 | +/-3.57% | 3 | | diffLines | 1% shared position pairs over unique alphabets at 256, 512, 1,024, and 2,048 lines per side | 4 | 1.1146 | +/-9.14% | 3 | | diffLines | 5% shared position pairs over unique alphabets at 256, 512, 1,024, and 2,048 lines per side | 4 | 1.3120 | +/-11.82% | 3 | | diffLines | 10% shared position pairs over unique alphabets at 256, 512, 1,024, and 2,048 lines per side | 4 | 1.3273 | +/-21.92% | 3 | | diffLines | Duplicate-heavy low-distance fallback at 256, 512, 1,024, and 2,048 lines per side | 4 | 0.6563 | +/-1.38% | 3 | | diffLines | Mostly equal unique low-distance inputs at 256, 512, 1,024, and 2,048 lines per side | 4 | 1.2352 | +/-0.88% | 3 | | diffLines | Lower-match side of the relative memory crossover at 512, 1,024, and 2,048 lines per side | 3 | 1.5390 | +/-0.83% | 3 | | diffLines | Higher-match side of the relative memory crossover at 512, 1,024, and 2,048 lines per side | 3 | 47.4203 | +/-0.44% | 3 | | diffGraphemes | 11,000 disjoint graphemes per side | 1 | 3.8488 | +/-2.07% | 3 |

Semantic cleanup workflows

| Workflow | Schedule | Calls | Mean (ms) | RME | Samples | | ----------------------------------- | ---------------------------------------- | ----: | --------: | --------- | ------: | | diffGraphemes + cleanupSemantic | 4,250,000 equivalent semantic placements | 1 | 1,886.09 | +/-7.85% | 3 | | cleanupSemantic | 100 consecutive shiftable edits | 1 | 0.2373 | +/-8.23% | 3 | | cleanupSemantic | 400 consecutive shiftable edits | 1 | 0.8529 | +/-1.80% | 3 | | cleanupSemantic | 1,600 consecutive shiftable edits | 1 | 3.6013 | +/-4.95% | 3 | | cleanupSemantic | 6,400 consecutive shiftable edits | 1 | 16.7458 | +/-5.13% | 3 | | cleanupSemantic | 250 chained trivial equalities | 1 | 0.6296 | +/-9.24% | 3 | | cleanupSemantic | 500 chained trivial equalities | 1 | 1.1926 | +/-2.97% | 3 | | cleanupSemantic | 1,000 chained trivial equalities | 1 | 2.4092 | +/-0.99% | 3 | | cleanupSemantic | 2,000 chained trivial equalities | 1 | 5.2282 | +/-0.45% | 3 | | cleanupSemantic | 4,000 chained trivial equalities | 1 | 11.7667 | +/-23.30% | 3 |

Efficiency cleanup workflows

| Workflow | Schedule | Calls | Mean (ms) | RME | Samples | | ------------------------------------- | ------------------------------------------- | ----: | --------: | --------- | ------: | | diffGraphemes + cleanupEfficiency | 8,200 interleaved single-token replacements | 1 | 1,923.60 | +/-0.94% | 3 | | cleanupEfficiency | One 100,000-token equality | 1 | 0.4015 | +/-6.41% | 3 | | cleanupEfficiency | One replacement among 100,000 tokens | 1 | 0.4305 | +/-12.42% | 3 | | cleanupEfficiency | 100 consecutive shiftable edits | 1 | 0.4859 | +/-15.19% | 3 | | cleanupEfficiency | 400 consecutive shiftable edits | 1 | 1.0184 | +/-24.37% | 3 | | cleanupEfficiency | 1,600 consecutive shiftable edits | 1 | 3.6654 | +/-4.33% | 3 | | cleanupEfficiency | 6,400 consecutive shiftable edits | 1 | 26.9863 | +/-35.36% | 3 | | cleanupEfficiency | 250 chained trivial equalities | 1 | 0.6495 | +/-19.65% | 3 | | cleanupEfficiency | 500 chained trivial equalities | 1 | 1.2077 | +/-2.09% | 3 | | cleanupEfficiency | 1,000 chained trivial equalities | 1 | 2.4976 | +/-1.80% | 3 | | cleanupEfficiency | 2,000 chained trivial equalities | 1 | 5.7722 | +/-0.95% | 3 | | cleanupEfficiency | 4,000 chained trivial equalities | 1 | 12.5890 | +/-2.20% | 3 | | cleanupEfficiency | 160 alternating equality candidates | 1 | 0.1930 | +/-19.37% | 3 | | cleanupEfficiency | 320 alternating equality candidates | 1 | 0.3630 | +/-13.62% | 3 | | cleanupEfficiency | 640 alternating equality candidates | 1 | 0.7322 | +/-0.65% | 3 | | cleanupEfficiency | 1,280 alternating equality candidates | 1 | 1.4961 | +/-11.42% | 3 |

Memory

npm run benchmark:memory and npm run benchmark:adversarial:memory run the corresponding benchmark files one at a time in fresh Node.js processes and report the baseline RSS, peak RSS, and peak increase for each workflow. The memory runner uses Node.js's operating-system-backed maximum resident set size instead of a V8 heap snapshot, so the peak also captures typed-array backing stores, strings, Intl.Segmenter, and other native or external allocations. The reported increase still includes Vitest, fixture loading, inputs, outputs, and allocator behavior; use it to compare identical workloads on the same Node.js and operating-system versions rather than as an exact count of retained algorithm objects. Use the regular benchmark commands above for timing comparisons.

Each row reports the process high-water mark after the warmup and three measured iterations in its fresh benchmark process. An adversarial row covers every runtime schedule for that workflow because its benchmark file runs in one process.

Representative workloads

| Workflow | Schedule | Baseline RSS (MiB) | Peak RSS (MiB) | Peak increase (MiB) | | ------------------------------------- | ------------------------------------------ | -----------------: | -------------: | ------------------: | | diffLines | Representative size/edit mix | 99.27 | 373.55 | 274.28 | | diffGraphemes | Representative prose and mixed-Unicode mix | 99.10 | 269.14 | 170.04 | | diffGraphemes + cleanupSemantic | Scaled representative grapheme mix | 99.16 | 270.39 | 171.23 | | diffGraphemes + cleanupEfficiency | Scaled representative grapheme mix | 99.07 | 274.59 | 175.51 |

Adversarial workloads

| Workflow | Schedules | Baseline RSS (MiB) | Peak RSS (MiB) | Peak increase (MiB) | | ------------------- | -------------------------------------------- | -----------------: | -------------: | ------------------: | | diffLines | All adversarial line-diff schedules | 98.99 | 423.72 | 324.73 | | diffGraphemes | 11,000 disjoint graphemes per side | 99.49 | 214.97 | 115.48 | | cleanupSemantic | All adversarial semantic-cleanup schedules | 98.74 | 1,143.62 | 1,044.88 | | cleanupEfficiency | All adversarial efficiency-cleanup schedules | 99.00 | 383.39 | 284.38 |

The four representative schedules and the two composed cleanup stress fixtures target roughly two seconds per measured schedule on the reference machine. The disjoint fixtures retain their historical sizes and are now intentionally much shorter under sparse-match selection. All measurements remain machine-specific observations, not performance guarantees. See Expected input distribution and benchmark mapping for the heuristic distribution, fixture construction, correctness checks, and interpretation guidance.

Licensing

Original project code is distributed under the MIT license. Portions of the diff and cleanup implementation are derived from diff-match-patch-es and Google Diff Match Patch and remain under the Apache License, Version 2.0. The package therefore declares MIT AND Apache-2.0. See the third-party notices and the included Apache-2.0 license text.

Development

Development requires Node.js 22.12 or newer. The published library supports Node.js 20 or newer. npm run test runs the unit and integration suites. After a build, npm run test:package packs and installs the tarball in a temporary consumer to verify runtime and declaration resolution. Benchmarks use deterministic generated workloads and run separately; they report measurements without enforcing machine-specific performance thresholds. The distribution assumptions, workload groups, fixture mapping, and interpretation policy are documented in Expected input distribution and benchmark mapping.

npm run format:check
npm run build
npm run test
npm run test:package
npm run benchmark
npm run benchmark:adversarial
npm run benchmark:memory
npm run benchmark:adversarial:memory
npm run verify