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

jevaro

v0.5.0

Published

Send batches of states to Jev and stream Arrow results

Readme

Jevaro JavaScript SDK

Send a batch of states and one shared set of questions to Jev through the Jevaro batching proxy and read the answers in input order as an Apache Arrow stream.

Part of Jevaro, an experiment in bulk inference. The batching proxy handles concurrent API calls, retries, and conversion from JSON to Arrow. This SDK returns a reader with one row per state and one column per question.

Works in modern browsers and Node.js 22+. Requires a running Jevaro server. ESM imports, CommonJS, and TypeScript are supported; browsers can use a bundler or an ESM CDN. Start with the quickstart.

Install

npm install jevaro

Read results

import { TypeSafeClient, noul } from "jevaro";

const client = new TypeSafeClient();
const reader = await client.systemOne({
  states: ["Please refund the shoes.", "Where is my parcel?"],
  questions: { refund: noul("Is a refund being requested?") },
});

try {
  console.log(reader.schema);
  for await (const batch of reader) {
    for (const row of batch) console.log(row.refund);
  }
} finally {
  await reader.cancel();
}

For CommonJS, use const { TypeSafeClient, noul } = require("jevaro").

The result is an Apache Arrow AsyncRecordBatchStreamReader. The schema arrives before answers. The first batch has zero rows; later batches have one or more rows each. await reader.readAll() collects an array of record batches.

Full iteration releases the response. Breaking a for await loop cancels it. Call reader.cancel() if you open a reader without iterating it. There is no client-level close() method.

Browser use

Try the standalone HTML example with npx serve: edit JSON states and questions, or choose images, then view the streamed results in a table, with each Box drawn on its image. It loads the SDK from a CDN and includes instructions for starting a local server with CORS.

Bundle the import above with your app, then set the server address explicitly:

const client = new TypeSafeClient({ baseURL: window.location.origin });

This assumes your web server forwards /v1/systemone to Jevaro without buffering. Set TYPESAFE_API_KEY on the Jevaro server so the key stays there. For a server on a different origin, configure CORS for your app's origin; Jevaro does not enable CORS by default.

The browser needs streaming fetch and AbortSignal.any. Node.js is not required at runtime in the browser.

Questions and arguments

choice, score, noul, box, image, Image, and withAssets are re-exported from the official TypeSafe SDK. Plain question objects work too. See the bundled three-type example.

await client.systemOne(request, options) accepts:

| Request field | Meaning | | --- | --- | | state | One string, object, array, or image | | states | A nonempty array of states, or an Arrow Table | | stateColumn | With a Table, the column whose values are the states | | questions | A nonempty map of question IDs to definitions | | model | Override the client's default model | | rowsPerCall | States per upstream call, 1 to 256; above 1, packs states | | assets | Images under names you choose, to write inside text | | boxFormat | "typesafe" (default) or "geoarrow": how Box answers store their edges |

Supply exactly one of state and states. An array in singular state is one evaluation. Use states to evaluate its elements separately.

The optional second argument accepts headers, timeout in milliseconds, and an AbortSignal as signal. A signal can cancel after the schema arrives.

Arrow states

states also accepts an Apache Arrow Table. The SDK sends it to Jevaro as an Arrow IPC stream. Each row becomes one state.

Without stateColumn, each state is an object of the row's columns. With stateColumn, each state is that column's value, and the other columns are not sent:

import { tableFromArrays } from "apache-arrow";
import { TypeSafeClient, noul } from "jevaro";

const client = new TypeSafeClient();
const tickets = tableFromArrays({
  id: Int32Array.from([101, 102]),
  subject: ["Shoes", "Parcel"],
  body: ["Please refund the shoes.", "Where is my parcel?"],
});

// First state: {"id": 101, "subject": "Shoes", "body": "Please refund the shoes."}
const rows = await client.systemOne({
  states: tickets,
  questions: { refund: noul("Does the customer's `body` ask for a refund?") },
});
for await (const batch of rows) {
  for (const row of batch) console.log(row.refund);
}

// First state: "Please refund the shoes."
const bodies = await client.systemOne({
  states: tickets,
  stateColumn: "body",
  questions: { refund: noul("Is a refund being requested?") },
});
for await (const batch of bodies) {
  for (const row of batch) console.log(row.refund);
}

