@hazae41/gardien
v1.0.0
Published
Runtime schema validation and parsing
Maintainers
Readme
Gardien
Runtime schema validation and parsing
npm i @hazae41/gardienFeatures
Current features
- No external dependencies
- Rust-like patterns
- Under-engineered
- Unit-tested
- Extensible
Usage
Parsing a number
We can parse something as a number and then validate it
import { asOrThrow, $numberable, $number } from "@hazae41/gardien"
const value = asOrThrow($numberable().then($number.nonNegative()), "0x123")This is like
const value = Number("0x123")
if (value < 0)
throw new Error()
return valueValidating a string with an error message
import { asOrThrow, $string } from "@hazae41/gardien"
asOrThrow($string().then($length.minmax(6, 24, "Password must be between 6 and 24 characters")), password)This is like
if (typeof password !== "string")
throw new Error()
if (password.length < 6 || password.length > 24)
throw new Error("Password must be between 6 and 24 characters")Validating a record
We can define complex guards that include other guards
const RpcRequestGuard = $object().then($record({
jsonrpc: $strong("2.0"),
id: $either([$strong(null), $number(), $string()]),
method: $string(),
params: $pass()
} as const))
function onMessage(message: string) {
const request = asOrThrow(RpcRequestGuard, JSON.parse(message) as unknown)
if (request.method === "example")
return void example(request)
throw new Error("Unknown method")
}Validating a generic record
We can define high-order guards that dynamically include other guards
import { Guard } from "@hazae41/gardien"
const RpcRequestGuard = <M extends Guard<unknown, string>, P extends Guard<unknown, unknown>>(method: M, params: P) => $object().then($record({
jsonrpc: $strong("2.0"),
id: $union([$strong(null), $number(), $string()]),
method: method,
params: params
} as const))
const ExampleRequestGuard = RpcRequestGuard($strong("example"), $object().then($record({
example: $string()
} as const)))
const request = asOrThrow(ExampleRequestGuard, JSON.parse(message) as unknown)Validating with your own logic
Make your own guard
export interface Guard<I = any, O = any> {
asOrThrow(value: I): O
}export class IPv4Guard {
static asOrThrow(value: string): string {
if (!/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(value))
throw new Error()
return value
}
}Then make an helper (optional)
import { $error } from "@hazae41/gardien"
export function $ipv4(message?: string) {
return $error(IPv4Guard, message)
}Then use it as you wish
asOrThrow($string().then($ipv4("This is not an IPv4 address")), input)Parsing with your own logic
You can parse and transform with your own logic
export class ZeroHexlifyGuard {
static asOrThrow(value: any): `0x${string}` {
return `0x${BigInt(value).toString(16)}`
}
}console.log(asOrThrow(ZeroHexlifyGuard, "12345")) // 0x3039Variables narrowing
You can use is to narrow variables
function f(x: unknown) {
if (is($string(), x)) {
console.log(x.toUpperCase())
} else {
console.log("not a string")
}
}