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

vectorless-js

v0.5.2

Published

Vectorless, reasoning-based RAG (PageIndex) in JavaScript. Runs in the browser via WASM and anywhere JS runs. Deterministic tree index plus LLM tree-search retrieval.

Readme

vectorless-js

Retrieval without embeddings. Instead of chunking a document and searching for text that looks similar, this builds a table of contents, hands it to an LLM, and lets the model pick which section to read, the way a person uses an index.

It runs wherever JS does: the browser through WASM, plus Bun, Node, and Cloudflare Workers. It is a JavaScript port of PageIndex, verified byte-for-byte against its Python tree builder.

bun add vectorless-js
bun add @llamaindex/liteparse-wasm   # optional, only if you feed it PDFs

Use it

import { buildIndexAuto, ask, complete, type LLMConfig } from "vectorless-js";

const llm: LLMConfig = {
  baseURL: process.env.OPENAI_BASE_URL!,   // https://api.openai.com/v1
  apiKey: process.env.OPENAI_API_KEY,
  model: process.env.OPENAI_MODEL,         // defaults to gpt-4o
};

const doc = await buildIndexAuto(markdown, "lease.pdf", (p) => complete(p, llm));
const { answer, citations } = await ask(doc, "What is the notice period?", llm);

The config mirrors the OpenAI client, so the standard environment variables drop straight in, and anything speaking OpenAI chat completions works. Pass a bare URL instead of a config when you point at a proxy that holds the key.

The PDF reader is a separate import. Keeping it out of the main entry point means readers who feed it Markdown skip the WASM download entirely:

import { initParser, pdfToMarkdown } from "vectorless-js/parse";

await initParser(wasmBytesOrUrl);   // you supply the wasm; the library never guesses
const markdown = await pdfToMarkdown(bytes);

How it works

PDF
 |   liteparse, compiled to WASM. Runs on your machine.
 v
markdown
 |   buildIndex: headings become nodes.
 v   Deterministic, no LLM, free.
tree
 |   The only smart part. The model reads the table of
 v   contents and fetches the one section it picks.
answer + citations

buildIndex turns Markdown headings into a tree without calling an LLM or touching the network, so it costs nothing and gives you the same tree every run. The agent then navigates that tree through three tools that execute locally: get_document, get_document_structure, and get_page_content.

Running the tools locally is what keeps the document at home. On a 28-page filing the model sees about 1.6 KB of section titles and line numbers, picks one, and pulls only those lines. The other 60-odd KB stays where it is, because nothing asked for it. In a browser that extends to the file itself, which you never upload; the only thing crossing the network is text the model chose to read.

Some documents come out flat, and buildIndexAuto repairs them. A PDF that gives every heading the same rank has correct titles and no nesting, so an LLM assigns the depths and nothing but the headings leaves the machine. A contract with no headings at all has nothing to nest, so an LLM infers the outline from the text. Both feed the same deterministic builder.

Beyond one document, ask also takes a corpus keyed by doc id, and routeDocuments reads per-document descriptions to decide which are worth opening. That routing reads descriptions rather than vectors, same as the rest.

Known limits

OCR is off, so born-digital PDFs read fine and scanned pages come back empty. Wiring a JS OCR engine through pdfToMarkdown's config is the fix.

A document with no headings at all goes through reconstructTree, which sends the whole thing to the model. That is fine for a contract and a problem for a large filing: 448 KB of text is a prompt around 133k tokens, which will not fit a 128k context window. Documents that do have headings avoid this, however uniform those headings are, because buildIndexAuto sends only the headings. Check isFlatTree before reaching for reconstruction on a large heading-less document.

The tree holds the document text, so memory tracks document size. A 53 KB contract becomes a 59 KB tree.

Develop

bun install
bun test            # deterministic tree and tool logic
bun run typecheck
bun run build:lib   # library → dist-lib/
bun run dev         # demo app on :5173
bun run docs:dev    # docs site

License

MIT. This is a port of PageIndex by Vectify AI, also MIT. Both notices are in LICENSE.