Skip to main content

@antithrow/standard-schema

Bridges Standard Schema–conforming validators (Zod, Valibot, ArkType, etc.) to antithrow Result and ResultAsync types.

Installation

npm install @antithrow/standard-schema

You'll also need a Standard Schema–compatible validator:

npm install zod

validate()

function validate<S extends StandardSchemaV1>(
schema: S,
value: unknown,
options?: StandardSchemaV1.Options,
): ResultAsync<StandardSchemaV1.InferOutput<S>, StandardSchemaV1.FailureResult>;

Validates a value against a Standard Schema. Always returns ResultAsync because Standard Schema validators may be async.

Exceptions thrown by the validator are caught and wrapped in a synthetic FailureResult with a single issue containing the error message.

import { validate } from "@antithrow/standard-schema";
import { z } from "zod";

const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
});

const result = await validate(UserSchema, {
name: "Alice",
email: "alice@example.com",
});

result.match({
ok: (user) => console.log("Valid:", user),
// user is { name: string; email: string }
err: ({ issues }) => console.error("Invalid:", issues),
});

validateSync()

function validateSync<S extends StandardSchemaV1>(
schema: S,
value: unknown,
options?: StandardSchemaV1.Options,
): Result<StandardSchemaV1.InferOutput<S>, StandardSchemaV1.FailureResult>;

Synchronous variant. Returns a plain Result instead of ResultAsync.

Throws TypeError if the schema's validate method returns a Promise — use validate() instead for schemas that may be async.

import { validateSync } from "@antithrow/standard-schema";
import { z } from "zod";

const NumberSchema = z.number().min(0);

const result = validateSync(NumberSchema, 42);
// ok(42)

const invalid = validateSync(NumberSchema, -1);
// err({ issues: [{ message: "Number must be greater than or equal to 0" }] })

Error type: FailureResult

Both validate() and validateSync() use StandardSchemaV1.FailureResult as the error type:

interface FailureResult {
issues: Array<{
message: string;
path?: Array<PropertyKey | StandardSchemaV1.PathSegment>;
}>;
}

This is the standard error shape defined by the Standard Schema specification. Each issue contains a message and an optional path indicating which field failed.

Composing with chain()

Combine schema validation with other Result operations:

import { chain, errAsync, type ResultAsync } from "antithrow";
import { validate } from "@antithrow/standard-schema";
import { z } from "zod";

const EmailSchema = z.string().email();

type AppError = { type: "validation"; message: string } | { type: "duplicate" };

function createAccount(input: unknown): ResultAsync<Account, AppError> {
return chain(async function* () {
const email = yield* validate(EmailSchema, input).mapErr(
({ issues }): AppError => ({
type: "validation",
message: issues[0]?.message ?? "Invalid input",
}),
);

const exists = yield* checkEmailExists(email);
if (exists) {
return yield* errAsync<Account, AppError>({ type: "duplicate" });
}

return yield* saveAccount(email);
});
}

Supported validators

Any validator implementing the Standard Schema spec works out of the box: