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

@ornyx/cartographer

v0.1.0

Published

Deterministic repository inventory and structural graph engine.

Readme

Cartographer

Cartographer builds a deterministic, durable, queryable map of a repository — inventory, structure, and the evidence behind every fact — so that a coding agent can look something up instead of guessing.

It is a library first: five separately callable layers plus a lifecycle facade, with a JSON-first CLI and an MCP server over the same API. It does static analysis only; it never executes, imports, or evaluates the code it reads.

This repository starts from the working Cartographer prototype originally developed inside Rector. It is now an independent Apache-2.0 project and a foundation for further development.

Why this one is different

Four properties are unusual, and each one is checked rather than asserted.

1. Deterministic, byte-identical structural output. Counts, snapshot ids, node and edge ids, paged SHA-256 hashes over canonical records, and operation result fingerprints reproduce exactly. Two consecutive full benchmark runs produced a structural.json with the same SHA-256, and re-running one corpus from a different absolute directory produced a byte-identical structural record — so absolute paths do not leak into ids (BENCHMARKS.md § Determinism; npm run bench:public -- --require-stable fails on drift). Timings are not deterministic and are never presented as if they were. Byte-identity across different machines or different Node.js versions is expected but has not been demonstrated, so it is not claimed.

2. Stable, content-addressed IDs. ID policy v3 scopes ids to a logical repository identity rather than a checkout path, and symbol ids are line- and reorder-independent (lexical scope plus declaration signature). Moving a function down a file does not change its id, and two clones of the same repository at different paths agree (tests/cartographer/graphIds.test.ts, docs/architecture.md).

3. A typed error taxonomy, not string matching. Every failure mode is a distinct exported class carrying structured fields — which limit was exceeded and by how much, which SQLite condition occurred and whether it is retryable, which input was rejected. See Errors an agent can branch on.

4. Evidence with explicit confidence, and honest uncertainty. Every emitted edge and derived node carries a canonical evidenceRecord with a qualitative confidence class, a method, the adapter and schema versions that produced it, and the file hashes it depends on (docs/API.md § Evidence and confidence). There are no numeric relevance or confidence scores anywhere. Where a fact cannot be proven, Cartographer says so: an unresolved import stays recorded on the importing file and never becomes an edge to an invented target; truncated results are flagged as truncated; counts that are lower bounds are labelled lower bounds; and verifyClaims returns not-derivable rather than guessing.

Static analysis only. Cartographer reads and parses; it does not run your code, your build, your tests, or your package scripts. The single subprocess in the codebase is icacls on Windows, used to lock down permissions on Cartographer's own state directory (src/cartographer/filePermissions.ts).

Requirements

  • Node.js 22.13 or newer (node:sqlite is required, and it is only available unflagged from v22.13.0 — on 22.5.0–22.12.x it needs --experimental-sqlite)
  • npm 10 or newer
  • ESM-only package consumption: use import syntax. No CommonJS entry is exported; require() via package exports is unsupported. Node.js >=22.13.0 remains required.

Install

The package is not published yet. For local development:

npm install
npm run verify

Quick start

Index once, then read. Snapshot ids are optional on every read — they resolve to what index() just wrote.

import { SEARCH_SCHEMA_VERSION, initializeRepository } from "@ornyx/cartographer";

const repository = await initializeRepository({ repoRoot: process.cwd() });

try {
  await repository.index();

  // Where is this thing?
  const hits = await repository.search({
    schemaVersion: SEARCH_SCHEMA_VERSION,
    query: "compileTaskContext",
  });

  // Give me a bounded, budgeted briefing for a task.
  const context = await repository.context({
    task: "Fix the task-context byte budget",
    objective: "change",
    budget: { maxBytes: 16_384 },
  });

  // What else does this change touch?
  const blast = await repository.impact({
    changedNormalizedPaths: ["src/cartographer/taskContextCompiler.ts"],
  });

  console.log(
    hits.hits.length,
    context.primaryNodes.length,
    blast.classifications.length,
  );
} finally {
  await repository.close();
}

initializeRepository is idempotent and creates the database at .cartographer/cartographer.db by default. openRepository reopens an existing one; indexRepository is the initialize/index/close one-shot for scripts. update() is incremental and returns status: "fresh" as a true no-op when nothing changed. Full lifecycle, options, cancellation, and limits: docs/API.md.

Snapshot resolution

