JSPM

  • Created
  • Published
  • Downloads 46
  • Score
    100M100P100Q79885F
  • License MIT

A simple, opinionated type validator built for performance

Package Exports

  • stnl
  • stnl/build
  • stnl/build/json
  • stnl/build/json/assert
  • stnl/build/json/stringify
  • stnl/build/utils
  • stnl/compat
  • stnl/compat/json-schema
  • stnl/limit
  • stnl/regex
  • stnl/type

Readme

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.

JSON schema

Convert stnl schema to 2020-12 spec JSON schema.

import { compat } from 'stnl';

const schema = compat.toJSONSchema(
  t.dict({
    name: l.string(l.minLen(3), l.maxLen(16)),
    code: l.string(l.minLen(8), l.maxLen(32))
  })
);

// Set schema version for root schema (has type hint)
schema.$schema = 'https://json-schema.org/draft/2020-12/schema';

Type inference for JSON schema is impossible with the current inference system.

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.compile(schema);
if (isUser(user)) {
  console.log('Name', user.name);
  console.log('Code', user.code);
}

For code injection to other functions:

console.log(build.json.assert.code(schema));

Stringify JSON

To compile a schema to an optimized JSON stringifier function:

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