Use typed arrays such as Int32Array for integer columns. A plain array of numbers becomes a float column, so 101 would be sent as 101.0.

To send some columns but not others, put them in a struct column and name it as the state column. Here id stays in the table but out of the states. Answer rows follow input rows, so the row index joins each answer to its ticket:

const tickets = tableFromArrays({
  id: Int32Array.from([101, 102]),
  ticket: [
    { subject: "Shoes", body: "Please refund the shoes." },
    { subject: "Parcel", body: "Where is my parcel?" },
  ],
});

// First state: {"subject": "Shoes", "body": "Please refund the shoes."}
const reader = await client.systemOne({
  states: tickets,
  stateColumn: "ticket",
  questions: { refund: noul("Does the ticket's `body` ask for a refund?") },
});
let index = 0;
for await (const batch of reader) {
  for (const row of batch) console.log(tickets.get(index++).id, row.refund);
}

A state column can also hold lists, maps, or arrow.json values. See the HTTP API for how Arrow types become JSON.

Create the Table with apache-arrow 21.2.0, the version this SDK uses. Its CommonJS and ES module builds both work; a Table from another version is rejected with a TypeError.

Images and Box questions

States and questions can include images, and a box question finds where something is in one. TypeSafe calls these features dreamer-ready: fit for prototypes, not yet for production. Send the TypeSafe-Danger-Zone-Images: true header, which Jevaro passes on to TypeSafe. box needs Jev 1.14, so use jev-preview:

import { openAsBlob } from "node:fs";
import { TypeSafeClient, box, image, noul } from "jevaro";

const client = new TypeSafeClient({ defaultModel: "jev-preview" });
const reader = await client.systemOne({
  states: [
    { id: 1, photo: image(await openAsBlob("kitchen.jpg")) },
    { id: 2, photo: image(await openAsBlob("office.png")) },
  ],
  questions: {
    mug: box("the coffee mug in `photo`"),
    mine: noul({ question: "Is the mug in `photo` the one in `example`?", example: image(await openAsBlob("my-mug.jpg")) }),
  },
}, { headers: { "TypeSafe-Danger-Zone-Images": "true" } });

for await (const batch of reader) {
  for (const row of batch) console.log(JSON.stringify({ mug: row.mug, mine: row.mine }));
}

Each row has the box's edges, as fractions of the image's height and width, and the Noul:

{"mug":{"box":{"top":0.25,"bottom":0.82,"left":0.44,"right":0.84},"confidence":0.92},"mine":0.87}
{"mug":{"box":{"top":0.31,"bottom":0.64,"left":0.12,"right":0.35},"confidence":0.88},"mine":0.08}

The SDK writes each image as its reference, String(image), such as <image:3fa85f64...>, and uploads the file once, in its own part of the request. Jevaro sends each upstream call the images that its state and the questions refer to: here, one photo and my-mug.jpg. Create an image from a Blob, such as a File, from bytes with image(), or from base64 text with Image.fromBase64(). Images from the SDK's ES module and CommonJS builds both work. In a browser, image(file) sends a file the user chose.

To send the header with every request, create the client with defaultHeaders: { "TypeSafe-Danger-Zone-Images": "true" }. Jevaro passes on every TypeSafe-Danger-Zone-* header, including TypeSafe-Danger-Zone-Long-Context. rowsPerCall above 1 can't be used with images or Box questions.

Images in Arrow states

In an Arrow Table, each binary value is an image. Jevaro writes a reference in its place and sends the image with that row's call:

import { readFileSync } from "node:fs";
import { Binary, Int32, Table, vectorFromArray } from "apache-arrow";

const photos = new Table({
  id: vectorFromArray([1, 2], new Int32()),
  photo: vectorFromArray([readFileSync("kitchen.jpg"), readFileSync("office.png")], new Binary()),
});
// First state: {"id": 1, "photo": "<image:5d1c2a7e0b9f4c3d8e6a000000000000>"}
const boxes = await client.systemOne({
  states: photos, questions: { mug: box("the coffee mug in `photo`") },
}, { headers: { "TypeSafe-Danger-Zone-Images": "true" } });

