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

@nxgt/telemetry-httpyz

v0.2.1

Published

An @nxgt/httpyz middleware for @nxgt/telemetry: one client span per call, with the current traceparent on the way out

Readme

@nxgt/telemetry-httpyz

One client span per @nxgt/httpyz request, with the current traceparent on the way out — which is the half that makes a trace a trace.

bun add @nxgt/telemetry @nxgt/telemetry-httpyz @nxgt/httpyz
import { createHttpClient } from '@nxgt/httpyz';
import { tracing } from '@nxgt/telemetry-httpyz';

const api = createHttpClient({
  baseUrl: 'https://api.example',
  use: [tracing()],
});

That is all of it. Inside a server span — from @nxgt/telemetry-hono, or from any span() — the outgoing request carries the header that lets the service on the other end continue the same trace, and the client span becomes a child of whatever is open now. Nothing has to be passed down to the call site.

What it records

| attribute | | | --- | --- | | http.request.method | uppercased, whatever the caller wrote | | url.full | the URL, without its userinfo | | server.address, server.port | the port only when there is one | | url.template | the path as the caller wrote it: /employees/{id} | | http.operation | the operationId, when the call has one | | http.response.status_code | after the reply |

The span is named for the operationId when there is one, and "<METHOD> <path>" otherwise — where the path is the template the caller wrote, /employees/{id}, not the one it filled in. There is no cardinality problem to solve here: httpyz hands over the template already.

A client span fails at 400, not at 500

This is where a client span and a server span disagree, and the asymmetry is the point. A 404 answered by a server is that server working — @nxgt/telemetry-hono records it as ok. The same 404 received by a caller is a call that did not do what it was for, and this records it as an error. Both readings are correct, and each belongs to the span on its own side of the wire.

A timeout is neither: cancelled, because a dashboard that counts timeouts as failures is a dashboard nobody trusts.

Options

| | default | | | --- | --- | --- | | traced | everything | whether a call gets a span at all | | spanName | the operationId, else "<METHOD> <path>" | | | url | the URL without its userinfo | return undefined to record none |

tracing({
  traced: (call) => call.path !== '/health',
  url: (request) => new URL(request.url).origin,   // a query string that carries a key
})

Nothing is skipped by default, and a hook that throws costs its own answer and nothing else: traced falls back to tracing, spanName to the default name, url to recording none. A predicate that raises must not turn observability into a failed call.

API

| | | | --- | --- | | tracing(options) | the middleware | | TracingOptions | the options above | | TRACEPARENT | 'traceparent' | | callAttributes(request, method) | what is known before the call is sent | | callFailed(status), CLIENT_ERROR_FROM | the 400 rule | | safeUrl(url) | a URL with its credentials removed, or undefined | | method(call) | the call's method, uppercased | | HTTP_METHOD, URL_FULL, SERVER_ADDRESS, SERVER_PORT, HTTP_STATUS, OPERATION | the attribute names |

Traps

  • With no telemetry anywhere, no header is sent. A detached scope's traceparent() is all zeros, which this library's own parser rejects and which W3C calls invalid; sending it is worse than sending nothing, because a strict receiver refuses the request and a lenient one starts a fresh trace exactly as an absent header would. The call still goes out.
  • An existing traceparent on the request is replaced. The header describes the call being made now. A stale one — copied from an inbound request, say — would attach this call to a span that has already ended.
  • @nxgt/httpyz is an optional peer, and only its types are used. Nothing here imports a value from it, so this package has no runtime dependency on the client at all.
  • The span ends when the Response is returned, not when its body is read. A streamed reply is recorded as the time it took to start.
  • url.full keeps the query string. It is usually what tells one call from another. If yours carries a key, pass a url hook — the userinfo is stripped for you, the query is not.
  • A retry is a span per attempt. retry and auth sit outside the middlewares in httpyz — use: is composed inside both — so a retried call, and a call replayed after a token refresh, each open a fresh span. Three attempts are three spans under one parent, which is what a trace should show; a middleware cannot see that it is the second one, and there is no placement that gets outside retry.
  • A failure that happens after the reply arrives is not on the span. httpyz reads and validates the body outside the middleware chain, so a ValidationError, an UndeclaredStatusError or a ReplyStatusError leaves the client span ok with the status the server sent. The caller saw a failure and the trace says the call was fine. currentSpan() — or the enclosing scope.fail(…) — is where to put it if you need it.

License

MIT