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

@nodii/telemetry

v0.23.0

Published

Substrate observability library for the Nodii microservice stack — OTel wrapper, log envelope, intent lifecycle, audit signal, agent registry, context propagation. Spec: planning hub docKey=telemetry.

Readme

@nodii/telemetry

Substrate observability library for the Nodii microservice stack. v0.1.0 ships the SDK surface — bootstrap, log envelope, tracer, meter, audit signal, intent lifecycle, agent registry, context propagation — without the OTel-SDK wiring (deferred to a v0.1.x follow-up; see .agent-todo.md TS-12).

Spec: https://planning.dev.nucleus-cloud.in/api/v1/feature-docs?serviceId=nodii-libs&docKey=telemetry

Install

bun add @nodii/telemetry

Bootstrap (D152)

import { initTelemetry } from "@nodii/telemetry";

initTelemetry({
  serviceName: "nodii-billing-service",
  env: process.env.APPLICATION_STATE!, // 'dev' | 'uat' | 'prod'
  otlpEndpoint: process.env.OTLP_ENDPOINT,
  vocabularyRegistry: {
    billing_payments_total: {
      family: "async_worker",
      description: "Payments processed",
      decision: "D23",
    },
  },
  intentTypes: {
    "billing.invoice.create": {
      typeName: "billing.invoice.create",
      server_mintable: true,
      client_mintable: false,
      timeout_seconds: 300,
    },
  },
});

Subsequent initTelemetry() calls warn-log + no-op. Calling any public API before init throws TelemetryNotInitialized.

Logger (D13)

import { logger } from "@nodii/telemetry";

logger.info("payment.charged", { invoice_id, amount_minor_units });
logger.error("saga.compensation.failed", { saga_id, step, reason });

The 11-field envelope (level, ts, msg, service, env, tenant_id, user_id, request_id, trace_id, span_id, intent_id) is auto-attached from the active NodiiContext + the init config + the active OTel span. Do not pass envelope fields yourself — they get filled in.

D15 redaction runs on every line (structured-field keys against the D2 PII list + regex pass on the message string) and is fail-closed: if redaction throws, the line is DROPPED and telemetry.sdk.redaction_failure_total{reason} bumps.

Tracer (D26 / D27 / D28)

import { tracer, withSpanAttributes } from "@nodii/telemetry";

await withSpanAttributes({ invoice_id }, async () => {
  await tracer.span("billing.invoice.create", async (span) => {
    span.setAttribute("payment.method", method);
    // ... business logic; mandatory attrs auto-applied
  });
});

Mandatory attributes (tenant_id, request_id, intent_id) are stamped on span start. 4 KB per-value cap with _truncated=true marker. D27 status mapping: recordException auto-pairs with ERROR; clean return defaults to OK.

Auto-instrumentation (D154) — Bun / ESM caveat

When initTelemetry is given an OTLP endpoint, the resolved autoInstrumentation flags register the OpenTelemetry http / grpc / pg instrumentations, so a service emits request/query spans without any manual tracer.span calls.

ESM services must load the instrumentation hook before any instrumented module. OpenTelemetry patches modules via import-in-the-middle; under ESM that only works if the loader hook is installed at process start, before node:http / @grpc/grpc-js / pg are first imported. Launch with:

node --import @opentelemetry/instrumentation/hook.mjs ./dist/server.js
# or, with a TS entry under tsx:
node --import tsx --import @opentelemetry/instrumentation/hook.mjs ./src/server.ts

Without the hook, ESM services will see auto-instrumentation reported as enabled in config but emit no http/grpc/pg spans (the modules are never patched). CommonJS services need no flag (require-based patching applies automatically).

Bun note: Bun's native server bypasses Node's http module, so auto-instr HTTP server spans are not produced under Bun regardless of the hook. Run the service under Node (the command above) to get auto-instr server spans; manual tracer.span + epoch-correct timestamps work under both runtimes.

The httpServer/httpClient and grpcServer/grpcClient flags are coarse on/off pairs — the underlying instrumentation patches both directions, so enabling either half instruments both.

