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

@x12i/provider-metadata

v1.2.1

Published

Pack, open, and normalize one provider metadata record. The same wire object for every provider.

Readme

@x12i/provider-metadata

Pack and open one metadata record. The keys are whatever the caller stored.

packMetadata writes the same object for every provider. openMetadata reads that object back. A reader passes the log object and gets every stored pair, for any provider and any set of keys.

Repeating concepts are normalized to one canonical key. Vendor prefixes may stay (openrouter.api_key_name), and the shared key is always available (api_key_name). Grow METADATA_NORMALIZATION_MAP as more aliases appear.

The package runs on Node 20 or newer and publishes ESM and CommonJS from the same entry.

Install

npm install @x12i/provider-metadata

Pack and open

import { openMetadata, packMetadata } from "@x12i/provider-metadata";

const wire = packMetadata({
  objectType: "finding",
  stepId: "nx-p1-context-finding",
  agentId: "neo-networks-v2",
  skillId: "skill-9",
  recordId: "rec-1"
});

const record = openMetadata(wire);

record is the full string map. Another caller map, with different keys, packs and opens the same way.

Normalization

Use normalizeMetadata on provider logs, OTLP attribute bags, or nested vendor objects. Known aliases are promoted to canonical keys. The vendor key remains when it was present.

import { METADATA_NORMALIZATION_MAP, normalizeMetadata } from "@x12i/provider-metadata";

const record = normalizeMetadata({
  "trace.metadata.openrouter.api_key_name": "prod-key",
  "trace.metadata.tenant": "acme"
});
// { api_key_name: "prod-key", "openrouter.api_key_name": "prod-key", tenant: "acme" }

record.api_key_name;
METADATA_NORMALIZATION_MAP.api_key_name; // ["openrouter.api_key_name", ...]

packMetadata and openMetadata apply the same map. Nested objects under known provider namespaces (for example openrouter) are flattened onto the wire. Other nested objects stay off the wire.

Current canonical keys include api_key_name, provider_slug, provider_name, entity_id, user_id, finish_reason, input_unit_price, output_unit_price, source, and session_id.

Automatic trim

Writers create one packer at initialization. pack keeps the record inside 4096 base64 characters. It drops the lowest-priority pair, then the next, and reports every key it removed.

priority is highest priority first. Listed keys are kept ahead of keys that are not in the list. An earlier list entry is kept ahead of a later one. Among keys with the same priority, the longest value is dropped first.

import { createMetadataPacker, openMetadata } from "@x12i/provider-metadata";

const packer = createMetadataPacker({
  priority: ["agentId", "orgId", "stepId", "skillId", "recordId", "objectType"]
});

const packed = packer.pack({
  objectType: "finding",
  recordId: "rec-1",
  note: veryLongText
});

if (packed.dropped.length) {
  // packed.dropped lists the keys that did not fit, least important first
}

const record = openMetadata(packed.wire);

@x12i/ai-dispatcher does this on every call. Pass the same list once as metadataPriority. A trimmed call adds a METADATA_RECORD_TRIMMED warning. metadataTrimWarning(packed.dropped) builds that warning from a direct pack result.

packMetadata does not drop keys. A record that does not fit throws METADATA_RECORD_TOO_LARGE.

packMetadata(...layers) accepts Record<string, unknown> | undefined. Later layers win on the same key. String, number, and boolean values are stored. A number becomes its decimal string, and a boolean becomes "true" or "false". Nested objects under known provider namespaces are flattened and normalized. Other objects and arrays are omitted. When a later value is an object or array for a non-provider key, the earlier scalar for that key stays. An empty result is undefined.

Wire object

The stored value is UTF-8 JSON of the string map, with keys sorted, then base64. That base64 string is split into m.0, m.1, and so on.

| Constant | Value | | --- | --- | | PROVIDER_METADATA_CHUNK_SIZE | 256 characters | | PROVIDER_METADATA_MAX_CHUNKS | 16 |

The limit is 4096 base64 characters. createMetadataPacker drops pairs until the record fits. packMetadata throws METADATA_RECORD_TOO_LARGE instead.

openMetadata joins m.0, m.1, … in order, decodes the base64, and returns every string in that JSON object. Pass the wire object, or the JSON string of that object (Cloudflare cf-aig-metadata). A missing record, invalid JSON, or a value that is not an object returns {}.

These provider slots hold that same object:

| Provider | Log field | | --- | --- | | OpenRouter | Body metadata | | OpenAI | Responses body metadata | | Bedrock | requestMetadata | | Cloudflare | Body metadata and the cf-aig-metadata header (JSON string) |

@x12i/ai-dispatcher creates one packer for the caller. The caller sends one map on the request. The dispatcher writes this object onto the provider that runs the call.