npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

libtuple-schema

v1.0.0

Published

Schemas for libtuple

Readme

libtuple-schema


npm version GitHub Actions Workflow Status License

$ npm install libtuple-schema

A Schema allows you to define a complex structure for your immutables. It is defined by one or more SchemaMapper functions, which take a value and will either return it, or throw an error:

import s from 'libtuple-schema';

const boolSchema = s.boolean(); // returns the boolean SchemaMapper

boolSchema(true);  // returns true
boolSchema(false); // returns false
boolSchema(123);   // throws an error

I am giving up my bed for one night.

My Sleep Out helps youth facing homelessness find safe shelter and loving care at Covenant House. That care includes essential services like education, job training, medical care, mental health and substance use counseling, and legal aid — everything they need to build independent, sustainable futures.

By supporting my Sleep Out, you are supporting the dreams of young people overcoming homelessness.

Click here to help out: https://www.sleepout.org/participants/62915

More info: https://www.sleepout.org/ | https://www.covenanthouse.org/ | https://www.charitynavigator.org/ein/132725416

Together, we are working towards a future where every young person has a safe place to sleep.

Thank you.

and now back to your documentation...

Practical Example

Imagine validating a blog post payload from an API before processing:

import s from 'libtuple-schema';

const postSchema = s.record({
  id:          s.integer({min: 1}),
  title:       s.string({min: 1}),
  content:     s.string({min: 1}),
  tags:        s.array({each: s.string()}),
  publishedAt: s.dateString({nullable: true}),
});

// Example payload (possibly coming from a request):
const raw = {
  id: 0,                // invalid: below min
  title: 'Hello World',
  content: '<p>Welcome to my blog</p>',
  tags: ['js', 'schema'],
  publishedAt: '2021-07-15',
};

try {
  const post = postSchema(raw);
  console.log('Valid post:', post);
} catch (err) {
  console.error('Validation failed:', err.message);
}

Using parse to return NaN on error instead of throwing:

import s from 'libtuple-schema';

const result = s.parse(postSchema, raw, err => console.error(err));
if (Number.isNaN(result)) {
  // handle invalid payload
} else {
  console.log('Parsed post:', result);
}

You can create schemas for Tuples, Groups, Records, and Dicts:

import s from 'libtuple-schema';

const pointSchema = s.tuple(
    s.number(),
    s.number(),
);

const pointTuple = pointSchema([5, 10]);

console.log(pointTuple);

const userSchema = s.record({
    id: s.number(),
    email: s.string(),
});

const userRecord = userSchema({id: 1, email: '[email protected]'});

console.log(userRecord);

Schema.parse(schema, values[, onError])

Schema.parse() will return the parsed value, or NaN on error, since NaN is falsey and NaN !== NaN.

The optional onError callback will be invoked with the Error before returning NaN.

import s from 'libtuple-schema';

const boolSchema = s.boolean();

s.parse(boolSchema, true);  // returns true
s.parse(boolSchema, false); // returns false
s.parse(boolSchema, 123);   // returns NaN

SchemaMappers

SchemaMappers for Values

Schema.value(options)

Validate an arbitrary value.

  • options.map - Callback to transform the value after its been validated.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.literal(options)

Validate a literal value. Must be strictly equal.

  • options.value - The value to check.

Schema.overwrite(options)

Overwrite a value.

  • options.value - The value to return.
  • options.map - Callback to transform the value.

Schema.drop()

Drop the value (always maps to undefined).

Schema.boolean(options)

Validate a boolean.

  • options.optional - Is this value optional?
  • options.default - If the value is optional & the value is undefined, use this value.
  • options.nullable - Is this value nullable?
  • options.map - Callback to transform the value after it has been validated.

Schema.number(options)

  • options.min - Min value
  • options.max - Max value
  • options.map - Callback to transform the value after its been validated.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.bigint(options)

  • options.min - Min value
  • options.max - Max value
  • options.map - Callback to transform the value after its been validated.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.string(options)

  • options.min - Min length
  • options.max - Max length
  • options.map - Callback to transform the value after it has been validated.
  • options.match - Throw a TypeError if this does not match
  • options.noMatch - Throw a TypeError if this does match
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined, use this value.
  • options.prefix - Throw a TypeError if the string does not start with this prefix.
  • options.suffix - Throw a TypeError if the string does not end with this suffix.
  • options.infix - Throw a TypeError if the string does not contain this infix.

Schema.array(options)

  • options.min - Min length
  • options.max - Max length
  • options.map - Callback to transform the value after its been validated.
  • options.each - Callback to transform each element.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.object(options)

  • options.class - Throw a TypeError if the class does not match.
  • options.map - Callback to transform the value after its been validated.
  • options.each - Callback to transform each element.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.function(options)

  • options.map - Callback to transform the value after its been validated.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.symbol(options)

  • options.map - Callback to transform the value after its been validated.
  • options.check - Throw a TypeError if this returns false.
  • options.nullable - Is this value nullable?
  • options.optional - Is this value optional?
  • options.default - If the value is optional & undefined or missing, use this value.