Omitting snapshotId resolves, in order, to the snapshot this handle last wrote, then to the store's latest snapshot header (greatest createdAt, ties broken by smallest id — a fixed total order over persisted rows, with no clock read at call time), and otherwise throws RepositoryNotInitializedError rather than returning an empty result. An explicitly supplied id is never silently replaced by the latest one. diff keeps beforeSnapshotId mandatory, because defaulting both ends of a comparison has no unambiguous meaning. Full rules: docs/API.md § Snapshot resolution.

Checking what a model claims about the repository

verifyClaims takes structured claims — the kind an LLM emits while reasoning about a codebase — and returns a deterministic three-valued judgment per claim, with graph evidence and a templated (never synthesized) explanation.

const result = await repository.verifyClaims({
  schemaVersion: CLAIM_VERIFICATION_SCHEMA_VERSION,
  claims: [
    { kind: "file-exists", path: "src/cartographer/repository.ts" },
    {
      kind: "symbol-exported-from-module",
      modulePath: "src/cartographer/repository.ts",
      symbol: "initializeRepository",
    },
    {
      kind: "symbol-calls",
      callerSymbol: "index",
      callerPath: "src/cartographer/repository.ts",
      calleeSymbol: "buildGraphSnapshot",
    },
  ],
});

for (const judgment of result.judgments) {
  console.log(judgment.verdict, judgment.reason);
}

Read the caveat before you rely on this. The verdicts are supported, contradicted, and not-derivable, and the value of the API is the third one. contradicted is returned only where the graph's absence of a fact is documented proof that the fact is false — file and directory existence always, symbol and export claims only for files classified as source, import claims only when the importer has no unresolved imports. Everywhere else the answer is not-derivable with a machine-readable reason from a closed set. In particular, symbol-calls never returns contradicted: call-edge coverage is partial in syntax mode and still does not model runtime dispatch in semantic mode, so a missing call edge is not proof of a missing call.

So this is not a hallucination detector, and treating not-derivable as a soft "no" would reintroduce exactly the error it exists to prevent. It is a checker that tells you precisely when it can settle a question and when it cannot. The full absence-provability table is the contract: docs/API.md § Claim verification, tested in tests/cartographer/claimVerifier.test.ts.

CLI

cartographer is a JSON-first wrapper over the same public API, for agents that do not run Node in-process.

cartographer index
cartographer search --query "initializeRepository" --match-class symbol --max-results 20
cartographer impact --path src/cartographer/graphBuilder.ts

stdout is exactly one JSON document per invocation and nothing else; stderr carries diagnostics only (--quiet silences it). Exit 0 is success, 1 is a typed Cartographer error, 2 is a usage error — both error codes print the typed error name and its structured fields as JSON. Read commands are byte-reproducible for the same repository state; index, update, and status route their non-deterministic timings block to stderr so stdout stays stable. Full reference: docs/CLI.md.

MCP server

packages/cartographer-mcp exposes seven tools over the Model Context Protocol — sync, status, search, context, impact, diff, and verify_claims. Every read reports a freshness state (fresh, stale, unknown, or not-indexed), so a client is told when the index is behind the working tree instead of silently receiving stale answers.

Errors an agent can branch on

Failures are exported classes with structured fields, so recovery does not depend on parsing messages.

| Error | Fires when | | --- | --- | | RepositoryNotInitializedError | Opening a repository with no database, or reading a snapshot that does not exist or belongs to another repository | | CartographerRepositoryClosedError | Any indexing operation after close() | | LimitExceededError | A configured maxFiles, maxTotalBytes, maxGraphNodes, or maxGraphEdges budget is exceeded; carries the limit, the actual and maximum values, and diagnostics so far | | CartographerAbortedError | A supplied AbortSignal fires; transactions are never interrupted mid-commit | | CartographerPathEscapeError | A resolved path leaves the repository root; carries the offending absolute path. The scanner never reads outside the root | | UnsupportedSchemaVersionError | The database was written by a newer schema version | | CartographerStoreSqliteError | An underlying SQLite failure; carries a classification and a retryable flag. CartographerStoreContentionError and CartographerStoreConstraintError are the specific subclasses | | CartographerStoreCorruptionError | A persisted row cannot be decoded as a valid Cartographer record | | CartographerStoreNotSupportedError | A store does not implement a capability an operation requires — third-party stores fail this gate honestly rather than degrading silently | | GraphStoreLimitExceededError | A traversal request exceeds the exported hard depth/node/edge caps | | TaskContextBudgetInsufficientError | The irreducible evidence floor cannot fit the requested byte budget; carries both budgetBytes and floorBytes instead of returning an empty or over-budget envelope | | CartographerTaskContextInputError / CartographerClaimInputError | Caller input was rejected by validation; carries the offending hint or the failing issues | | CartographerInternalInvariantError | An engine, store, or adapter contract was violated — this one is a bug report, not a usage error |

