Skip to content

feat(nest-profiler-event-emitter): profile @nestjs/event-emitter domain events - #229

Draft
fpasquet wants to merge 1 commit into
mainfrom
feat/nest-profiler-event-emitter
Draft

feat(nest-profiler-event-emitter): profile @nestjs/event-emitter domain events#229
fpasquet wants to merge 1 commit into
mainfrom
feat/nest-profiler-event-emitter

Conversation

@fpasquet

Copy link
Copy Markdown
Contributor

Summary

Adds @eleven-labs/nest-profiler-event-emitter, the 14th publishable collector, closing the last blind spot around first-party NestJS integrations: domain events dispatched through @nestjs/event-emitter. Today a controller answers, the request profile closes, and whatever the @OnEvent listeners did is never recorded anywhere.

The collector contributes three surfaces:

  1. An Events panel on the emitting profile — one row per emit / emitAsync call with the event name, listener count (0 · no listener flagged in amber), duration, async flag and redacted payload. Entries feed the performance-rule engine under their own event tag domain, so slow / n-plus-one / chatty thresholds never inherit the query ones.
  2. An event entrypoint — each @OnEvent execution becomes a first-class profile carrying its own logs, queries and sub-requests, in a dedicated Events list view with Status / Event filters and an Event detail tab. Same philosophy as nest-profiler-commander for CLI commands; opt out with profileListeners: false.
  3. An Event Listeners group in the Routes panel, scanning providers and controllers.

The implementation started from a collector prototyped outside the repo. Porting it to the monorepo conventions (CommonJS + repo-build, @repo/* presets, ConfigurableModuleBuilder + buildCollectorModule, Jest at the 90 % gate) surfaced several defects, fixed here:

  • @OnEvent on controllers was ignored — the scan only walked getProviders(), while @nestjs/event-emitter's own loader also scans controllers. Those listeners were missing from both the Routes panel and the profiling wrapper.
  • A misleading "process-local storage" warning was copied from the commander collector. Event listeners run in the same process as the profiler UI, so in-memory storage works fine — the warning fired on the default configuration.
  • Nothing was restored on shutdown — neither the patched emit/emitAsync nor the wrapped handlers, leaking across apps in e2e suites that create and destroy several.
  • priority: 25 collided with the validator collector.
  • A /g RegExp in ignoreEvents matched every other event (RegExp.test advances lastIndex).
  • An emitterToken resolving to a non-emitter crashed bootstrap — found while writing the tests; now guarded structurally.

Two deliberate additions on top: configurable error classification (error?: ProfilerErrorOptions, with the status layer off by default so an exceptions override is actually meaningful — the GraphQL kind does the same) and TaggableCollector support.

Two limitations are documented rather than solved: request-scoped subscribers cannot be profiled (@nestjs/event-emitter resolves a fresh instance per event, so there is no stable handler to wrap), and EventEntry.error is rarely populated (@OnEvent defaults to suppressErrors: true; the handler's own event profile records the failure either way).

Demonstration in example-api

The notifications context gains a third EventPublisher adapter, backed by EventEmitter2. Since it needs no infrastructure, it becomes the default in place of the no-op adapter, and POST /api/v1/products now publishes product.created — so the collector has data out of the box, with no Docker.

NotificationsNoopModule is kept as the minimal reference implementation of the port but is no longer wired; its docblocks say so explicitly.

Verified

Beyond CI: the full e2e suite (11 suites, 71 tests, including 6 new ones), plus a manual run against the example app confirming the Events panel (1 listener, 25 ms, redacted payload), the ?view=event list, the Event Listeners Routes group, a clean boot with PROFILER_ENABLED=false, and the absence of the spurious storage warning under in-memory storage.

Screenshots are declared in scripts/generate-profiler-screenshots.ts (events.png, events-list.png) but not generated in this PR — nothing references them yet, so there are no broken links.

Related issue

Closes #228

Checklist

  • Tests were added or updated to cover the change
  • Documentation was added or updated
  • A changeset was added, or this PR does not affect published packages
  • Breaking changes use a major changeset with a BREAKING: note

Breaking changes

  • This PR includes breaking changes

New package only — nothing existing changes shape. The one consumer-visible shift is confined to example-api, where the default EventPublisher adapter moves from the no-op to the in-process emitter.

…in events

packages:
- add @eleven-labs/nest-profiler-event-emitter, the 14th collector, in the lockstep release group
- add an Events panel: one row per emit/emitAsync, with listeners, duration, async flag and payload
- add an `event` entrypoint so each @onevent execution gets its own profile, logs and queries
- add an Event Listeners group to the Routes panel, scanning providers and controllers
- tag emissions under a dedicated `event` domain, so thresholds never inherit the query ones
- support configurable error classification, with the status layer off so `exceptions` applies
- restore the patched emit/emitAsync and the wrapped handlers on shutdown, avoiding e2e leaks
- ignore an `emitterToken` resolving to a non-emitter, which would otherwise crash bootstrap

examples:
- add an in-process EventPublisher adapter and make it the default over the unwired no-op one
- emit `product.created` from ProductService so the collector has data with zero infrastructure
- cover the panel, the event entrypoint and both profiler views in a new e2e spec

docs:
- add the package README, its packages/api-reference pages and a tutorial, all in the navigation
- list the collector in the docs index, the API reference index and the profiler UI tour

chore:
- register the package in the changeset group, codecov, labels, labeler and issue templates
- document the collector in both consumer skills and declare its screenshots in the generator
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nest-profiler Ready Ready Preview, Comment Jul 26, 2026 9:04am
nest-profiler-example Ready Ready Preview, Comment Jul 26, 2026 9:04am

@github-actions github-actions Bot added scope: packages Change to a publishable package scope: docs Change to the documentation site scope: ci Change to CI/CD workflows or tooling scope: examples Change to example applications labels Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: ci Change to CI/CD workflows or tooling scope: docs Change to the documentation site scope: examples Change to example applications scope: packages Change to a publishable package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: profile @nestjs/event-emitter domain events

1 participant