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

@inkly/otel

v0.0.0

Published

OpenTelemetry tracing + metrics for inkly, built on the core instrumentation seam. Zero hard runtime deps.

Readme

@inkly/otel

OpenTelemetry tracing + metrics for inkly, built on the core instrumentation seam. One line wires every action and stream dispatch to a span and a set of metrics.

import { inkly } from "@inkly/core";
import { trace, metrics } from "@opentelemetry/api";
import { createOtelInstrumentation } from "@inkly/otel";

const app = inkly(contract, {
  instrumentation: createOtelInstrumentation({
    tracer: trace.getTracer("inkly"),
    meter: metrics.getMeter("inkly"),
  }),
});

Why this design

  • No hard dependency on @opentelemetry/api. You pass in a Tracer and/or a Meter that you obtained from your own OTel setup; this package talks to them through small structural interfaces (OtelTracer, OtelMeter). @opentelemetry/api is an optional peer dependency. This mirrors the way @inkly/redis accepts any structurally-compatible client, and keeps inkly's core promise of a lean, dependency-light runtime. (The example passes genuine @opentelemetry/api objects and is typechecked in CI, which proves the interfaces line up.)
  • Passive by construction. It is built on inkly's instrumentation hook, which cannot change a dispatch — it only observes. Every callback inkly invokes is wrapped in try/catch inside the core, so even a buggy exporter can never break a connection or drop a message.
  • Tracer and meter are independent. Provide only a tracer for traces, only a meter for metrics, or both. Anything you omit is simply not emitted.

Install

pnpm add @inkly/otel @opentelemetry/api

You also need an OTel SDK (e.g. @opentelemetry/sdk-node) configured elsewhere in your app to export the spans and metrics. This package only produces them.

What it emits

Spans (when a tracer is given)

One span per action/stream dispatch:

| Field | Value | | --- | --- | | name | inkly.<kind> <name> (e.g. inkly.action sendMessage, inkly.stream ticks) | | kind | SERVER | | inkly.kind | "action" or "stream" | | inkly.name | the contract entry name | | inkly.peer.id | the peer id (spans only; disable with includePeerId: false) | | status | OK, or ERROR + recordException(error) when the handler throws |

A stream span stays open for the lifetime of the stream and ends when the stream completes, errors, or is cancelled (unsubscribe / disconnect).

Metrics (when a meter is given)

| Metric | Instrument | Unit | Attributes | | --- | --- | --- | --- | | inkly.server.dispatch.duration | Histogram | s | inkly.kind, inkly.name, inkly.ok | | inkly.server.dispatches | Counter | — | inkly.kind, inkly.name, inkly.ok | | inkly.server.errors | Counter | — | inkly.kind, inkly.name, inkly.phase | | inkly.server.active_connections | UpDownCounter | — | — | | inkly.server.connection.duration | Histogram | s | — |

Durations are recorded in seconds, following the OpenTelemetry semantic conventions for duration histograms.

Cardinality: the peer id is attached only to spans, never to metric attributes. Metric attributes are kept to low-cardinality fields (kind, name, ok, phase) so they don't explode your time-series database.

Options

createOtelInstrumentation({
  tracer,             // OtelTracer — omit to disable spans
  meter,              // OtelMeter  — omit to disable metrics
  prefix: "inkly",    // namespace for span names, metric names, attribute keys
  includePeerId: true // put peer.id on spans
});

Resume + the active-connections gauge

inkly supports session resume: if a socket drops, the client can reconnect and resume the same session without re-authorizing. Because a resumed connection re-attaches without a fresh open (and therefore without a fresh onConnect), this package tracks the set of ids it has actually incremented and only decrements those. The result: inkly.server.active_connections never goes negative, and a brief network blip on a resumable session is not counted as a brand-new connection. The trade-off is that the window during which a session is detached-but-resumable is not counted as "active". This matches inkly's open/close lifecycle semantics exactly.

Caveats

  • This package produces telemetry; it does not configure or start an OTel SDK. Set up exporting (OTLP, Prometheus, etc.) separately.
  • Spans are not automatically parented to an incoming HTTP trace context — a WebSocket connection is long-lived and a dispatch is not an HTTP request. Each dispatch span is a root SERVER span. If you need linkage to an upstream trace, propagate it yourself via your authorize context.

License

LicenseRef-Dazza-1.0. See LICENSE.