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

@bielok/jpath

v0.1.0

Published

A fast, streaming JSON traversal library.

Readme

npm downloads Made in Buenos Aires

A fast, streaming JSON traversal library.

This library is new, experimental, and can still be sped up, at least, by a factor of two.

jpath is a streaming JSON path parser that operates on raw byte streams without creating intermediate objects, arrays, or strings. By using chained method calls to define paths and bitwise/ring-buffered structures for state tracking, it minimizes memory footprint and GC pressure, making it ideal for processing large JSON files.

  • No runtime dependenies.
  • Zero-allocation parsing (no object/array instantiation).
  • Streaming input processing (supports chunked data).
  • 90%+ test coverage.
  • Chained API for defining paths (.key(), .index(), .any()).
  • Compatible with both pure JavaScript and TypeScript projects.
  • CommonJS, ESM and UMD distributables.
  • Includes a fuzzer and concurrency tests.
  • Successfully deployed on edge workers using < 128 MiB RAM.

Getting started

Installation

$ npm install @bielok/jpath

ESM

import { JPath } from "@bielok/jpath";

CommonJS

const { JPath } = require("@bielok/jpath");

UMD (browser)

<script src="https://unpkg.com/@bielok/jpath/dist/jpath.umd.js"></script>
<script>
    const { JPath } = window.jpath;
</script>

Example

import { JPath } from "@bielok/jpath";

const p = new JPath();

// Path: $.store.books[0].authors[0]
p.key("store").key("books").index(0).key("authors").index(0);

const json = '{"store":{"books":[{"authors":["Author Name"]}]}}';
const result = p.processChunk(new TextEncoder().encode(json));

// result -> '"Author Name"'

API Reference

new JPath(capture?, arena?, nesting?, segments?)

Initializes a new parser instance. All arguments are optional:

| Option | Type | Default | Description | |---|---|---|---| | capture | uint | 65536 | Maximum bytes for a single captured value. | | arena | uint | 16384 | Maximum bytes for path key storage. | | nesting | uint | 256 | Maximum supported nesting depth. | | segments | uint | 64 | Maximum number of path segments. |

// Defaults are fine for most cases:
const p = new JPath();

// Tune for deep structures or long paths:
const p = new JPath(65536, 16384, 512, 128);

reset()

Resets the parser state for reuse, preserving all pre-allocated buffers. Call this between searches to reconfigure the path without allocating.

p.reset();
p.key("another").key("path");

key(string)

