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

@harlan-zw/nuxt-wide-events

v0.1.4

Published

Minimal Nuxt Wide Events with build-time field enforcement.

Readme

npm version npm downloads License Nuxt

Nuxt Wide Events emits one structured record per request from your Nuxt server routes.

Production writes one flat JSON line. Development prints a richer record with error details.

Status: experimental. APIs may change before the first release.

Why Nuxt Wide Events?

Traditional request logging scatters one request across many disconnected lines. A Wide Event collects that context into one record.

This module drops runtime redaction entirely. You configure every application Field before any code can use it.

The build parses each server file. It rejects unknown Fields, object spreads, computed names, and dynamic objects. So the boundary stays visible to reviewers and to coding agents.

Features

  • 📝 One record per request: method, path, status, duration, request ID, and your Fields on a single flat line.
  • 🚧 Build-time Field enforcement: an unapproved key stops the build, so no secret reaches a log by accident.
  • 🪶 Small production runtime: no stack formatting, deep redaction, regular expressions, or pretty printing.
  • 🎚️ Levels that stick: a record keeps the highest level it receives, even when the handler recovers.
  • ⚙️ Background records: createWideEvent covers Queue Jobs, scheduled work, and anything off the request path.
  • 🚰 Drain hook: send records to D1, Sentry, or your own adapter through one Nitro hook.
  • 📉 Route exclusion and sampling: the config shape matches evlog, so migration is a rename.

Installation

npx nuxi@latest module add @harlan-zw/nuxt-wide-events

[!TIP] Generate an Agent Skill for this package using skilld:

npx skilld add @harlan-zw/nuxt-wide-events
export default defineNuxtConfig({
  modules: ['@harlan-zw/nuxt-wide-events'],

  wideEvents: {
    service: 'shop',
    fields: [
      'cart.itemCount',
      'user.id',
    ],
  },
})

Add Fields

addWideEventFields is available in server code.

export default defineEventHandler((event) => {
  addWideEventFields(event, {
    'cart.itemCount': 2,
    'user.id': 'user_123',
  })

  return { ok: true }
})

Each value must be a string, number, boolean, or null. Nested objects cannot hide unapproved data.

This code stops the build because user.email is not configured:

addWideEventFields(event, {
  'user.email': user.email,
})

Variables and spreads also stop the build:

addWideEventFields(event, fields)
addWideEventFields(event, { ...fields })

Set the level

setWideEventLevel marks a request Wide Event as debug, info, warn, or error.

export default defineEventHandler(async (event) => {
  try {
    return await chargeCard()
  }
  catch {
    setWideEventLevel(event, 'error')
    return { charged: false }
  }
})

A record keeps the highest level it receives. If the request handler recovers from an error, the record stays an error. A drain and a sampling rate both see the real level.

getActiveWideEventRequestId(event) returns the request identity while the Wide Event is collecting. Use it to correlate an application logger, Sentry, or another request-scoped module. It returns undefined before collection starts and after emission.

This code stops the build because user.email is not configured:

addWideEventFields(event, {
  'user.email': user.email,
})

Variables and spreads also stop the build:

addWideEventFields(event, fields)
addWideEventFields(event, { ...fields })

This constraint keeps the Field boundary visible to reviewers and coding agents.

Production output

Default production performs no stack formatting, deep redaction, regular expression matching, or pretty printing.

{ "timestamp": "2026-08-13T04:12:00.000Z", "level": "info", "kind": "request", "service": "shop", "method": "GET", "path": "/api/cart", "status": 200, "durationMs": 1.4, "requestId": "req_123", "cart.itemCount": 2, "user.id": "user_123" }

kind is request for a request record and background for a background record.

Production errors include status only. All error strings remain absent because they can contain unapproved data.

Development records include error messages and stacks. Development uses compact terminal blocks with request metadata in the header and configured Fields in a tree.

Background operations

createWideEvent is available in server code for Queue Jobs, scheduled work, and other background operations.

