Skip to content

re-utils/stnl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

80 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A simple, opinionated type validator built for performance.

Features

  • Types work across languages
  • Efficient representation format
  • Fast compilation time

Builder

stnl schema builder.

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:

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
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

// Match 'admin' or 'user'
t.union(['admin', 'user']);

Constants

t.value() only accepts number, string, or boolean.

// Match only 0
t.value(0);

// Match only 'str'
t.value('str');

// Match only true
t.value(true);

Lists

// 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

// { 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

// [number, string]
t.tuple([
  t.int,
  t.string
]);

Tagged unions

// { 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:

// { name: string, id: number } | null
t.nullable(
  t.dict({
    name: t.string,
    id: t.int
  })
);

Scopes & references

Recursive types with scope:

// 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:

const user = t.scope(
  t.dict({
    name: t.ref('name')
  }),
  { name: t.string }
};

Generics with scope:

// 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.

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.

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.

// 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 (require Function for code generation).

const schema = compat.standardSchema.toV1(user, 'User validation failed');

// Use standard schema
console.log(schema['~standard'].validate({}));

Compilers

stnl schema compilers.

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):

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:

const stringifyUser = build.json.stringify(schema);
if (isUser(user))
  console.log(stringifyUser(user) === JSON.stringify(user)); // true

About

A validator format

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published