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

@airo-js/log

v0.1.0

Published

Airo log — sink-based structured event logging for the airo-js framework. Replaces scattered console.* calls across @airo-js/* packages with a single dispatcher; apps replace the sink to feed events into devtools panels, Sentry, Datadog, or stay with the

Downloads

605

Readme

@airo-js/log

Sink-based structured event logging for the airo-js framework. Replaces scattered console.* calls across @airo-js/* packages with a single dispatcher; apps replace the sink to feed events into devtools panels, Sentry, Datadog, or stay with the default console behaviour.

Status: v0.1.0. Stable surface. Layer 1 of the framework devtools story (the dispatcher + structured event types). A separate @airo-js/devtools panel that consumes the sink will follow.

What this is

A dependency-free leaf package that the rest of @airo-js/* emits into:

  • logger(channel) — returns a ChannelLogger with debug / info / warn / error methods. Framework packages call this at module scope.
  • AiroEvent — structured event shape (timestamp, channel, level, message, optional phase / widgetId / cartridgeId / data / error).
  • AiroSink{ emit(event): void }. Replaceable.
  • consoleSink (default) — preserves existing [@airo-js/<channel>] <msg> console behaviour verbatim.
  • noopSink — for tests.
  • setSink / getSink / resetSink — sink management.

Why a sink

Three real consumer needs the framework couldn't address before:

  1. Forward framework lifecycle events to existing observability — Sentry breadcrumbs, Datadog logs, OpenTelemetry spans. Without the sink, every consumer would have to monkey-patch console.
  2. In-page devtools panels — a panel subscribes to events and renders a live stream / state inspector. The sink is the panel's data source.
  3. Test noise control — replace the sink with noopSink in unit tests so framework warnings don't flood test output.

Default behaviour: identical to today

// @airo-js/embed today (post-retrofit):
import { logger } from '@airo-js/log';
const log = logger('embed');

log.warn(`'${elementName}' already registered; skipping.`);
// → console.warn('[@airo-js/embed]', "'<name>' already registered; skipping.")

The default consoleSink produces output indistinguishable from the previous console.warn('[@airo-js/embed] ...') lines. Apps that never call setSink see zero behavioural change.

Replacing the sink

import { setSink, type AiroSink } from '@airo-js/log';

const sentrySink: AiroSink = {
  emit(event) {
    Sentry.addBreadcrumb({
      category: `airo:${event.channel}`,
      level: event.level,
      message: event.msg,
      data: { ...event.data, phase: event.phase, widgetId: event.widgetId },
    });
    if (event.level === 'error' && event.err) {
      Sentry.captureException(new Error(event.err.message));
    }
  },
};

setSink(sentrySink);

Or multiplex — log to console and capture to a ring buffer for the devtools panel:

import { consoleSink, setSink, type AiroEvent } from '@airo-js/log';

const ringBuffer: AiroEvent[] = [];
const RING_SIZE = 500;

setSink({
  emit(event) {
    consoleSink.emit(event);
    ringBuffer.push(event);
    if (ringBuffer.length > RING_SIZE) ringBuffer.shift();
  },
});

// ringBuffer is the data source for the devtools panel.

Channel taxonomy

| Channel | Source | |---|---| | core | @airo-js/core (event bus, page manager, pipeline, router) | | runtime | @airo-js/runtime | | embed | @airo-js/embed | | ssr | @airo-js/ssr | | cartridge-kit | @airo-js/cartridge-kit | | mcp | @airo-js/mcp | | app | Apps emit their own structured events on this channel |

The phase field is free-form — typical values: 'shell' | 'gate' | 'fetch' | 'pipeline' | 'mount' for runtime mount lifecycle, page ids for navigation, gate ids for blocked-by-X warnings.

What this is NOT

  • Not a replacement for thrown errors — throws still propagate to the caller via promise rejection / try-catch. The sink is for side-channel observability (warnings, recoverable errors, lifecycle phases that don't throw).
  • Not a metrics or telemetry pipeline — sinks can forward to one, but this package doesn't ship the forwarding logic.
  • Not a logger framework with formatters / appenders / log4j-style hierarchies — it's a single sink with structured events. Complexity belongs in the sink implementation, not the dispatcher.

License

Apache-2.0 — same as the rest of @airo-js/*.