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

tracelapse

v0.9.0

Published

A timelapse of every request — full request stack trace (traceId + before/after of each table change) for Node.js.

Readme

🛰️ Tracelapse

A timelapse of every request.

Full request stack trace for Node.js — one traceId per request (à la AWS X-Ray / x-amzn-RequestId) that ties what came in (HTTP) to everything that changed in your database (before/after of each row) to what went out (response). See exactly what a request did — and time-travel any row.

npm license types esm + cjs

POST /transfers                         x-trace-id: 01J9…ABC   201 · 142ms
 ├─ http.in    POST /transfers          user: u-42 · ip: 1.2.3.4
 ├─ db.update  accounts#42              balance 100 → 70     (-30)
 ├─ db.insert  ledger_entries#991       { amount: 30, type: "debit" }
 ├─ db.update  accounts#88              balance 0 → 30       (+30)
 ├─ event      transfer.completed       { from: 42, to: 88, amount: 30 }
 └─ http.out   201

Give support a traceId and they reconstruct the whole story: the request, every row it touched (with the past and future of each one), the domain events, and the response — in order.


Why

  • One id correlates everything. A traceId is generated per request (ULID, returned in x-trace-id) and propagated via AsyncLocalStorage — your service code and the ORM subscriber all see it, with zero plumbing.
  • It goes into the database, not just the HTTP edge. Every INSERT/UPDATE/DELETE is captured with the row's before (past) and after (future) state, plus a diff.
  • Time-travel any row. "Show me every state accounts#42 has been in, and which request changed it."
  • Safe by default. Sensitive fields are redacted at write time; logging is best-effort and never breaks a request.
  • Plug-and-play, framework-agnostic core. One package, opt-in adapters, optional peer deps.

Install

