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

redline-engine

v0.3.0

Published

Structural HTML redlines with validated before/after reconstruction, precise inline highlights and bounded failure.

Readme

redline-engine

Structural HTML redlines for Node.js and bundled browser applications. Compare two body fragments, highlight text and structural edits, and reconstruct either original parsed body from the merged result. Written in TypeScript, with no runtime DOM globals or network calls.

ESM-only, with TypeScript declarations. Requires Node.js 22+ or a browser bundler.

Why use it over node-htmldiff?

redline-engine is a structural alternative to node-htmldiff 0.9.4. It aligns HTML subtrees and refines local text changes, making large documents with scattered edits practical while validating that both original parsed bodies can be reconstructed.

On synthetic documents with 6,000 paragraphs per side, about 1 MB each, 0.3.0 records:

| Sparse-edit workload | 0.3.0 | 0.2.0 | 0.1.0 | node-htmldiff 0.9.4 | | ------------------------------------- | -----: | -----: | -----: | ------------------: | | Original pair | 393 ms | 438 ms | 433 ms | Timed out at 15 s | | Both bodies enclosed in a section | 408 ms | 447 ms | 465 ms | Timed out at 15 s | | Extra paragraph inserted at the start | 398 ms | 521 ms | 447 ms | Timed out at 15 s |

Measured on an Apple M1 Max with Node.js 26.7.0. Structural values are medians of three fresh-process calls including parsing, matching, rendering and both-side validation. The raw legacy renderer was run once per workload and terminated at 15 seconds, including process startup; its eventual completion time is unknown. 0.3.0 removes the 0.2.0 start-insertion slowdown and is faster than both earlier releases on all three workloads. Timing targets are advisory; all three workloads succeed with zero coarse replacements. See the 0.3.0 samples, 0.2.0 samples and original samples.

The gain is workload-dependent: simple insertion-only and repetitive cases were faster in node-htmldiff in the baseline benchmarks. This is a replacement API, not a drop-in adapter.

Beyond performance, the engine represents attribute-only changes and preserves whitespace, comments, empty elements and namespaces. Successful results pass reconstruction checks; unsupported inputs and exceeded budgets produce explicit outcomes. The compatibility suite covers adapted upstream behavior, intentional corrections, quality fixtures and independent projection checks. Version 0.3.0 passes all 532 compatibility checks, 143 unit tests, 87 unchanged upstream tests and 25 browser/playground tests.

Install and compare

Install version 0.3.0:

npm install [email protected]
import { compareBodies, project, renderMerged } from "redline-engine";

const result = compareBodies({
  beforeHtml: "<p>Hello <b>old</b> world.</p>",
  afterHtml: "<p>Hello <b>new</b> world.</p>",
});

if (result.outcome === "success") {
  const { html, diagnostics } = renderMerged(result.comparison);
  const beforeBody = project(result.comparison, "before");
  const afterBody = project(result.comparison, "after");
  console.log(html, beforeBody, afterBody, diagnostics);
} else if (result.outcome === "limit") {
  console.error("Comparison exceeded", result.limit);
} else {
  console.error("Cannot represent this comparison", result.diagnostics);
}

Success is returned only after both projections of reparsed merged HTML match the canonical input DOM trees, including namespaces, attributes, comments, whitespace and empty nodes. Source bytes, attribute order and equivalent entity spellings may normalize. Moves are represented as deletion/insertion. Unsupported and limit results contain no partial HTML.

Public API

| Export | Purpose | | ------------------------------------------ | ---------------------------------------------------------------------- | | compareBodies(input) | Synchronous comparison returning success, unsupported or limit. | | renderMerged(comparison) | Returns { html, diagnostics } from the validated comparison. | | project(comparison, "before" \| "after") | Reconstructs a canonical body string using owned markers. | | DEFAULT_LIMITS | Default input, node, depth, work, output and cooperative time budgets. | | MODEL_VERSION | Operation model version, currently 2. |

Types: CompareBodiesInput, ComparisonResult, Comparison, Operation, Diagnostic, Limits, Side and Timings. Import through redline-engine; internal module paths are not public API. Do not mutate comparison objects. During 0.x, incompatible API/marker changes will require a minor version; fixes preserving the contract use patch versions.

Migration from model 1: accept MODEL_VERSION === 2, handle Operation.kind === "attributes", and include [data-diff-attrs] and [data-diff-lead] in marker handling. A model-1 host may continue to consume 0.1.x results, but must reject a model-2 comparison before rendering it.

