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

@wizeworks/lens-node

v0.3.0-alpha.14

Published

WizeLens add-on for Node.js: request timeline, outgoing HTTP, database calls, logs, errors, and an in-app dashboard.

Readme

@wizeworks/lens-node

The Node add-on of WizeLens: one trace per request with every database call, outgoing call, message, log, and error, and a dashboard at /_wizelens.

import express from "express";
import pg from "pg";
import { createWizeLens, instrumentPg } from "@wizeworks/lens-node";

const wizelens = createWizeLens({ serviceName: "orders-api" });
instrumentPg(wizelens, pg);

const app = express();
app.use(wizelens.middleware);      // first
// ...your routes...
app.use(wizelens.errorMiddleware); // last
  • Frameworks: Express (wizelens.middleware), Fastify (wizelensFastify), Koa (wizelensKoa), Hono (wizelensHono), or plain node:http (wizelens.handler).
  • Databases: instrumentPg, instrumentPostgresJs, instrumentMysql2, instrumentBetterSqlite3, instrumentNodeSqlite, prismaExtension, instrumentTypeOrm, instrumentDrizzle, or wizelens.traceDb for anything else.
  • gRPC and messaging: instrumentGrpc, instrumentAmqplib, instrumentKafkaJs, instrumentServiceBus.
  • Outgoing fetch is traced automatically. Tag requests with wizelens.tag(key, value) and name the user with wizelens.setUser(id).

The HUD on your pages

While the dashboard is served, the add-on puts the WizeLens HUD on the HTML pages your app answers, with no code in the page. It serves the @wizeworks/lens-browser script at /_wizelens/wizelens.js (and its map) and adds this tag right before the last </body>:

<script src="/_wizelens/wizelens.js" data-endpoint="/_wizelens" data-hud="true" data-source="inject" defer></script>

Only a GET answered 200 with Content-Type: text/html and no Content-Encoding gets it, outside /_wizelens, and only when the body has a </body> (fragments, such as htmx partials, are left alone). The body streams through, and an answer that gets the tag loses a Content-Length it can no longer keep. It works the same for Express, Fastify, Koa, Hono, NestJS, Next.js, and plain node:http. A page that starts WizeLens itself takes over from the injected script. Turn it off with hud: false or WIZELENS_HUD=false.

So that a compressing layer (compression(), @fastify/compress, koa-compress, hono/compress, Next.js) answers a page in plain HTML that can take the tag, a page navigation (a GET whose Accept has text/html) reaches your app without its Accept-Encoding header while the HUD is on. Scripts, JSON, and other files are still compressed. This only happens while the dashboard is served, which is development by default.

Logs

Log lines written during a request go into its trace. Lines written outside a request (startup, timers) are skipped.

| Logger | How | |---|---| | console.* | Automatic. Turn it off with captureConsole: false. | | Fastify (request.log, app.log) | Automatic with wizelensFastify. | | pino (also NestJS with nestjs-pino) | instrumentPino(wizelens, logger). Covers every logger from the same pino copy. | | winston | instrumentWinston(wizelens, logger). Child loggers are covered. | | bunyan | instrumentBunyan(wizelens, logger). Call it before you make child loggers. | | consola | instrumentConsola(wizelens, consola) | | log4js | Add an appender: appenders: { wizelens: { type: log4jsAppender(wizelens) } }, and list it in your categories. | | NestJS built-in logger | NestFactory.create(AppModule, { logger: nestLogger(wizelens, new ConsoleLogger()) }) | | OpenTelemetry logs | new LoggerProvider({ processors: [wizelensLogProcessor(wizelens)] }) | | Anything else | wizelens.logger.info("message", { key: "value" }) |

Your loggers still write where they wrote before. WizeLens only copies the lines. Fields become log attributes, sensitive names are redacted, and errors keep their type, message, and stack. Fastify's own "incoming request" and "request completed" lines are skipped, because the trace already shows them. When WizeLens reads pino, winston, or bunyan directly, the OpenTelemetry bridge records of the same logger are skipped, so no line shows twice.

Metrics

Every 10 seconds the add-on samples its runtime: CPU (process.cpu.utilization, process.cpu.time), memory (process.memory.usage), the V8 heap, event loop delay and use, and GC pauses (v8js.gc.duration). The names follow OpenTelemetry. They show on the Metrics page, at api/metrics/points?since=<epoch ms>, and in the MCP tools list_metrics and get_metric.

To keep your own OpenTelemetry metrics too, add the WizeLens exporter to your meter provider:

import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { wizelensMetricExporter } from "@wizeworks/lens-node";

const provider = new MeterProvider({ readers: [new PeriodicExportingMetricReader({ exporter: wizelensMetricExporter(wizelens) })] });

Metrics are kept in memory for 1 hour (metricsMaxAgeMs), at most 50,000 points and 1,000 series. With a hub set, they are sent to the hub as well. metricsIntervalMs changes the sample rate; captureMetrics: false turns metrics off.

On by default in development only. See the getting-started guide and the full option list in the README.

MIT licensed.