Performance

Measured, reproducible, and published with the unflattering results included: BENCHMARKS.md.

Cold index — walk, ignore, hash, classify, parse, build the graph, and persist to SQLite — ran at 284.7–408.5 files/s on three pinned public repositories of 120, 228, and 233 files (single-threaded, syntax-only adapters, median of 3 process-isolated runs, on the machine described on that page). Persistence alone is 39–45% of that time, which is the price of producing a durable, reopenable, integrity-checkable artifact rather than an in-memory index.

Two things that page is careful about, and so is this one. Those are small repositories and the throughput does not hold at scale: archived measurements on 4 700–6 100-file repositories imply roughly 117–184 files/s. And incremental update() currently saves only 4–13% over a cold index for a one-file edit, because every update mints a new snapshot and persists Θ(N) rows; that is a known storage-format problem and it is not fixed in 0.1.0.

Using Cartographer from an agent

docs/agents.md is the playbook: which read operation answers which question, how to detect a stale index before trusting a result, how to spend a token budget, which parts of the JSON are stable enough to cache, and how to use verifyClaims without over-reading it.

Primitives

The lifecycle facade is optional. Every layer is separately callable.

import { scanRepository } from "@ornyx/cartographer";

const result = await scanRepository({ repoRoot: process.cwd() });
console.log(result.snapshot, result.files.length, result.errors.length);
  • Inventory — scanRepository, scanChangedFiles, classifyFile, hashFile, ignore-policy helpers. Walks without following symlinks, applies nested .gitignore and the root legacy .rectorignore, and excludes .cartographer/.
  • Graph — buildGraphSnapshot, buildIncrementalGraphSnapshot, analyzeTypeScriptSemantics, the language-adapter registry. Project, package, directory, file, symbol, tool, capability, and eval records; TypeScript/JavaScript and Python syntax extraction, plus an optional compiler-backed semantic layer for typed calls, references, inheritance, and path aliases.
  • Persistence — InMemoryCartographerInventoryStore / SqliteCartographerInventoryStore and the matching graph stores, behind identical interfaces.
  • Comparison — compareGraphSnapshotsFromStore (store-native, paged) and the legacy hydrated compareGraphSnapshots. Move/rename matching is conservative; ambiguous candidates stay unmatched and are reported as ambiguous.
  • Read paths — searchRepository, compileTaskContextFromStore, analyzeImpact, verifyClaims, CartographerQueryService. These take an explicit snapshot id, so callers that own snapshot scoping keep full control.

Documentation

| Document | Contents | | --- | --- | | docs/API.md | The full public contract: lifecycle, requests, responses, schema versions, evidence | | docs/agents.md | Playbook for agent consumers | | docs/CLI.md | Command reference, flags, exit codes, output contract | | docs/architecture.md | The five layers and how they compose | | docs/limitations.md | What is deliberately not covered, and where results are partial | | BENCHMARKS.md | Measured performance, methodology, and what is not measured |

Commands

| Command | Purpose | | --- | --- | | npm run check | Type-check source without emitting files | | npm test | Run the complete test suite | | npm run build | Emit ESM JavaScript and declarations to dist/ | | npm run test:coverage | Generate V8 coverage reports | | npm run cartographer:self-scan | Scan this repository into .cartographer/self-scan/ | | npm run cartographer:self-scan:check | Validate generated self-scan artifacts | | npm run bench:public | Reproduce the published benchmarks | | npm run verify | Run type-check, tests, build, self-scan, and checker | | npm run verify:evidence | Verify the immutable final real-repository evidence bundle |

Compatibility

The initial standalone release deliberately retains .rectorignore and the wire identifiers rector.cartographer.selfScan.v1, rector.cartographer.selfScanAllowlist.v1, and rector.cartographer.latestFiles.v1. They are compatibility contracts, not runtime dependencies on Rector.

License

Apache License 2.0. See LICENSE and NOTICE.