Schema.null(options)

  • options.optional - Is this value optional?
  • options.default - If the value is optional & the value is undefined, use this value.
  • options.map - Callback to transform the value after it has been validated.

Schema.undefined(options)

  • options.optional - Is this value optional?
  • options.default - If the value is optional & the value is undefined, use this value.
  • options.nullable - Is this value nullable?
  • options.map - Callback to transform the value after it has been validated.

Schema Mappers for Convenience

Convenience methods for numbers

The following methods will call s.number with additional constraints added:

  • s.integer
  • s.float
  • s.NaN
  • s.infinity

Convenience methods for strings

The following methods will call s.string with additional constraints added:

  • s.numericString
    // options.min & options.max are overridden for numeric comparison.
    const positive = s.numericString({map: Number, min: Number.EPSILON});
    const negative = s.numericString({map: Number, max: -Number.EPSILON});
    
    negative('-100'); // -100
    positive('100');  //  100
    
    negative('5');    // ERROR
    positive('-5');   // ERROR
  • s.dateString
    // options.min & options.max are overridden for comparison with Date objects.
    const after1994 = s.dateString({min: new Date('01/01/1995')});
    
    after1994('07/04/1995'); // '07/04/1995'
    after1994('07/04/1989'); // ERROR
  • s.uuidString
    const uuidSchema = s.uuidString();
    uuidSchema('0ff5d941-f46a-4f4a-aec8-1d1ec117e2a3'); // '0ff5d941-f46a-4f4a-aec8-1d1ec117e2a3'
    uuidSchema('0ff5d941'); // ERROR
  • s.urlString
    const urlSchema = s.urlString();
    urlSchema('https://example.com'); // 'https://example.com'
    urlSchema('not a url');           // ERROR
  • s.emailString
    const emailSchema = s.emailString();
    emailSchema('[email protected]'); // '[email protected]'
    emailSchema('not an email');       // ERROR
  • s.regexString
    const regexSchema = s.regexString();
    regexSchema('.+?'); // '.+?'
    regexSchema('+++'); // ERROR
  • s.base64String
    const base64Schema = s.base64String();
    base64Schema('RXhhbXBsZSBzdHJpbmc='); // 'RXhhbXBsZSBzdHJpbmc='
    base64Schema('notbase64');            // ERROR;
  • s.jsonString
    const jsonSchema = s.jsonString();
    jsonSchema('[0, 1, 2]'); // '[0, 1, 2]';
    jsonSchema('not json');  // ERROR;

Special Schema Mappers

Schema.and(...schemaMappers)

Run the value through each SchemaMapper in sequence. Passes only if all mappers succeed, returning the last mapped value.

import s from 'libtuple-schema';

const schema = s.and(
  s.string(),
  s.oneOf(['foo', 'bar', 'baz'])
);

console.log(s.parse(schema, 'foo')); // 'foo'
s.parse(schema, 'XYZ');              // NaN (fails to match oneOf clause)

Schema.or(...schemaMappers)

Map the value with the first matching SchemaMapper

import s from 'libtuple-schema';

const dateSchema = s.or(
    s.string({match: /\d\d \w+ \d\d\d\d \d\d:\d\d:\d\d \w+?/, map: s => new Date(s)}),
    s.object({class: Date})
);

console.log( dateSchema('04 Apr 1995 00:12:00 GMT') );
console.log( dateSchema(new Date) );

Schema.not(schemaMapper)

Invert a SchemaMapper: succeeds only if the mapper throws, returning the original value.

import s from 'libtuple-schema';

const schema = s.not(
  s.string({match: /^foo/})
);

console.log(s.parse(schema, 'bar')); // 'bar'
s.parse(schema, 'foobar');           // NaN (starts with 'foo')

Schema.repeat(r, schemaMapper)

Repeat a SchemaMapper n times

import s from 'libtuple-schema';

const pointSchema = s.tuple(...s.repeat(2, s.number()));

const point = pointSchema([5, 10]);

Schema.oneOf(literals = [], options = {})

Match the value to a set of literals with strict-equals comparison.

  • options.map - Callback to transform the value after it has been validated.
import s from 'libtuple-schema';

const schema = s.oneOf(['something', 1234]);

s.parse(schema, 1234);          // 1234
s.parse(schema, 'something');   // 'something'
s.parse(schema, 'not on list'); // ERROR!

Schema.asyncVal(schemaMapper)

Convert a SchemaMapper so it accepts a Promise as input and awaits it.

import s from 'libtuple-schema';

