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

@optiqio/qbrix

v0.1.0

Published

JavaScript/TypeScript SDK for the Qbrix multi-armed bandit platform — select and feedback in ~10 lines.

Readme


A tiny, isomorphic SDK for multi-armed-bandit selection and feedbackselect an arm, render it, report a reward. Works in the browser, Node 18+, Deno, Bun, and edge runtimes, with zero runtime dependencies.

Install

npm install @optiqio/qbrix

Quickstart

import { QbrixClient } from "@optiqio/qbrix";

const qbrix = new QbrixClient({ apiKey: process.env.QBRIX_API_KEY });

// 1. select an arm for this user/context
const { arm, requestId } = await qbrix.select("homepage-cta", { id: "user-42" });

// 2. render the chosen arm
console.log(`showing: ${arm.name}`);

// 3. report the outcome — pass back the requestId from select
await qbrix.feedback(requestId, 1.0); // e.g. 1 = converted, 0 = no action

select returns the chosen arm, a requestId, and isDefault. The requestId is the handle that ties a later feedback call back to the decision — hold onto it.

Use it server-side

[!IMPORTANT] Your API key (optiq_…) is a secret. The SDK sends it as an X-API-Key header, so anywhere the client runs, the key goes too. Bundling it into browser code exposes it to every visitor. Run qbrix on a server (route handler, edge function, backend) and keep the key in an environment variable. Have the browser call your endpoint, not Qbrix directly.

The recommended pattern is a thin server-side handler — the key never reaches the client:

// edge / route handler — runs on the server
import { QbrixClient } from "@optiqio/qbrix";

const qbrix = new QbrixClient({ apiKey: process.env.QBRIX_API_KEY });

export default async function handler(req: Request): Promise<Response> {
  const { userId } = await req.json();
  const { arm, requestId } = await qbrix.select("homepage-cta", { id: userId });
  return Response.json({ arm, requestId });
}

See examples/edge-route.ts and examples/node-quickstart.ts for runnable versions.

Browser usage is supported for trusted or low-stakes contexts (internal tools, prototypes), but it is a deliberate trade-off: a key shipped to the browser is public. Prefer a server-side proxy for anything user-facing.

API

new QbrixClient(options?)

| Option | Type | Default | Env fallback | | --- | --- | --- | --- | | apiKey | string | — | QBRIX_API_KEY | | baseUrl | string | http://localhost:8080 | QBRIX_BASE_URL | | timeout | number (ms) | 30000 | — | | maxRetries | number | 2 | — | | retryOn | number[] | [429, 502, 503, 504] | — | | fetch | typeof fetch | runtime global | — | | headers | Record<string, string> | {} | — | | logger | QbrixLogger | console (when a level is active) | — | | logLevel | LogLevel | "off" | QBRIX_LOG, QBRIX_DEBUG |

Resolution order per option: explicit argument → environment variable → default.

select(experimentId, context)

select(experimentId: string, context: Context): Promise<SelectResult>
interface Context {
  id: string;                          // required — a stable user/session identifier
  vector?: number[];                   // optional feature vector
  metadata?: Record<string, unknown>;  // optional arbitrary attributes
}

interface SelectResult {
  arm: { id: string; name: string; index: number };
  requestId: string;   // pass this back into feedback()
  isDefault: boolean;  // true when the platform returned the fallback arm
}

feedback(requestId, reward)

feedback(requestId: string, reward: number): Promise<void>

Reports the outcome for a prior select. requestId is the value returned by that select; reward is the observed signal (e.g. 1 for a conversion, 0 for none — any numeric reward your experiment defines).

Errors

Every failure throws a typed error from the QbrixError hierarchy — catch the ones you care about with instanceof.

import {
  QbrixAPIError,
  RateLimitedError,
  AuthenticationError,
  QbrixTimeoutError,
} from "@optiqio/qbrix";

try {
  const { arm, requestId } = await qbrix.select("homepage-cta", { id: "user-42" });
} catch (err) {
  if (err instanceof RateLimitedError) {
    console.warn(`rate limited; retry after ${err.retryAfter}s`);
  } else if (err instanceof AuthenticationError) {
    throw new Error("check your QBRIX_API_KEY");
  } else if (err instanceof QbrixTimeoutError) {
    // request exceeded `timeout`
  } else if (err instanceof QbrixAPIError) {
    console.error(`qbrix ${err.status} ${err.code}: ${err.detail}`);
  }
  throw err;
}
  • QbrixError — base class for everything thrown by the SDK.
  • QbrixAPIError — the proxy returned a non-2xx response. Carries status, detail, a machine-readable code, and optional context. Status-specific subclasses: BadRequestError (400), AuthenticationError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), RateLimitedError (429, adds retryAfter), InternalServerError (500), BadGatewayError (502), ServiceUnavailableError (503), GatewayTimeoutError (504).
  • QbrixConnectionError — the request never completed (network failure).
  • QbrixTimeoutError — the request exceeded timeout.

Logging

The SDK is silent by default. Turn on logging by setting logLevel ("debug" | "info" | "warn" | "error" | "off"), injecting a logger, or via the QBRIX_LOG / QBRIX_DEBUG environment variables — handy for debugging a running deployment without a code change.

// route to the console at a chosen verbosity
new QbrixClient({ apiKey, logLevel: "debug" });

// or plug in your own sink (pino, winston, …) — receives only request metadata
new QbrixClient({
  apiKey,
  logger: {
    debug: (msg, ctx) => log.debug(ctx, msg),
    info: (msg, ctx) => log.info(ctx, msg),
    warn: (msg, ctx) => log.warn(ctx, msg),
    error: (msg, ctx) => log.error(ctx, msg),
  },
});
QBRIX_LOG=warn node app.js   # or QBRIX_DEBUG=1 for full debug output

The transport logs each request attempt and success at debug, retries at warn, and failures (give-up, timeout, connection) at error. Log context is limited to method, path, status, and attempt counts — it never includes your API key, headers, or request/response bodies.

Runtime support

Runs unmodified on Node 18+, Deno, Bun, edge runtimes, and the browser — built on universal primitives (fetch, AbortController) with zero runtime dependencies. Ships dual ESM + CJS with bundled type declarations.

Full documentation: qbrix.io/docs.

Development

npm install
npm run build      # dual ESM + CJS + .d.ts via tsup
npm run test       # vitest
npm run typecheck  # tsc --noEmit
npm run lint       # biome

License

MIT