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

@zeer-dev/prep-client

v0.1.0

Published

Experimental native-fetch client for Zeer Prep historical full-block queries

Readme

@zeer-dev/prep-client

Dependency-free JavaScript ESM implementation with TypeScript declarations. Requires Node 22.18+ and native fetch. This is an HTTP client for the Prep full-block archive; it is not a wallet SDK or native Rust binding.

Install the client with:

npm install @zeer-dev/[email protected]
import { PrepClient } from "@zeer-dev/prep-client";
const client = new PrepClient({ url: "https://zeer.endpx.cloud/api/prep" });
for await (const page of client.stream({
  from_block: 0, to_block: 2,
  field_selection: { block: ["height", "hash"], transaction: [] },
})) console.log(page.data.blocks);

See the quickstart and client documentation.

For large historical scans on a compatible server, inspect (await client.getCapabilities()).query.max_adaptive_scan_blocks, then pass scan_mode: "adaptive" and a limit up to 1,000,000 to get or stream. The server returns bounded partial pages after its soft time slice or byte ceiling; stream follows their cursors. This does not mean unlimited results or that an empty first page proves absence over the entire range. Keep the original query and its cursor together. Fixed queries retain the 1,000-block limit. See the scan contract.

For one bounded lookup rather than paginating through a block range:

const capabilities = await client.getCapabilities();
const result = await client.getTransactions({
  from_block: 1,
  to_block: 2049,
  txids: transactionIds, // up to 128 entries
});
const next = await client.getTransactions({
  from_block: 1,
  to_block: 2049,
  txids: anotherIdSet,
  at: result.snapshot, // same canonical prefix; an append beyond it is allowed
});

The configured client is constructed below. getTransactions defaults to eight public metadata fields, including canonical height/index. Optional fields must include txid, block_height, and index. Limits are 10,000 blocks, 4,096 result locations and 4 MiB. Every requested unique ID is accounted for by matching rows or missing_txids; absence is scoped to that completed range. Duplicate input IDs do not duplicate rows, while multiple canonical locations are preserved. The client rejects partial/unrequested/unsorted responses and changed snapshots. These methods use the existing auth, timeout, cancellation and typed errors. They require a server implementing the new routes. Capabilities report supported protocol features; getHeight() reports current readiness and archive coverage.

For compact public transaction metadata, select size_bytes, transparent_input_count, and transparent_output_count alongside txid, block_height, version, and locktime. These are bounded JSON integers and do not require transferring full decoded input/output objects. They require a Prep server with the transaction metadata extension; older servers reject unknown selected fields. Unknown or duplicate selections are rejected by the client before transport. Existing defaults and lossless decoded-number handling remain.

import { PrepClient, ReorgError } from "@zeer-dev/prep-client";

const client = new PrepClient({
  url: "http://127.0.0.1:39368",
  token: process.env.PREP_API_TOKEN,
  timeoutMs: 30_000,
});

const height = await client.getHeight();
console.log(height.indexed_height, height.source_height, height.lag_blocks);

try {
  for await (const page of client.stream({
    from_block: 100,
    to_block: 200, // exclusive; omitting captures the head on the first page
    limit: 25,    // scanned blocks, not matching transactions
    field_selection: {
      block: ["height", "hash"],
      transaction: ["txid", "block_height", "index", "raw", "vout"],
    },
  }, { totalTimeoutMs: 120_000 })) {
    // Persist application effects and the continuation together if necessary.
    // Stream never restarts automatically or combines results from another epoch.
    console.log(page.next_block, page.data.transactions);
  }
} catch (error) {
  if (error instanceof ReorgError) {
    // Reconcile the application's prior effects against the current archive.
    // Blindly resuming or repeating financial side effects is not safe.
    throw error;
  }
  throw error;
}

get(query) fetches one page. Continue with the same original request plus its cursor. stream(query) follows cursor pages to one finite captured end; it does not follow the chain forever. Empty filtered pages still advance. New appended blocks do not extend an in-flight stream. A changed epoch or HTTP 409 REORG throws ReorgError, with no retry or implicit restart.

getHeight() returns nullable indexed/source heights, observed lag, source status, and decimal-string epoch/revision. Readiness describes the configured source and historical archive, not independent consensus or guaranteed live canonicality.

The default timeout is 30 seconds per request and 120 seconds for the whole stream, including time the consumer spends between pages. Pass signal for caller cancellation; abort/timeout exceptions preserve the native signal reason. A finite maxPages guard defaults to 100,000. The client bounds response bodies to 32 MiB, rejects malformed/nonadvancing continuations, and does not retry requests silently.

Every nested number in transaction vin/vout/decoded and whole-block decoded is a string, preserving exact node JSON monetary values before JavaScript parses them. The client rejects rich decoded responses containing numeric tokens. Use exact decimals/integers rather than Number for amounts. Top-level heights/indexes/versions are bounded integers. The raw block/transaction fields contain complete serialized bytes encoded as hex.

Select block decoded to retrieve every stored source field, including nonce, bits, roots, transaction objects, and additional source properties. This whole object is returned even when the separate transaction selection is empty; its nested transactions are not trimmed by transaction filters. Header-only requests omit both block raw and decoded and use the cached header path.

Transparent output address filters support public scriptPubKey address strings only. They cannot reveal shielded recipients or resolve input addresses. See the full API contract for all fields, filter semantics, and source trust limits.

Keep bearer tokens on a trusted backend and use HTTPS when crossing an untrusted network. The package never accepts wallet keys. HTTP redirects are rejected so an authorization header is not silently forwarded to another endpoint.

Run the offline and local HTTP client tests:

node --test packages/prep-client/test/client.test.js

Public shielded structure and node observations

On a compatible server, query sapling_spend_count, sapling_output_count, orchard_action_count, ironwood_action_count, is_coinbase, transaction_type and expiry_height. shielded_pools matches any pool in its Sapling/Orchard/Ironwood list; separate filter properties combine with AND. These counts describe public components, which can include dummy actions. See the packaged-client example.

const caps = await client.getCapabilities();
if (caps.live?.configured) {
  const pool = await client.getMempool();
  if (pool.transactions.length) {
    const observed = await client.getTransaction(pool.transactions[0].txid);
    console.log(observed.status); // pending, mined, or not_observed
  }
}

Live methods require the optional protected testnet node source. Configuration presence is not a readiness guarantee. Mempool membership can change between requests. These are read-only observations, not wallet operations or Chef events; no viewing key is accepted. See the live contract.