One package; import only the subpath you need (your stack's deps are optional peers):

npm i tracelapse        # you already have @nestjs/*, typeorm, pg, rxjs, reflect-metadata
import { TracelapseModule }                 from 'tracelapse/nestjs';  // transport: traceId + interceptor
import { TypeOrmSink, TypeOrmTraceStore }   from 'tracelapse/typeorm'; // capture + persistence (Postgres)
import { MongoTraceSink }                   from 'tracelapse/mongo';   // (optional) store logs in Mongo
import { buildTimeline, traceContext }      from 'tracelapse';         // agnostic core

Quickstart — NestJS + TypeORM + Postgres

// 1) register the subscriber on your DataSource
import { TraceSubscriber, TraceEntity, TraceChangeEntity } from 'tracelapse/typeorm';
export const AppDataSource = new DataSource({
  /* ...your config */
  entities: [/* ...your entities */, TraceEntity, TraceChangeEntity],
  subscribers: [TraceSubscriber],
});

// 2) wire the module (sync forRoot, or forRootAsync with @nestjs/typeorm)
@Module({
  imports: [
    TracelapseModule.forRoot({
      sink:  new TypeOrmSink(AppDataSource),       // writes trace / trace_change
      store: new TypeOrmTraceStore(AppDataSource), // enables reads — expose them with your guards (see below)
      userIdFrom: 'user.sub',                      // associate the trace to a user
      persist: { level: 'full' },                  // full | standard | minimal
      buffered: true,                              // async writes — off the response path (default)
    }),
  ],
})
export class AppModule {}

Create the tables (pick one):

# reads DATABASE_URL from the environment (inline, exported, or an env file)
DATABASE_URL=postgres://… npx tracelapse-migrate          # inline
npx tracelapse-migrate                                    # auto-loads ./.env
npx tracelapse-migrate --env-file .env.prod               # pick the file (.env.local, .env.prod, any name)
# or add CreateTraceTables1780000000000 to your migrations
# or apply createTraceTablesSql() yourself

That's it. Now any write inside a request is captured:

curl -i -X POST localhost:3000/transfers -d '{"from":42,"to":88,"amount":30}'
# → response header: x-trace-id: 01J9…ABC

Read the traces (secure by default)

store enables reconstruction + time-travel, but Tracelapse does not mount any read endpoint by default — it's forensic data and the lib can't know your auth. Expose it from your own guarded controller, injecting the exported TRACE_QUERY_SERVICE:

import { TRACE_QUERY_SERVICE } from 'tracelapse/nestjs';
import type { TraceQueryService } from 'tracelapse';

@UseGuards(JwtAuthGuard, AdminGuard)         // your guards, your route — fail-closed
@Controller('traces')
export class TraceReadController {
  constructor(@Inject(TRACE_QUERY_SERVICE) private readonly traces: TraceQueryService) {}
  @Get(':id')              get(@Param('id') id: string) { return this.traces.getTrace(id); }
  @Get('row/:table/:id')   row(@Param('table') t: string, @Param('id') id: string) { return this.traces.getRowHistory(t, id); }
}
curl -H "authorization: Bearer …" localhost:3000/traces/01J9…ABC        # request + timeline (before/after + events)
curl -H "authorization: Bearer …" localhost:3000/traces/row/accounts/42 # time-travel: every state of that row

Prefer this over mounting the lib's built-in controller and guarding it with a global APP_GUARD keyed on a path prefix (req.path.startsWith('/traces')): if the prefix ever drifts (global prefix, versioning, route change) the guard silently no-ops and the API fails open. Owning the route keeps it fail-closed.

Escape hatch: if the route is already protected upstream (gateway/dev), mountReadApi: true mounts the lib's controller (GET /traces/:id, time-travel) for you — unauthenticated, so only behind your own protection.

Read your traces with AI (MCP)

Point your AI client (Claude Desktop/Code, …) at the built-in MCP server and just ask: "reconstruct trace 01J9…ABC and tell me what changed in account 42."

# local (stdio)
DATABASE_URL=postgres://readonly:***@host/db npx tracelapse-mcp

# remote (HTTP, authenticated — Bearer token required)
DATABASE_URL=… TRACELAPSE_MCP_PORT=7070 TRACELAPSE_MCP_TOKEN=$(openssl rand -hex 32) npx tracelapse-mcp

Tools: get_trace, get_row_history, search_traces (by user / fingerprint / method / status code / period). Read-only and redacted. See docs/09-mcp.md.

What gets captured

| Per request (trace) | Per row change (trace_change) | Domain events | |---|---|---| | traceId, method, endpoint, actionName | seq, tableName, operation | name | | userId, fingerprint | primaryKey | data | | ip, visitorId, userAgent | before (past) · after (future) · diff | capturedAt | | requestData, responseData (redacted) | source (orm | db) | | | statusCode, errorMessage, executionTimeMs | capturedAt | |

Indexed for fast queries: traceId, userId, fingerprint, method, statusCode, createdAt.

Configure

| Option | What it does | |---|---| | headerName | correlation header (default x-trace-id) | | traceMethods / excludeMethods | trace only some HTTP methods, or skip some | | userIdFrom | where to read the user id (dot-path or (req) => id) | | fingerprint | derive a request fingerprint ((req) => string) | | redactOptions / redact | extra sensitive fields, or a custom redactor | | persist | how much to save: full · standard · minimal (+ per-field overrides) | | buffered | async writes (default true) — never adds latency to the response | | sinks: [...] | fan-out to several sinks (e.g. DB + your own OpenTelemetry exporter) | | mountReadApi | mount the lib's read controller (default true); set false to expose TRACE_QUERY_SERVICE from your own guarded controller |

Storage adapters

The capture is database-agnostic — TraceSink/TraceStore are the seam:

  • tracelapse/typeorm — Postgres (trace / trace_change tables), triggers for raw-SQL capture, retention helpers.
  • tracelapse/prisma — Prisma Client Extension for capture (prisma.$extends(createTracelapseExtension())) + sink/store over the same tables. For full before/after on updates, pair with the Postgres triggers (ORM-agnostic).
  • tracelapse/mongo — one document per trace; app can run on Postgres and ship logs to Mongo.
  • Your own — implement TraceSink (and optionally TraceStore) for Kafka, files, anything. The snapshot is plain, serializable data.

Documentation

| | | |---|---| | Vision & problem | Architecture | | Data model | Trace lifecycle | | Extensibility | Storage & rotation | | Read via MCP | Runnable example |

Built test-first (DDD-ish + strict TDD). npm test · npm run build.

License

Apache-2.0 © QTV Group