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

@withboundary/contract

v1.5.1

Published

Make LLM outputs correct, not just valid. Schema + rules + auto-repair.

Readme

@withboundary/contract

npm version license

Make LLM output correct before it crosses into your system.

@withboundary/contract is a local TypeScript engine for accepting structured LLM output. It cleans model responses, validates them with Zod, checks your domain rules, and gives the model targeted repair instructions when an attempt fails.

It does not call Boundary. It does not proxy your LLM traffic. It does not send telemetry. The only network calls are the ones you write in your run function.

Install

npm install @withboundary/contract zod
pnpm add @withboundary/contract zod

Quickstart

import { enforce } from "@withboundary/contract";
import { z } from "zod";

const LeadScore = z.object({
  tier: z.enum(["hot", "warm", "cold"]),
  score: z.number().min(0).max(100),
  reason: z.string(),
});

const result = await enforce(
  LeadScore,
  async (attempt) => {
    const res = await openai.responses.create({
      model: "gpt-4.1",
      input: [
        { role: "system", content: attempt.instructions },
        { role: "user", content: "Score this lead: ACME, 500 employees..." },
        ...attempt.repairs,
      ],
    });

    return res.output_text;
  },
  {
    name: "lead-scoring",
    rules: [
      {
        name: "hot_requires_high_score",
        description: "Hot leads must have a score of at least 70",
        check: (lead) =>
          lead.tier !== "hot" ||
          lead.score >= 70 ||
          `tier is "hot" but score is ${lead.score} (minimum 70)`,
      },
    ],
  },
);

if (result.ok) {
  result.data;
  // typed as { tier: "hot" | "warm" | "cold"; score: number; reason: string }
}

If the model returns this:

{ "tier": "hot", "score": 25, "reason": "Strong fit" }

the schema passes, but the rule fails. The contract returns a repair message through attempt.repairs, retries your run function, and accepts only an output that satisfies the schema and every rule.

Why Use It

Schema validation catches wrong types. Contracts catch wrong decisions.

| Problem | What the contract does | | --------------------------------- | ----------------------------------------------- | | JSON wrapped in Markdown or prose | clean() extracts and parses the JSON | | "85" returned as a string | Primitive coercion normalizes common LLM output | | Schema mismatch | Zod issues become targeted repair messages | | Cross-field business rule failure | Named rules reject and explain the issue | | Transient model failure | The retry loop gives the model precise context |

Define Once

Use defineContract when the same contract runs in more than one place.

import { defineContract } from "@withboundary/contract";

const leadContract = defineContract({
  name: "lead-scoring",
  schema: LeadScore,
  rules: [
    {
      name: "hot_requires_high_score",
      description: "Hot leads must have a score of at least 70",
      check: (lead) =>
        lead.tier !== "hot" ||
        lead.score >= 70 ||
        `tier is "hot" but score is ${lead.score} (minimum 70)`,
    },
  ],
  retry: { maxAttempts: 4 },
});

const result = await leadContract.accept(runLLM, {
  model: "gpt-4.1-mini",
});

Each accept() call is independent. Logger hooks receive both the stable contractName and a per-call runHandle, so observability sinks can isolate concurrent runs of the same contract instance.

Rules

Rules are named deterministic checks over parsed, typed data.

rules: [
  {
    name: "invoice_math_consistent",
    description: "Subtotal plus tax must equal total",
    fields: ["subtotal", "tax", "total"],
    check: (invoice) =>
      Math.abs(invoice.subtotal + invoice.tax - invoice.total) < 0.01 ||
      `subtotal + tax does not equal total`,
  },
  {
    name: "line_items_required",
    description: "Invoices must include at least one line item",
    check: (invoice) => invoice.lineItems.length > 0 || "invoice has no line items",
  },
];

check returns true to pass. Return a string to fail with a repair message. fields is optional; the engine infers simple field reads from the function source. Set it explicitly when a rule delegates to helpers or is minified.

Result Shape

enforce and accept never throw for validation failure. They return a discriminated union:

type ContractResult<T> =
  | {
      ok: true;
      data: T;
      attempts: number;
      raw: string;
      durationMS: number;
    }
  | {
      ok: false;
      error: ContractError;
    };

Your run function can still throw. Thrown errors are captured as RUN_ERROR attempts and can be retried like other categories.

Observability

For local debugging:

import { createConsoleLogger } from "@withboundary/contract";

const result = await enforce(schema, runLLM, {
  name: "invoice-extraction",
  logger: createConsoleLogger({ showCleanedOutput: true }),
});

For production observability, pair with the optional cloud/custom-sink SDK:

import { createBoundaryLogger } from "@withboundary/sdk";

const logger = createBoundaryLogger({
  apiKey: process.env.BOUNDARY_API_KEY,
  environment: "production",
});

const contract = defineContract({
  name: "invoice-extraction",
  schema,
  rules,
  logger,
});

Installing only @withboundary/contract keeps everything in process. Nothing is sent to Boundary unless you add @withboundary/sdk and pass a logger.

Engine Primitives

The core pieces are exported for custom pipelines:

| Function | Purpose | | ------------------------------- | ------------------------------------------------------------- | | clean(raw) | Extract and normalize JSON-like LLM output | | verify(data, schema, rules?) | Validate parsed data with schema and rules | | classify(raw, cleaned) | Categorize empty, refusal, no JSON, truncated, parse failures | | repair(detail, overrides?) | Build repair messages for the next attempt | | instructions(schema) | Generate schema-driven prompt instructions | | createConsoleLogger(options?) | Print contract lifecycle hooks for debugging |

Works With Any Model

Boundary owns the acceptance boundary. You own the provider call. Use OpenAI, Anthropic, Gemini, Mistral, local models, or any function that returns text.

Security Model

  • No fetch, HTTP client, API key, analytics, or background worker in this package.
  • No prompt interception. The model call happens inside your run function.
  • No hidden persistence. Failed attempts are returned to your code in ContractResult.
  • Optional observability lives in a separate package, @withboundary/sdk.

Versioning

Follows semver. Breaking API changes ship in major releases; new options and types ship in minor releases; non-breaking enhancements ship in patches. The optional companion package @withboundary/sdk declares this package as a peer dependency.

Links

MIT