With stateColumn: "photo", each state is the image alone; write instructions such as "the coffee mug".

Name images in text

To write an image inside text, use withAssets, or pass it in assets under a name of your choice and write the name where the image belongs:

import { withAssets } from "jevaro";

const myMug = image(await openAsBlob("my-mug.jpg"));
const kitchen = { id: 1, photo: image(await openAsBlob("kitchen.jpg")) };
const headers = { "TypeSafe-Danger-Zone-Images": "true" };

// The same question two ways:
await client.systemOne({ states: [kitchen], questions: { mine: noul(withAssets`Is the mug in \`photo\` this one: ${myMug}?`) } }, { headers });
await client.systemOne({
  states: [kitchen], questions: { mine: noul("Is the mug in `photo` this one: <my-mug>?") }, assets: { "<my-mug>": myMug },
}, { headers });

Use names that can't appear in your text by accident. Every name must appear in a state, instructions, or criteria. An image in assets is written as its name wherever you place it, including in withAssets text, and uploaded once.

Box answers

By default, a Box column keeps TypeSafe's edge names: row.mug.box.left. With boxFormat: "geoarrow", its box field is a GeoArrow box, {xmin, ymin, xmax, ymax}:

const geo = await client.systemOne({
  states: photos, questions: { mug: box("the coffee mug in `photo`") }, boxFormat: "geoarrow",
}, { headers: { "TypeSafe-Danger-Zone-Images": "true" } });
// The first row's JSON.stringify(row.mug.box): {"xmin":0.44,"ymin":0.25,"xmax":0.84,"ymax":0.82}

confidence is null when TypeSafe doesn't report one. See the Arrow schema for both layouts.

TypeSafe checks each image: PNG or JPEG, 256 to 1,568 pixels on each side, and its other limits. An image that it rejects ends the stream at that row, so prepare images first. See the HTTP API for which images each call sends and the checks Jevaro makes first.

Pack states into fewer calls

rowsPerCall puts up to that many states in each upstream call:

const reader = await client.systemOne({ states: messages, questions, rowsPerCall: 64 });

The answers have the same schema and row order as without it.

In a live test of short messages with three questions, this raised throughput from 1,040 to 2,000 states per second and used 28% fewer input tokens.

There are two tradeoffs:

  • Answers shift slightly, and consistently. About 1.5% of Choice answers differed from one state per call.
  • Long states with many questions cost more. Each question carries its own copy of the state.

See the HTTP API for how it works and which questions it rejects.

Client configuration

| Option | Default | | --- | --- | | apiKey | TYPESAFE_API_KEY; otherwise the server may supply its key | | baseURL | TYPESAFE_BASE_URL, then http://127.0.0.1:8000 | | defaultModel | TYPESAFE_DEFAULT_MODEL, then jev-latest | | timeout | 60,000 milliseconds to receive the schema | | defaultHeaders | No extra headers | | fetch | Global fetch |

Environment variable defaults apply in Node.js. In browsers, pass options directly to the constructor.

The timeout ends when the schema arrives. Use signal to set a deadline for reading the whole response.

Metadata

The schema uses Arrow extension metadata for Choice, Noul, Score, and Box. Capture reader.schema before iteration; Arrow may clear it when the reader closes. Choice selections index the field's shared labels:

// With the "department" Choice from example.mjs:
const field = reader.schema.fields.find(f => f.name === "department");
const { labels } = JSON.parse(field.metadata.get("ARROW:extension:metadata"));
// Resolve a row's choice with labels[row.department.choice].

Score legends and probability positions also come from field metadata. Labels and legends are stored once in the schema. Noul stores the probability of yes, and Box stores four edges and a confidence. See the Arrow schema.

Errors and compatibility

Invalid argument combinations reject with TypeError. HTTP errors before streaming reject with APIError, which has status and body properties. Transport, decoding, and row-count errors reject while reading.

This SDK implements System One evaluation with Arrow results. Model listing, JSON answer objects, APIPromise helpers, and the official SDK's retry/error API are outside its API. Per-state retries happen at the batching proxy; this SDK does not retry an entire batch.

See Contributing for tests.

Apache-2.0. Copyright 2026 Columnar Technologies Inc.