● Open sourceStreaming Schema Validation

tripwire.

Stop paying for a doomed generation

Over an API you cannot touch logits, so everyone enforces structured output with a retry loop: emit four thousand tokens, parse, fail, throw all of it away, pay again.

TypeScriptZero deps47 testsJSON Schema
Language TypeScriptTests 47License MIT
tripwire — demo
› {"sta ok
› tus": " ok
› "esc invalid
/status enum
↳ stopped at 15 of 59 chars
01 The problem

The evidence arrives early. The bill arrives late.

A model writing "esc into a field whose schema allows only open, closed or pending has already failed. Everything after that character is certain waste, and the retry loop pays for all of it before noticing.

02 The hard part

Where the work actually was.

Ordinary validation asks whether a finished value conforms. This asks whether any completion of a half-written value could — reasoning about a set of futures rather than a single present. One asymmetry governs the whole design: a wrong "keep going" costs a few tokens, a wrong "abort" kills a generation that was fine.

1

Resumable lexer

Chunks land mid-string, mid-escape, two of four hex digits into a \u sequence. Every partial state is representable.

2

Prefix reachability

No permitted enum value starts with what has been written? Provably dead, three characters in.

3

Stricter than JS

01, +1, 1. and .5 are rejected. Passing a document JSON.parse then throws on is the worst failure available.

4

Chunk-independent

Every no-false-positive case is streamed at every chunk size from 1 to full length.

5

Honest about regex

Full prefix matching needs Brzozowski derivatives. It extracts an anchored literal run and gives up otherwise.

6

Sound by default

Mid-number bound checks are opt-in, because 150e-1 is 15 and no prefix provably exceeds a maximum.

03 Measured

What it actually does.

From examples/benchmark.ts, on a realistic extraction schema:

case full tripwire saved hallucinated enum value 632 12 98% wrong type 612 33 95% bad phone format 607 88 86% nested enum wrong 631 206 67% too many array items 817 325 60% invented property 647 623 4% missing required field 432 432 — on the invalid documents alone: 61% fewer
04 Engineering

How it's built.

LanguageTypeScript, no build step
Lexerincremental, chunk-boundary safe
Reachabilityconservative by construction
PathsRFC 6901 JSON Pointer
Supportedenum, const, pattern, min/max, anyOf…
Tests47, every chunk size
05 Honestly

What it doesn't do.

No $ref, $defs, if/then/else or not — resolve references before handing the schema over. And two of the benchmark cases save nothing: a missing required property is not provable until the closing brace. Early abort only helps when the evidence arrives early. It usually does.

Want a closer look?

The README goes deeper, and the tests are the honest documentation. Happy to walk through the trade-offs.