TokenPolice
Docs
Troubleshooting

ESM / instrumentation order

Node ESM apps go quiet when a client is imported before init() — here's where init belongs in each framework.

Node only. Calls succeed, the dashboard is empty, and firewall: "enforce" blocks nothing.

Symptoms

  • Nothing at all arrives, in a Next.js, Vite, tsup-ESM, Bun, or Deno app.
  • One library arrives and another doesn't — typically the one imported highest in your entry file.
  • It worked under ts-node/CommonJS and stopped after moving to native ESM.

All three have the same cause: a provider client that was imported before tp.init() ran is left un-instrumented, silently, by fail-open design.

Two things fix it, and you generally want both: pass your provider modules to init() via instrumentModules, and put init() somewhere that runs first. The full key list and import forms are in Node & ESM; the placement is below.

Where init goes per framework

Next.js (app router) — put init in instrumentation.ts at the project root, guarded to the Node runtime. Next loads it once before serving any route.

Vite / tsup / Bun / Deno — put init in its own module and import it first in your entry file, before anything else:

// entry.ts
import "./instrument";       // must be the first import
import { startServer } from "./server";

LangChain / LangGraph — install the token-police-langchain companion package, and static-import @langchain/core/language_models/chat_models and @langchain/core/callbacks/manager before init(). Keep every other @langchain/* import out of the entry module. See LangChain.

LlamaIndex — pass each @llamaindex/* provider namespace under instrumentModules.llamaIndex. See LlamaIndex.

Vercel AI SDK — first-party providers are found automatically; community providers go in the aiSdkProviders array, and bundled or edge builds use the middleware instead. See Vercel AI SDK.

Next