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

@alxmss/tracekit

v0.1.1

Published

Runtime instrumentation and source pruning for TypeScript modules

Readme

TraceKit

Runtime instrumentation and deterministic source pruning for TypeScript.

TraceKit wraps your modules in a transparent Proxy, records which functions are called during an execution, and then rewrites your source files to remove every function body that was never touched — replacing them with { /* pruned */ }. The result is a minimal, accurate picture of what your code actually does at runtime.

Status: Operational — 34/34 tests passing.
Node.js 22+, TypeScript 5.x, ESM.

Tests TypeScript Node


How it works

your code  →  tk.slice()  →  Proxy  →  tk.track()  →  Registry
                                                            ↓
tracekit distill  ←  trace.json  ←  registry.drain()  ←  traceId
        ↓
  mathService.pruned.ts   (complexMatrixMultiply body gone, add/subtract intact)
  1. Slice — wrap a module with tk.slice(name, module). The return type is T, so IntelliSense and JSDoc are fully preserved.
  2. Track — run your code inside tk.track(fn). Every function call on a wrapped module is tagged with a unique trace ID and stored in the in-process registry.
  3. Drain — export the registry to a JSON file with registry.drain().
  4. Distill — run the CLI against the JSON file. Functions that appear in the trace keep their bodies; everything else is pruned.

Is TraceKit right for your project?

TraceKit is a precision instrument. It works best when you know which execution you want to understand, not just where a function is defined.

The sweet spot

Language: TypeScript / JavaScript (Node.js & modern web)

TraceKit's engine is built on the ECMAScript Proxy API and Node's AsyncLocalStorage, making it native to the JS/TS ecosystem. It wraps any object or function export without an OS-level agent.

  • Node.js backends — Express, Fastify, NestJS
  • Modern frontend build pipelines — React/Vite, Next.js (server-side)

Architecture: modular codebases with clear service boundaries

TraceKit thrives when logic is separated into services, controllers, or slices. You tell it "watch only the auth and payment slices" and it ignores the other 200 files in your project — so the output is 100% focused on the transaction flow you care about.

Good fits: /services, /controllers, /slices, /repositories, or any Redux Toolkit-style layout.

Task type: interaction-heavy debugging with a clear start and end

TraceKit records a single execution boundary — everything between tk.track() start and finish.

  • API request/response cycles — trace every function from route entry to response
  • Failing tests — run a broken test inside tk.track(); the distilled output is exactly the code that ran
  • State transitions — pinpoint why a Redux action or database mutation didn't behave as expected

Project scale: the "context wall"

| Project size | Recommendation | |---|---| | < 5k LOC | TraceKit is overkill — an LLM can read the whole project | | 20k–200k+ LOC | TraceKit becomes a game-changer — a search returns 50 matches, TraceKit shows the 2 that actually ran |

TraceKit vs. standard RAG (vector search)

| | Standard RAG | TraceKit | |---|---|---| | Relevance | Shows code that looks relevant | Shows code that actually ran | | Async logic | Struggles to follow callbacks | Follows perfectly via AsyncLocalStorage | | Token usage | Scalable but noisy | Ultra-minimal | | Setup cost | Requires a vector DB | Zero-config via tracekit init | | Ambiguity | High | Zero |

If you're working in a large TypeScript codebase where "finding where the logic actually lives" is the hardest part of the job, TraceKit turns a needle-in-a-haystack problem into a here is the needle solution.


Installation

In a project that already has TypeScript

npm install --save-dev @alxmss/tracekit

Then initialise the config (this creates tracekit.config.ts and adds a helper script to package.json):

npx tracekit init

From source (this repo)

git clone https://github.com/alxmss/TraceKit.git
cd TraceKit
npm install
npm run build        # compiles src/ → dist/

Quick start

1. Initialise

npx tracekit init

This creates tracekit.config.ts in your project root and adds trace:distill to your package.json scripts.

2. Edit the config

Open tracekit.config.ts and map your slice names to their source files:

// tracekit.config.ts
import { defineConfig } from '@alxmss/tracekit';

export default defineConfig({
  slices: {
    math:   './src/mathService.ts',
    auth:   './src/auth.ts',
    db:     './src/db/index.ts',
  },
  outputDir: '.tracekit',
});

3. Instrument your code

import { tk, registry, traceStorage } from '@alxmss/tracekit';
import { writeFile } from 'node:fs/promises';
import * as mathService from './src/mathService.js';

// Option A — explicit (no config required)
const math = tk.slice('math', mathService);

// Option B — config-aware (reads sliceMap from tracekit.config.ts automatically)
import config from './tracekit.config.js';
tk.configure(config);
const math = tk.autoSlice('math', mathService);

4. Run a trace

let traceId!: string;

await tk.track(async () => {
  traceId = traceStorage.getStore()!.traceId;

  // Only call the functions you actually need right now
  math.add(3, 7);
  math.subtract(10, 4);
  // math.complexMatrixMultiply is never called → will be pruned
});