export default defineTask({
  async run() {
    const wideEvent = createWideEvent({ 'job.id': 'job_123' })
    wideEvent.setLevel('warn')
    return await wideEvent.emit()
  },
})

A background record carries kind: "background". It has no method, path, or status, because a background operation has none.

The Nuxt auto-import selects JSON output in production and object output in development. It uses the configured service, console, sampling, and drain options. With drain: true, emit() returns a Promise and waits for background drain adapters. Without a drain, emit() remains synchronous.

Use @harlan-zw/nuxt-wide-events/standalone when Nuxt auto-imports are unavailable. Inside Nitro this export resolves to the same configured variant as the auto-import, so a deep import never loses service, console, sampling, or drain. Outside Nitro it writes production JSON without module configuration.

Set request: false to disable request collection. Field enforcement, createWideEvent, and setWideEventLevel remain available.

Set enabled: false to stop all output. Every server import still resolves, so application code needs no change.

Migrate from evlog

Map env.service to service. Keep exclude and sampling unchanged. Do not copy console: false: evlog applies it to browser output, while this option controls server output.

For requests, replace log.set({ section: { value } }) with an approved flat Field:

addWideEventFields(event, { 'section.value': value })

For background operations, replace createLogger(fields) with createWideEvent(fields). Replace each .set(fields) call with addWideEventFields(wideEvent, fields). Keep .setLevel(). If drain is enabled, await or return .emit().

For requests, replace log.setLevel(level) with setWideEventLevel(event, level).

Set request: false for background-only sites. Convert spreads, computed keys, arrays, and nested objects into configured primitive Fields. Keep browser logging and custom error transports in the application.

Production filtering

The configuration shape matches evlog for direct migration:

export default defineNuxtConfig({
  wideEvents: {
    exclude: ['/api/_nuxt_icon/**', '/api/_content/**'],
    sampling: {
      rates: { info: 10, warn: 50, debug: 0 },
      keep: [{ duration: 1000 }, { status: 400 }],
    },
  },
})

Rates are percentages. A record is kept when it matches one whole keep condition. Every part of one condition must match, and the conditions are tried in order. So { duration: 1000, status: 500 } keeps a slow server error, while [{ duration: 1000 }, { status: 500 }] keeps either one.

Every level rate applies to every Wide Event. A background record has no status, so a status condition never keeps one.

The module compiles route patterns during the build. A pattern that ends with /** also matches the bare prefix, which is how Nitro matches routes. Default production uses a separate plugin without filtering code.

Drain records

Use the Nitro hook when D1, Sentry, or another adapter owns the record:

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('wide-events:emit', async (record) => {
    await sendRecord(record)
  })
})

Set drain: true to enable this hook. console then defaults to false, because the hook owns the record. Set console: true to keep stdout output as well. Request drains use event.waitUntil(). Background emit() waits for every hook adapter and surfaces adapter failures.

Options

| Option | Default | Purpose | | --- | --- | --- | | enabled | true | Emit Wide Events. false keeps Field enforcement and server imports. | | request | true | Collect one Wide Event for each request. | | fields | [] | Allow application Fields. | | service | none | Add a service name. | | exclude | [] | Exclude routes that match a glob pattern. | | sampling | none | Set rates and keep conditions for production. | | console | true, or false with a drain | Write records to stdout. | | drain | false | Call the wide-events:emit hook for request and background records. |

Benchmarks

Run the production benchmarks on the target deployment runtime:

pnpm test:bench

The suite compares lifecycle cost and serialization against raw JSON, Pino, and evlog. It also includes 0x flamegraphs and real Nitro HTTP fixtures for this module and evlog.

See the core results, Nitro HTTP results, and CPU profile.

node bench/http/run.mjs
npx 0x --tree-debug bench/profile.mjs wide

The Cloudflare fixture builds with the Workers preset, passes a Wrangler deploy dry run, and serves a request through local workerd.

Scope

The first version supports Nuxt server requests, flat primitive Fields, route exclusion, sampling, stdout, and a Nitro hook.

It excludes browser logging, transports, audit logs, and production error presentation.

Sponsors

License

Licensed under the MIT license.