
Research
Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.
libtuple-schema
Advanced tools
$ 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...
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: 'fake@example.com'});
console.log(userRecord);
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
Validate an arbitrary value.
Validate a literal value. Must be strictly equal.
Overwrite a value.
Drop the value (always maps to undefined).
Validate a boolean.
undefined, use this value.undefined, use this value.undefined, use this value.undefined, use this value.The following methods will call s.number with additional constraints added:
The following methods will call s.string with additional constraints added:
// 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
// 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
const uuidSchema = s.uuidString();
uuidSchema('0ff5d941-f46a-4f4a-aec8-1d1ec117e2a3'); // '0ff5d941-f46a-4f4a-aec8-1d1ec117e2a3'
uuidSchema('0ff5d941'); // ERROR
const urlSchema = s.urlString();
urlSchema('https://example.com'); // 'https://example.com'
urlSchema('not a url'); // ERROR
const emailSchema = s.emailString();
emailSchema('person@example.com'); // 'person@example.com'
emailSchema('not an email'); // ERROR
const regexSchema = s.regexString();
regexSchema('.+?'); // '.+?'
regexSchema('+++'); // ERROR
const base64Schema = s.base64String();
base64Schema('RXhhbXBsZSBzdHJpbmc='); // 'RXhhbXBsZSBzdHJpbmc='
base64Schema('notbase64'); // ERROR;
const jsonSchema = s.jsonString();
jsonSchema('[0, 1, 2]'); // '[0, 1, 2]';
jsonSchema('not json'); // ERROR;
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)
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) );
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')
Repeat a SchemaMapper n times
import s from 'libtuple-schema';
const pointSchema = s.tuple(...s.repeat(2, s.number()));
const point = pointSchema([5, 10]);
Match the value to a set of literals with strict-equals comparison.
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!
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
})();
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]);
Map one or more values to a Group.
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});
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});
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});
Map n values to a Group. Will append each value in the input to the Group using the same mapper.
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'})
// )
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})
// )
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!
Strictly map values to a Group. Will throw an error if the number of values does not match.
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.',
});
Strictly map values to a Dict. Will throw an error if the number of values does not match.
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
Exclusively map values to a Group. Will drop any keys (indexes) not present in the schema.
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});
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});
FAQs
Schemas for libtuple
We found that libtuple-schema demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.