// Export the trace
const records = registry.drain();
await writeFile('trace.json', JSON.stringify(records), 'utf8');
console.log('Trace ID:', traceId);

5. Distill

# Auto-resolves slice→file from tracekit.config.ts
npx tracekit distill <traceId> --trace trace.json --root .

# Or print as markdown (useful for LLM context)
npx tracekit distill <traceId> --trace trace.json --root . --format md

# Or write to a directory
npx tracekit distill <traceId> --trace trace.json --root . --output .tracekit

Input (src/mathService.ts):

export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

export function complexMatrixMultiply(a: number[][], b: number[][]): number[][] {
  // ... 30 lines of O(n³) loops ...
}

Output (distilled):

export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

export function complexMatrixMultiply(a: number[][], b: number[][]): number[][] { /* pruned */ }

API reference

tk.slice<T>(name, module): T

Wraps a module in a transparent Proxy. The return type is exactly T — no type information is lost. Recording only happens inside a tk.track() context; calls made outside are passed through without being observed.

const auth = tk.slice('auth', authModule);

tk.configure(config)

Sets the global config so that tk.autoSlice knows the slice→file mapping. Call once at startup before any autoSlice calls.

import config from './tracekit.config.js';
tk.configure(config);

tk.autoSlice<T>(name, module): T

Like tk.slice, but validates that name is registered in the config. The CLI reads the same config file, so you never need to pass --map on the command line.

const db = tk.autoSlice('db', dbModule);

tk.track<T>(fn: () => T | Promise<T>): Promise<T>

Runs fn inside a new trace context backed by AsyncLocalStorage. Every function call on a sliced module within fn — including across await boundaries — is tagged with the same traceId. Concurrent tk.track() calls never share a context.

const result = await tk.track(async () => {
  const user = await userRepo.findById(42);
  return user;
});

tk.distill(traceId, options): Promise<Map<string, string>>

Programmatic distillation. Reads the registry for traceId, looks up source files via sliceMap, and returns a Map<filePath, prunedSource>. Does not drain the registry.

const pruned = await tk.distill(traceId, {
  projectRoot: process.cwd(),
  sliceMap: new Map([['auth', './src/auth.ts']]),
});

registry

The global in-process ring buffer (10 000 records by default, configurable via TRACEKIT_MAX_RECORDS).

| Method | Description | |--------|-------------| | registry.drain() | Returns all records and clears the buffer | | registry.snapshot() | Returns all records without clearing | | registry.onFlush | Optional callback fired synchronously on every record() call |

Each record is a tuple [timestamp, traceId, sliceName, fnPath].

defineConfig(config): TraceKitConfig

Identity helper with TypeScript type inference — the same pattern as Vite's defineConfig. Use it in tracekit.config.ts for editor autocomplete.


CLI reference

tracekit init

Scaffolds tracekit.config.ts in the project root and patches package.json.

Options:
  --root <path>   project root (default: current directory)
  --force         overwrite an existing tracekit.config.ts

tracekit distill <traceId>

Prunes source files based on a recorded trace.

Options:
  --trace <file>     JSON file from registry.drain() (use "-" for stdin)
  --root <path>      project root (required)
  --map <name=path>  slice → file mapping, repeatable; overrides tracekit.config.ts
  --output <dir>     write pruned files here instead of printing to stdout
  --format <fmt>     text (default) or md

When --map is omitted, the CLI auto-resolves the mapping from tracekit.config.ts at --root.

When --format md and --output are both set, all files are combined into a single <traceId>.md file — useful for pasting into an LLM prompt.


Edge cases and known behaviour

| Scenario | Behaviour | |----------|-----------| | Calls made outside tk.track() | Executed normally, not recorded | | Circular references | Safe — a WeakMap breaks cycles | | new ClassName() | Recorded as new sliceName.ClassName via construct trap | | Private class fields (#field) | Passed through without interception | | Symbol-keyed properties | Passed through; wrapping Symbol.iterator would break iteration | | Arrow functions with concise bodies | Bodies like () => expr are not pruned (no block to replace) | | Concurrent tk.track() calls | Each gets its own traceId; AsyncLocalStorage keeps them isolated |


Development

npm run build        # tsc → dist/
npm run dev          # tsc --watch
npm test             # vitest run (34 tests)
npm run test:watch   # vitest interactive
npm run typecheck    # tsc --noEmit (no emit, just type-check)

Project structure

src/
  registry.ts       Ring-buffer record store
  tracker.ts        AsyncLocalStorage context (traceId per tk.track call)
  slice.ts          Proxy engine + tk namespace
  distiller.ts      ts-morph AST parser + magic-string body replacer
  config.ts         defineConfig helper and TraceKitConfig interface
  config-loader.ts  CLI-side static parser for tracekit.config.ts
  cli.ts            Commander CLI (init, distill)
  index.ts          Public API surface
  __tests__/
    slice.test.ts
    tracker.test.ts
    distiller.test.ts