Some convenience utils for Standard Schema, expanding upon the existing @standard-schema/utils.
Mainly provides functions that make it easier to use Standard Schema without having to reuse code.
Heavily inspired by Zod and Valibot.
// The default export assumes Async Schemas...
import * as ss from "@ruintd/standard-utils";
// But there's also an export for Sync Schemas!
import * as ssSync from "@ruintd/standard-utils/sync";
// You can also access Sync methods from the Async export
// by appending Sync to the method names, and vice versa.
import * as z from "zod";
import * as v from "valibot";
const Player = z.object({
username: z.string(),
xp: z.number(),
});
const data = { username: "billie", xp: 100 };
const badData = { username: 42, xp: "100" };
await ss.parse(Player, data);
// { username: "billie", xp: 100 }
await ss.decode(Player, badData);
// Similar to parse, but expects a strongly typed input.
// Argument of type '...' is not assignable to parameter of type '...'.
await ss.validate(Player, data);
// { success: true, value: { username: "billie", xp: 100 } }
await ss.safeParse(Player, badData); // alias of "validate"
// { success: false, issues: [ ... ] }
// Piping
const stringToLength = ss.pipe(
v.string(),
z.transform((val) => val.length),
);For error handling, we export a prettifyError function;
which can take a FailureResult (from validate),
Issue[] (from FailureResult.issues), a single Issue, or a SchemaError;
and returns a user-friendly error message, similar to Zod's prettifyError:
try {
await ss.parse(Player, badData);
} catch (e) {
if (e instanceof ss.SchemaError) {
console.log(ss.prettifyError(e));
/*
✖ Invalid input: expected string, received number
→ at username
✖ Invalid input: expected number, received string
→ at xp
*/
}
}Or validate will return an object containing an issues array:
await ss.validate(Player, badData);
/* {
success: false,
issues: [
{
expected: "string",
code: "invalid_type",
path: [ "username" ],
message: "Invalid input: expected string, received number"
},
{
expected: "number",
code: "invalid_type",
path: [ "xp" ],
message: "Invalid input: expected number, received string"
}
]
} */Since TypeScript doesn't allow type guarding with async functions1, type guarding is only available for sync schemas.
data; // unknown
if (ssSync.is(Player, data)) {
data; // Player
}
data; // unknown
ssSync.assert(Player, data);
data; // Playerconst getUsername = ssSync.parser(Player); // (input: unknown) => string
getUsername(data); // "billie"
const getHostname = ssSync.decoder(
z.url().transform((url) => new URL(url).hostname),
); // (input: string) => string
getHostname("https://jsr.io/@ruintd/standard-utils"); // "jsr.io"const schema = ssSync.wrap(Player);
schema.parse(data);
schema.decode(data);
schema.validate(data);
schema.safeParse(data);
// Only on Wrapped *Sync* Schemas:
schema.is(data);
schema.assert(data);We also export SchemaStandardV1, as well as getDotPath and SchemaError from @standard-schema/utils.