Options are optional: className (default redline), dataPrefix (default diff), atomicTags (additional exact lowercase atomic element names), and partial limits. Invalid option values return unsupported. See CONTRACT.md for grammars, precision boundaries, operation semantics and supported HTML content models.

Worker and display boundary

This library is not a sanitizer. Sanitize both bodies before comparison and isolate the rendered result. The host owns document heads, resource loading, security policy and styling. Run user-supplied documents in a worker and terminate it after 15 seconds or cancellation: the synchronous parser cannot be preempted by the cooperative engine timeout.

Browser host and worker examples show a bundler-based worker with a deadline and cancellation. Copy both files into your application. The worker sends HTML, diagnostics and timings rather than the operation journal; retain the comparison in the worker if you need its public project function. Handle worker failures and cancellation in your application.

Default generated markers use data-diff-node="insert|delete", data-diff-op and optional data-diff-wrapper. Insertions and deletions in lists, tables and block-level flow containers may also carry data-diff-lead="", which gives the immediately preceding whitespace-only text node to the marked element or generated wrapper. Several markers can share one data-diff-op (a replacement's two sides, or whitespace next to a deleted first block); group by it when counting changes. Paired attribute changes use data-diff-attrs, a JSON array containing the complete before-side [name,value] list in source order. Cross-inline formatting shells use data-diff-unwrap="before|after". All data-diff-* input attributes are reserved and cause rejection; a custom prefix owns its corresponding namespace. Original ins/del elements are not generated markers merely because of their tag or class. Custom viewers must implement all marker semantics in the contract and run the shipped redline-engine/conformance/model-2.json vectors against their projector.

[data-diff-node="insert"] {
  background: #dcfce7;
}
[data-diff-node="delete"] {
  background: #fee2e2;
}
[data-diff-unwrap] {
  outline: 1px dashed #956600;
}
[data-diff-attrs] {
  outline: 1px dashed #6b4f9a;
}

Model 2 viewers must recognize node, wrapper, unwrap, lead and attribute markers. Projection processes descendants before it restores data-diff-attrs on the before side or unwraps a shell. On the side where a lead-owned marker is absent, its immediately preceding whitespace-only text node is removed with it. In lists and tables, a re-indented child is replaced on its own; changed trailing container whitespace still replaces the container. Code blocks (pre) are compared by line, with word-level highlights inside similar lines. Attribute changes use an attributes operation and the attribute-change diagnostic; the before projection restores the full JSON attribute list and the after projection keeps the after-side attributes.

Bounds and measured limitations

Defaults: 2,000,000 combined UTF-16 input units; 200,000 nodes per side; depth 256; 50,000,000 work units; 8,000,000 output units; 15,000 ms cooperative time. These budgets interact: passing the input or node cap does not guarantee that work/output budgets fit. They do not impose a process memory ceiling or bound host layout.

The 0.2.0's synthetic 70,000-replacement case expands to 7.8 million output units and records 2.463 seconds warm Chromium render-ready p95 (20 warm calls). The original release recorded 2.403 seconds; both exceed the original 2-second target. This version is about 2.5% above the later 2.403-second ceiling, now advisory. Extreme expansion is memory intensive. See the current distributions, current memory snapshots, and measurement method and historical evidence.

Malformed namespace-changing repairs can return unsupported. Bounded local refinement may produce diagnosed coarse replacements that preserve both sides. Document styling, hidden content and duplicate IDs remain application concerns.

Develop and verify

pnpm install --frozen-lockfile
pnpm exec playwright install chromium
pnpm run typecheck
pnpm run test
pnpm run test:upstream
pnpm run test:compatibility
pnpm run test:browser
pnpm run test:package
pnpm run test:integration   # needs the Redline plugin at ../redline (or PLUGIN_ROOT)
pnpm run playground
pnpm pack --pack-destination artifacts

test:package installs the tarball in a temporary consumer, checks TypeScript resolution and runs Node and browser-worker examples. pnpm run bench runs the isolated comparison benchmarks. CONTRACT.md specifies the API and preservation guarantees.

MIT. LICENSE. THIRD-PARTY-NOTICES.md describes runtime dependencies and the separately retained test/benchmark provenance. The old engine and its fixtures are excluded from the npm package.