const schema = s.asyncVal(s.number());

(async () => {
  console.log(await schema(Promise.resolve(123))); // 123
})();

Schema Mappers for Tuples, Groups, Records and Dicts

Schema.tuple(...values)

Map one or more values to a Tuple.

import s from 'libtuple-schema';

const pointSchema = s.tuple(s.number(), s.number());

const point = pointSchema([5, 10]);

Schema.group(...values)

Map one or more values to a Group.

Schema.record(properties)

Map one or more properties to a Record.

import s from 'libtuple-schema';

const companySchema = s.record({
    name: s.string(),
    phone: s.string(),
    address: s.string(),
});

const company = companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
});

console.log({company});

Schema.dict(properties)

Map one or more values to a Dict.

import s from 'libtuple-schema';

const companySchema = s.dict({
    name: s.string(),
    phone: s.string(),
    address: s.string(),
});

const company = companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
});

console.log({company});

Schema.nTuple(...values)

Map n values to a Tuple. Will append each value in the input to the Tuple using the same mapper.

import s from 'libtuple-schema';

const vectorSchema = s.nTuple(s.number());

const vec2 = vectorSchema([5, 10]);
const vec3 = vectorSchema([5, 10, 11]);
const vec4 = vectorSchema([5, 10, 11, 17]);

console.log({vec2, vec3, vec4});

Schema.nGroup(...values)

Map n values to a Group. Will append each value in the input to the Group using the same mapper.

Schema.nRecord(properties)

Map an array of objects to a Tuple of Records. If passed a single object, it is coerced into a one-element tuple.

import s from 'libtuple-schema';

const usersSchema = s.nRecord({
  id:   s.number(),
  name: s.string(),
});

const users = usersSchema([
  { id: 1, name: 'Alice', extra: 1234 },
  { id: 2, name: 'Bob' }
]);

console.log(users);
// Tuple(
//   Record({id:1, name:'Alice', extra: 1234}),
//   Record({id:2, name:'Bob'})
// )

Schema.nDict(properties)

Map an array of objects to a Tuple of Dicts. If passed a single object, it is coerced into a one-element tuple.

import s from 'libtuple-schema';

const configsSchema = s.nDict({
  mode: s.string(),
  flag: s.boolean(),
});

const configs = configsSchema([
  { mode: 'dev', flag: true, extra: 'ignored' },
  { mode: 'prod', flag: false }
]);

console.log(configs);
// Tuple(
//   Dict({mode:'dev', flag:true, extra:'ignored'}),
//   Dict({mode:'prod', flag:false})
// )

Schema.sTuple(...values)

Strictly map values to a Tuple. Will throw an error if the number of values does not match.

import s from 'libtuple-schema';

const pointSchema = s.sTuple(s.number(), s.number());

const pointA = pointSchema([5, 10]);
const pointB = pointSchema([5, 10, 1]); // ERROR!

Schema.sGroup(...values)

Strictly map values to a Group. Will throw an error if the number of values does not match.

Schema.sRecord(properties)

Strictly map values to a Record. Will throw an error if the number of values does not match.

import s from 'libtuple-schema';

const companySchema = s.sRecord({
    name: s.string(),
    phone: s.string(),
    address: s.string(),
});

const company = companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
});

// ERROR!
companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
    openHours: '9AM-7PM',
    slogan: 'We do business.',
});

Schema.sDict(properties)

Strictly map values to a Dict. Will throw an error if the number of values does not match.

Schema.xTuple(...values)

Exclusively map values to a Tuple. Will drop any keys (indexes) not present in the schema.

import s from 'libtuple-schema';

const pointSchema = s.xTuple(s.number(), s.number());

const pointA = pointSchema([5, 10]); // [5, 10]
const pointB = pointSchema([5, 10, 1]); // Also [5, 10]

console.log(pointB[0]); // 5
console.log(pointB[1]); // 10
console.log(pointB[2]); // undefined

Schema.xGroup(...values)

Exclusively map values to a Group. Will drop any keys (indexes) not present in the schema.

Schema.xRecord(properties)

Exclusively map values to a Record. Will drop any keys not present in the schema.

const companySchema = s.xRecord({
    name: s.string(),
    phone: s.string(),
    address: s.string(),
});

const company = companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
    openHours: '9AM-7PM',
    slogan: 'We do business.',
});

console.log({company});

Schema.xDict(properties)

Exclusively map values to a Dict. Will drop any keys not present in the schema.

const companySchema = s.xDict({
    name: s.string(),
    phone: s.string(),
    address: s.string(),
});

const company = companySchema({
    name: 'Acme Corporation',
    phone: '+1-000-555-1234',
    address: '123 Fake St, Anytown, USA',
    openHours: '9AM-7PM',
    slogan: 'We do business.',
});

console.log({company});