shipwithjev

Catalog / Tools & apps

0497GitHub

zod-jev

Pairs local Zod shape validation with Jev semantic validation.

jomatsu/zod-jevREADME ↗
# zod-jev

[](https://www.npmjs.com/package/zod-jev)
[](./LICENSE)

**Zod validates the shape. [Jev](https://typesafe.ai/) validates the meaning.**

`zod-jev` composes TypeSafe [Jev](https://typesafe.ai/) (System One) semantic checks into
[Zod](https://zod.dev) 4 schemas. Shape rules stay in Zod. Judgments that only a model can make
("does this body contain personal data?", "is this price plausible for this item?", "does the
category match the description?") become calibrated probabilities that your code can threshold on.

A single `parseAsync` call sends every rule of that schema to Jev in a **single request**, then turns
the probabilities into Zod issues. Callers continue to use ordinary Zod APIs and error objects.

```ts
import { createJevZod, getSemanticIssues } from "zod-jev";

const z = createJevZod(); // reads TYPESAFE_API_KEY

const Ticket = z.semantic(z.object({ subject: z.string(), body: z.string() }), [
  {
    id: "refund_requested",
    is: "`value.body` asks for a refund or a reversal of a charge",
    message: "This does not look like a refund request.",
    path: ["body"],
  },
  {
    id: "body_has_no_pii",
    is: "`value.body` contains no personal data (name, email, phone, card number, order id)",
    message: "Please remove personal data from the message.",
    path: ["body"],
  },
]);

const result = await Ticket.safeParseAsync({
  subject: "Duplicate charge",
  body: "I was charged twice for order A-104. Please refund the duplicate.",
});

if (!result.success) {
  for (const issue of getSemanticIssues(result.error)) {
    console.log(issue.path.join("."), issue.details.kind, issue.message);
  }
}
```

- **No new schema dialect.** `semantic()` returns the *same* schema type as your base schema with an
  extra async check. Methods such as `.extend()`

Also filed under Tools & apps