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

@harlan-zw/jev

v0.3.0

Published

Nuxt-free Jev judgements: one client, the ask tag API, digest helpers, and eval replay math.

Downloads

6,538

Readme

Jev

npm version npm downloads License

Ask Jev typed questions about one state and get probabilities back, with no Nuxt required.

Why Jev?

A generative model answers a yes or no question with a paragraph. Your code then parses prose and guesses what it meant.

  • 🧾 Prose instead of answers: You parse free text to get a boolean, a label, or a rank.
  • 💥 Thrown failures: An HTTP error, a timeout, or a malformed answer throws in the middle of your request.
  • 🔑 Unstable journal keys: The same state with keys in a different order hashes to a new digest, so a stored answer never gets reused.
  • 🎚️ Thresholds picked by feel: Nothing tells you whether a probability band agrees with the answers you already have.

Jev is a System One judge. It answers typed questions about one state with probabilities. It never generates text. This package is the Nuxt-free client for it: pure TypeScript with one dependency, ohash, for sync digests. It has no Nuxt, Nitro, or h3 code, and runs on Node 20+ and workers. For the Nuxt module, the decision runner, and the drizzle schema, use @harlan-zw/nuxt-jev.

Features

  • ⚖️ Typed questions: noul, choice, and score return a probability, a picked option, or a rubric level, never prose.
  • 🏷️ ask tags: Put the state and the question in one tagged template, and send a batch in one call.
  • 🧯 Failures as values: Every call resolves Ok or a tagged JevFailure, so nothing throws and nothing rejects.
  • 🔁 Retries in the client: The HTTP client retries with backoff inside one timeout window and validates each answer.
  • 🔑 Order-stable digests: digestKey and canonicalJson give the same key for the same state, whatever the key order.
  • 📊 Eval replay math: summarizeReplay and suggestBand measure agreement before you move a threshold.

Installation

pnpm add @harlan-zw/jev

[!TIP] Generate an Agent Skill for this package using skilld:

npx skilld add @harlan-zw/jev

What you get

| Export | What it gives you | | --- | --- | | createJevHttpClient | The tagged-failure HTTP client over Cloudflare /ai/run: retries, backoff, one timeout window, and answer validation. | | noul, choice, score | Question builders with typed answers. | | ask, ask.choice, ask.score, ask.chance, askIf | Tagged-template sugar over the client. | | digestKey, canonicalJson | Order-stable digests for journal keys. | | agreement, summarizeReplay, suggestBand, replayAnswer | Pure eval replay math. |

Failure style

Failures are values. Nothing throws and nothing rejects.

The client resolves { _tag: 'Ok', result } or { _tag: 'Err', failure }. Every failure is one tagged JevFailure: Http, Invalid, Network, or Timeout.

The ask API keeps the same contract. A batch resolves to { _tag: 'Ok', answers } or { _tag: 'Err', failure }. An awaited tag on its own resolves to its answer or the same tagged failure.

ask

import { ask } from '@harlan-zw/jev'

const result = await ask(
  { title: 'Crash on login', body: 'Anyone can log in as admin with an empty password.' },
  {
    security: 'Does this describe a security vulnerability?',
    kind: ask.choice`What kind of issue is this?`({ bug: 'Something is broken', other: null }),
    severity: ask.score`How severe?`(['Cosmetic', 'Workaround exists', 'Blocks production']),
  },
  { accountId, apiToken },
)
if (result._tag === 'Ok') {
  result.answers.security.chance // 0.9
  result.answers.kind.choice // 'bug'
  result.answers.severity.ratio // score scaled to 0 to 1
}

Each interpolated object goes once into input, and its slot becomes a path. The state travels with the question. If you await a tag on its own, it sends on its own. askIf resolves to a boolean above a threshold. Credentials come from options, or from CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN.

Client

import { createJevHttpClient, noul } from '@harlan-zw/jev'

const client = createJevHttpClient({ apiToken, accountId })
const call = await client.systemOne({
  state: { query: 'nuxtseo' },
  questions: { brand: noul('Is `query` a search for the site brand?') },
})
if (call._tag === 'Ok')
  call.result.answers.brand.noul

Jev 1.13 jagged edges

The model's own docs list its failure modes (jaggedness). This package and its consumers follow these rules:

  • Math, counting, dates stay in code. Never ask the model to tally, compare numbers, or order dates. Compute counts and buckets in code, then pass the value or a named bucket in state. Use Score answers to rank and threshold. Never infer a magnitude between two rubric levels.
  • Literal reading. Write the exact condition in instructions. Put boundary cases in criteria. If a question needs interpretation, split it into two literal questions and combine them in code.
  • Criteria extend the instruction. A true that means "no" gives worse answers. Keep the two aligned.
  • Thresholds do not carry across question types. A Noul band and a Choice confidence answer different questions. noul compares with probabilities['yes'], not with confidence. A question and its negation do not sum to 1. Tune each threshold on its own question. Never expect arithmetic identities between separate answers.
  • Choice settles "which"; Noul settles "whether". A Choice is relative: it picks one option. Each Noul is absolute: all of them can be low. If you need both answers, ask both on the same shortlist.
  • Filter state before sending. Unrelated detail costs accuracy (context rot), and the context window has a limit. Send only the fields the question names.
  • Adversarial content is data the model does not treat as hostile. Frame untrusted text as measurements to judge, never as instructions. Test those edges before you act on answers in production.
  • No generation. If the answer space is bounded, extract the options in code or with a generative model. Then let Jev pick one.

Sponsors

License

Licensed under the MIT license. Forked from pithings/advocaat (MIT); question and answer shapes mirror github.com/typesafe-ai/typesafe-sdk-js.