Metrics (D18 / D19 / D20 / D24)

import { metrics } from "@nodii/telemetry";

const paymentsCounter = metrics.counter("billing_payments_total", {
  family: "async_worker",
  description: "Payments processed",
});
paymentsCounter.inc({ tenant_id, pgw, status });

Names must exist in the vocabulary registry passed to initTelemetry({ vocabularyRegistry }) — D24 ADR-gated. Mismatched family or off-vocab name → MetricVocabularyError + bumps telemetry.sdk.cardinality_rejected_total{family, reason}.

D20 bare-label discipline: registration rejects tenant.id and tenantId variants.

D19: exponential histograms, max 160 buckets.

Audit (D17 / D33)

import { audit } from "@nodii/telemetry";

await audit.emit({
  action: "payment.refund.issued",
  target_kind: "payment",
  target_id: payment.id,
  payload: { amount_minor_units, reason },
});

The library ships the adapter (write path); the emission policy (method-level interceptor, saga lifecycle events, agent-layer double-emission) and the audit_log schema live in the audit doctrine + each consuming service.

Intent lifecycle (D46 / D155)

import { intent } from "@nodii/telemetry";

const id = await intent.begin("billing.invoice.create", { invoice_id });
try {
  // work
  await intent.complete(id, { result: "ok" });
} catch (err) {
  await intent.abandon(id, "error");
  throw err;
}

intent.begin HARD-validates the type name against the registry (D155); unknown type → IntentTypeNotRegistered. Activates the intent on the active NodiiContext (sets intent_id). Nested begins stack by id match.

intent.progress is a no-op stub at v0.1.0 per D155 § M02 carry-forward.

@agentInvocable (D156)

import { agentInvocable } from "@nodii/telemetry";

class PaymentService {
  @agentInvocable({
    mode: "composable",
    authTier: "confirmation-required",
    sideEffects: ["payment.refund"],
    idempotency: "keyed-by-request-id",
    description: "Refund a captured payment",
  })
  async refundPayment(req: RefundRequest): Promise<RefundResult> {
    /* ... */
  }
}

Runtime is a no-op at v0.1.0 (D156). The decorator attaches the config to the method + the class prototype under a symbol-keyed property; the build-time codegen (separate tool, deferred) walks the tsc AST + falls back to the runtime registry to emit agent-methods.generated.json.

Context propagation (D40 / D41)

import {
  type NodiiContext,
  withContext,
  getContext,
  requireContext,
} from "@nodii/telemetry";

const ctx: NodiiContext = {
  tenant_id: "...",
  user_id: "...",
  actor_type: "user",
  on_behalf_of: null,
  role: "owner",
  request_id: "...",
  intent_id: null,
};
await withContext(ctx, async () => {
  /* every log / span / metric / audit emit picks up this context */
});

Read with getContext() (returns null when no scope active) or requireContext() (throws). SDK middleware adapters (deferred to TS-9) are the ONLY writers of the parent-scope context per D41 lock; service code uses withContext for child scopes only.

SDK-internal counters

The library increments these on its own behaviors (do not register them in your vocabulary):

  • telemetry.sdk.redaction_failure_total{reason} — D15 fail-closed drop.
  • telemetry.sdk.cardinality_rejected_total{family, reason} — D18.
  • telemetry.sdk.export_failures_total{signal, reason} — D153.
  • telemetry.sdk.trace_propagation_missing_total — § 5.9 D25.

Deferred to v0.1.x

See .agent-todo.md at the repo root:

  • TS-9: Subpath adapters (/grpc, /bullmq, /outbox, /system-process, /lambda).
  • TS-12: Real OTel SDK wiring — replaces the no-op SpanEmitter + the default JSON log sink with the OTLP exporters.
  • TS-13: Smoke service per D157 at ts/telemetry-smoke/.
  • TS-14: Telemetry-tax-budget benchmark (D28 ≤ 200µs SDK-side per typical RPC).

License

MIT