Appends an object key segment to the target path. Keys are compared as raw UTF-8 bytes against the incoming JSON stream. Supports escaped characters (\", \\) inside the key but does not handle \uXXXX unicode escape sequences.

p.key("users").key("name");

index(uint)

Appends an array index segment to the target path. Accepts only unsigned integers. Negative indices are not supported.

p.index(0).key("email");

any()

Appends a wildcard segment. Matches any object key or any array index (first match wins).

p.key("users").any().key("name");

processChunk(Uint8Array)

Processes a chunk of raw JSON bytes. Returns:

  • A string: the captured value as raw JSON text (e.g. '"hello"', '42', '{"key":"val"}') when the path is found.
  • null: when the path is definitively not found in the stream.
  • undefined: when more data is needed to reach a conclusion.
const result = p.processChunk(bytes) ?? p.end();

end()

Signals the end of the input stream. Returns the final captured value if the path matches, null otherwise. Always call this after the last chunk.

const result: string | null = p.processChunk(bytes) ?? p.end();

nextSibling()

Advances the parser to the next sibling match at the same nesting level. After a hit, call this to continue scanning subsequent array elements or object values without descending deeper. This is the building block for streaming iteration — you control when to advance, so you can inspect, skip, or transform each match as it arrives.

const p = new JPath();
p.key("arr").any();

const bytes = new TextEncoder().encode(`{"arr":[10,20,30]}`);
const results: string[] = [];

for (let i = 0; i < bytes.length; i++)
{
    const r = p.processChunk(bytes.subarray(i, i + 1));
    if (r === null) break;
    if (r !== undefined)
    {
        results.push(r);
        p.nextSibling();
    }
}
// results: ["10", "20", "30"]

nextChild()

Advances the parser to the next match at any depth below the current position. Unlike nextSibling(), this enables recursive descent — the parser matches the target path no matter how deeply nested. This is the primitive for deep-scan queries like $..price in JSONPath, letting you find every occurrence of a key or pattern anywhere in the document.

const p = new JPath();
p.key("price");

const bytes = new TextEncoder().encode(
  `{"store":{"books":[{"price":10},{"price":20}],"name":"shop"},"price":5}`
);
const results: string[] = [];

for (let i = 0; i < bytes.length; i++)
{
    const r = p.processChunk(bytes.subarray(i, i + 1));
    if (r === null) break;
    if (r !== undefined)
    {
        results.push(r);
        p.nextChild();
    }
}
// results: ["5", "10", "20"]

Both methods are idempotent; calling them when there are no more elements is safe. The parser returns null when the path is definitively exhausted. If both are called, the last one wins.

Value Decoding

The parser returns raw JSON text (e.g. '"hello"' or '42'). Use these static methods to decode captured values into native JS types.

JPath.string(raw)

Decodes a captured JSON string, stripping surrounding quotes and processing escape sequences. Returns null if the raw value is not a valid JSON string.

JPath.string('"hello\\"world"') // -> 'hello"world'
JPath.string(null)             // -> null

JPath.number(raw)

Parses a captured JSON number. Returns null if the raw value is not a valid number.

JPath.number('42')       // -> 42
JPath.number('-3.14')    // -> -3.14
JPath.number('"hello"')  // -> null

JPath.bool(raw)

Parses a captured JSON boolean or null literal. Returns null if the raw value is not true, false, or null.

JPath.bool('true')  // -> true
JPath.bool('false') // -> false
JPath.bool('null')  // -> null

Diagnostics

memoryReport()

Returns the byte size of every pre-allocated buffer owned by this parser instance.

parser.memoryReport()
// -> { total: 102400, buffers: { arena: 16384, cross_buf: 65536, path_spans: 768, is_object_bits: 32, array_indices: 1024 } }

Comments

The parser silently skips JSON comments during traversal. This is useful when parsing JSONC (JSON with Comments) or other non-standard JSON dialects.

  • Line comments (//); everything from // to the end of the line is ignored.
  • Block comments (/* */); content between /* and */ is ignored. Block comments can span multiple chunks.
{
  // This is a line comment
  "name": "value",
  /* This is a
     block comment */
  "items": [1, 2, 3]
}

Comments are transparent; they do not affect path matching or capture behavior. The parser treats them as whitespace.

Unsupported JSONPath Features

This parser implements a subset of JSONPath focused on streaming extraction. The following standard JSONPath features (RFC 9535) are not supported:

  • Filter expressions ([?(@.price < 10)]); No sub-expression evaluation during traversal. The parser is structural only, not evaluative.
  • Negative indexing ([-1]); .index() only accepts unsigned integers.
  • Union / multi-select ([0,1] or ['name','price']); Each path targets a single key or index.

These limitations exist because the parser operates as a single-pass streaming state machine with zero-allocation constraints. Features requiring sub-expression evaluation or backtracking are architecturally incompatible with this design.

Building & Running tests

$ bun run build # Builds all distributables.
$ bun test      # Run unit and concurrency tests.
$ bun run fuzz  # Run the random-op fuzzer.

Benchmarks

Use the benchmark scripts to run performance tests against baseline implementations.

$ bun run scripts/benchmark.ts
jpath benchmark

cpu:  Apple M1 (8 cores)
time: 2026-07-18T17:29:07.401Z

Time from raw bytes to result (competitors pay JSON.parse)
┌───┬─────────────────────┬─────────┬───────────┬─────────────┬─────────────────┐
│   │ path                │ jpath   │ jsonpath  │ jsonpath-js │ jsonpath-faster │
├───┼─────────────────────┼─────────┼───────────┼─────────────┼─────────────────┤
│ 0 │ meta.version        │ 0.03 ms │ 219.00 ms │ 235.12 ms   │ 222.34 ms       │
│ 1 │ meta.stat.val       │ 0.04 ms │ 233.24 ms │ 311.15 ms   │ 214.32 ms       │
│ 2 │ user[0].email       │ 0.03 ms │ 262.16 ms │ 235.09 ms   │ 223.98 ms       │
│ 3 │ user[0].addr.lat    │ 0.02 ms │ 261.15 ms │ 217.52 ms   │ 216.69 ms       │
│ 4 │ user[500].email     │ 1.66 ms │ 221.91 ms │ 215.85 ms   │ 216.51 ms       │
│ 5 │ user[1916].prem     │ 6.31 ms │ 223.28 ms │ 215.52 ms   │ 214.84 ms       │
│ 6 │ user[0].addr        │ 0.01 ms │ 235.13 ms │ 297.57 ms   │ 234.25 ms       │
│ 7 │ first user.email(*) │ 0.01 ms │ 227.67 ms │ 242.92 ms   │ 226.11 ms       │
└───┴─────────────────────┴─────────┴───────────┴─────────────┴─────────────────┘

License

The author disclaims copyright to this source code.

This software is released into the public domain and is provided 'as-is', without warranty of any kind, express or implied.