stnl
Version:
A simple, opinionated type validator built for performance
283 lines (237 loc) • 5.52 kB
Markdown
A simple, opinionated type validator built for performance.
## Features
- Types work across languages
- Efficient representation format
- Fast compilation time
## Builder
`stnl` schema builder.
```ts
import { t, l } from 'stnl';
// or
import { type, limit } from 'stnl';
```
### Type inference
To infer payload type of a schema built using the schema builder:
```ts
const schema = t.list(t.int);
// number[]
type T = t.TInfer<typeof schema>;
```
### Primitives
- `t.int`: integer
- `t.float`: floating-point number
- `t.string`: string
- `t.bool`: boolean
- `t.any`: any type
```ts
t.int; // integer
t.float; // floating-point number
t.string; // strings
t.bool; // boolean
t.any; // any type
l.int(l.min(5)); // integer >= 5
l.int(l.max(9)); // integer <= 9
l.int(l.min(5), l.max(9)); // 5 <= integer <= 9
l.float(l.min(5)); // float >= 5
l.float(l.max(9)); // float <= 9
l.float(l.min(5), l.max(9)); // 5 <= float <= 9
l.string(l.minLen(5)); // string.length >= 5
l.string(l.maxLen(9)); // string.length <= 9
l.string(l.minLen(5), l.maxLen(9)); // 5 <= string.length <= 9
```
### Unions
```ts
// Match 'admin' or 'user'
t.union(['admin', 'user']);
```
### Constants
`t.value()` only accepts `number`, `string`, or `boolean`.
```ts
// Match only 0
t.value(0);
// Match only 'str'
t.value('str');
// Match only true
t.value(true);
```
### Lists
```ts
// A list of integers
t.list(t.int);
// A list of string with list.length >= 1
t.list(t.string, l.minLen(1));
// A list of float with list.length <= 10
t.list(t.float, l.maxLen(10));
// A list of float with 1 <= list.length <= 10
t.list(t.float, l.minLen(1), l.maxLen(10));
```
### Objects
```ts
// { id: number, name: string, display_names?: string[] }
t.dict(
// Required properties
{
id: t.int,
name: t.string
},
// Optional properties
{
display_names: t.list(t.string)
}
);
```
### Tuples
```ts
// [number, string]
t.tuple([
t.int,
t.string
]);
```
### Tagged unions
```ts
// { role: 'admin', id: string } | { role: 'user', name: string }
t.tag('role', {
admin: t.dict({
id: t.string
}),
user: t.dict({
name: t.string
})
});
```
### Nullable types
To make a schema accepts `null`:
```ts
// { name: string, id: number } | null
t.nullable(
t.dict({
name: t.string,
id: t.int
})
);
```
### Scopes & references
Recursive types with scope:
```ts
// interface Node { value: string, next: Node | null }
const node = t.scope(
t.dict(
{ value: t.string },
{ next: t.self } // Reference to the root type of the scope
)
);
```
References defined types in scope:
```ts
const user = t.scope(
t.dict({
name: t.ref('name')
}),
{ name: t.string }
};
```
Generics with scope:
```ts
// node is an unresolved type
const node = t.dict(
{ value: t.ref('type') },
{ next: t.self }
);
// This will error as not all references of node has been resolved
type Node = t.TInfer<typeof node>;
// int_node is a resolved type
const int_node = t.scope(node, {
type: t.int
});
```
### Modules
Modules can also be used for storing types that depends on other types.
```ts
const mod = t.module({
value: t.string,
node: t.dict({
prefix: t.string,
store: t.ref('value')
}, {
param: t.ref('param_node'),
child: t.ref('node')
}),
param_node: t.dict({
name: t.string
}, {
store: t.ref('value'),
child: t.ref('node')
}),
});
// Use module schemas
const schema = t.dict({
root: mod.node,
param_root: mod.param_node
});
```
You should only use `t.module()` when you need to re-use types in a scope.
## Compatibility
Translate `stnl` schema to other formats.
```ts
import { compat, build, t, l } from 'stnl';
// Used for examples below
const user = t.dict({
name: l.string(l.minLen(3), l.maxLen(16)),
code: l.string(l.minLen(8), l.maxLen(32))
});
```
### JSON schema
Convert `stnl` schema to a [JSON schema](https://json-schema.org/).
```ts
// 2020-12 spec schema
const schema = compat.toJSONSchema(user);
// Set schema version for root schema (has type hint)
schema.$schema = 'https://json-schema.org/draft/2020-12/schema';
```
Due to the inference strategy of the current system, type inference for JSON schema is impossible.
### Standard schema
Convert `stnl` to a [standard schema](https://standardschema.dev) (require `Function` for code generation).
```ts
const schema = compat.standardSchema.toV1(user, 'User validation failed');
// Use standard schema
console.log(schema['~standard'].validate({}));
```
## Compilers
`stnl` schema compilers.
```ts
import { build, t, l } from 'stnl';
// Used for examples below
const schema = t.dict({
name: l.string(l.minLen(3), l.maxLen(16)),
code: l.string(l.minLen(8), l.maxLen(32))
});
const user = {
name: 'reve',
code: '1234567890'
};
```
### Assert JSON
To compile a schema to a JSON type assertion function (require `Function` for code generation):
```ts
const isUser = build.json.assert(schema);
if (isUser(user)) {
console.log('Name', user.name);
console.log('Code', user.code);
}
```
### Stringify JSON
To compile a schema to an optimized JSON stringifier function:
```ts
const stringifyUser = build.json.stringify(schema);
if (isUser(user))
console.log(stringifyUser(user) === JSON.stringify(user)); // true
```
### Usage with `runtime-compiler`
Some `build` module supports `runtime-compiler`.
```ts
// runtime-compiler compatible modules ends with .rt
import build from 'stnl/build/json/stringify.rt';
// Build to a value string, eg $0(o)
build(schema, 'o');
```