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

@polebot/eval

v0.1.0-nightly.2

Published

Record each layer of a Pole request as JSON, and replay one layer against the recording

Readme

@polebot/eval

@polebot/eval turns Pole requests into JSON fixtures and tests one layer of your bot against them. record folds the events of each request into one Recording. replay runs your own gate, normalize, input, or preview function on a recording and reports each field where the output differs from what was recorded.

The package depends on @polebot/protocol only. It never imports a trigger or a runtime. Your application passes the layer functions to replay.

The API reference is in the TSDoc of each export, so your editor shows it on hover.

Install

pnpm add @polebot/eval @polebot/protocol

Privacy

A recording holds message content: the snapshot of the arrival, the stored event and the request context, and any input you add to it. Store recordings where you would store the messages themselves, and delete them on the same schedule.

Recording is off unless you wire it. record does nothing until you pass its onEvent and drain to serve.

Record requests

import { appendFile } from 'node:fs/promises';
import { record } from '@polebot/eval';
import { serve } from '@polebot/protocol';

const recorder = record({
	sink: recording => appendFile('recordings.jsonl', `${JSON.stringify(recording)}\n`),
});

const served = await serve({
	runtime,
	triggers: [discord.trigger],
	onEvent: recorder.onEvent,
	drain: recorder.drain,
});

// On shutdown:
await served.stop();
await recorder.flush();

record keys each request by traceId and hands it to sink once, when the request ends. A request that ends with a wide event carries it as wide. A drop below the drain's level floor arrives without one. flush hands over the requests still open and waits for every sink call. It rejects with the error of a sink call that failed.

What a recording holds

| Field | Layer | Filled by | | --- | --- | --- | | traceId, requestId, conversationKey, trigger | | record | | snapshot | source | record, from the source of intake.received | | decision | gate | record: accepted, resumed, or the drop reason | | event | context | record, from request.accepted | | context | context | record, from request.accepted | | input | input | you | | preview | act | you, such as from the runtime's preview | | wide | record | record, from the drain |

request.accepted carries the stored event and the request context when the trigger reports them, as the Discord and scheduler triggers do. A resumed request has neither. No event carries the input or the preview, because the input can hold attachment bytes. Add them to a recording before you save it as a fixture, or rebuild the input with replay from event and context.

Replay one layer

import { readFile } from 'node:fs/promises';
import { replay, type Recording } from '@polebot/eval';

const recording: Recording = JSON.parse(await readFile('fixtures/deploy-question.json', 'utf8'));

const { actual, diffs } = await replay(recording, {
	from: 'gate',
	layers: { gate: async snapshot => myGate(snapshot) },
});

The layers run in this order: gate, normalize, input, preview. replay starts at from and runs each later layer you pass. Each layer reads the output of the layer before it, or the recorded value when that layer did not run. A gate that does not pass ends the replay.

| Layer | Reads | Writes | | --- | --- | --- | | gate | snapshot | decision | | normalize | snapshot | event | | input | event, context | input | | preview | requestId, conversationKey, input, context | preview |

A passing gate agrees with every decision that the trigger makes after its gate: accepted, resumed, duplicate, and stopped. A gate that throws decides predicate-failed, as the trigger does.

diffs has one entry per field whose JSON differs from the recording, with expected and actual. Key order does not count. A field the recording lacks diffs against null. replay rejects when a layer needs a field that the recording lacks, such as gate without a snapshot.

Known limits

  • snapshot is the source of intake.received: what the trigger knew at arrival, not the full outside event.
  • record drops a request that ends before it has a decision, such as one lost during intake.