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 🙏

© 2024 – Pkg Stats / Ryan Hefner

ensurism

v0.7.0

Published

A validation and conditional assignment utility belt

Downloads

26

Readme

ensurism

Version Types License

A validation and conditional assignment utility belt.

Install

npm install ensurism

Contents

Use Cases

Validation

The most general util for validation is ensure, wich uses JSON Schema to validate your data. However, be sure to take a look at the remaining utils to find the best fits for your use cases.

Environment Variables

import process from 'node:process';

import { collect, ensure } from 'ensurism';

// Will always be 'development', 'production', or 'test'
const NODE_ENV = ensure(process.env.NODE_ENV, {
  type: 'string',
  enum: ['development', 'production', 'test'],
  default: 'development'
});

// Collecting -mapping- properties
const env = collect(process.env, ({ get, assert, ensure, coerce }) => ({
  FOO_VAR: get(),
  BAR_VAR: assert(),
  APP_URI: ensure({ type: 'string', format: 'uri' }),
  APP_PORT: coerce('number'),
  NODE_ENV: ensure({
    type: 'string',
    enum: ['development', 'production', 'test'],
    default: 'development'
  })
}));

Global Configuration

You might want to create a configuration object that relies on certain values. In the example below, we collect environment variables, and later use select to conditionally set the properties bar and baz for our final configuration object.

import process from 'node:process';

import { into } from 'pipettes';
import { collect, select } from 'ensurism';

export const configuration = into(
  collect(process.env, ({ ensure }) => ({
    NODE_ENV: ensure({
      type: 'string',
      enum: ['development', 'production', 'test'],
      default: 'development'
    })
  })),
  ({ NODE_ENV }) => ({
    env: NODE_ENV,
    foo: 'foo',
    bar: select(NODE_ENV, {
      default: 1,
      production: 2,
      development: 3
    }),
    baz: select(NODE_ENV, {
      default: 1,
      production: 2,
      test: 4
    })
  })
);

Utils

assert

Throws if data is undefined.

  • Signature: assert(data, options)
  • Params:
    • data: the input data.
    • options: optional object with optional properties:
      • name: a name to be used for error messages.
      • message: an error message to use instead of the default.
      • deep: if true and data is an array or a record, assert will also throw if any of their elements are undefined.
  • Returns: the input data.
import { assert } from 'ensurism';

// Succeed
assert('foo');
assert([undefined]);

// Fail
assert(undefined);
assert([undefined], { deep: true });

take

Returns the input data for records and basic types; for arrays, however, one of multiple strategies can be applied:

  • Signature: take(data, options)
  • Params:
    • data: the input data.
    • options: optional object with optional properties:
      • name: a name to be used for error messages.
      • assert: whether to assert the final output value is not undefined.
      • strategy: applies to data arrays; one of:
        • "first": it will return the first array item, if any. This is the default strategy.
        • "one": it will throw if the input array has more than one element, and return its first item, if any.
        • "maybe": it will return the first defined array item, if any.
  • Returns: the input data if it is a record or a basic type; if data is an array, it will return a data array value as per the specified strategy.
import { take } from 'ensurism';

// Succeed
take(['foo'], { strategy: 'one' }); // 'foo'
take(['foo', 'bar'], { strategy: 'first' }); // 'foo'
take([undefined, 'foo'], { strategy: 'maybe' }); // 'foo'

// Fail
take(['foo', 'bar'], { strategy: 'one' });
take([undefined], { assert: true, strategy: 'first' });

ensure

Throws if data doesn't conform to a given schema. If the schema has default values, they will be assigned to the returned data. Additional properties, when allowed by the schemas, will not be removed. No mutation will occurr for input data.

Uses JSON Schema validation, as implemented by ajv. Further string format validation is provided by ajv-formats and ajv-formats-draft2019.

  • Signature: ensure(data, schema, options)
  • Params:
    • data: the input data.
    • schema: either a JSON Schema object with a type property, or a valid schema type, as a string.
    • options: optional object with optional properties:
      • name: a name to be used for error messages; if empty, schema.title will be used instead when available.
      • assert: whether to assert the final output value is not undefined.
  • Returns: a clone of the input data with, if it applies, the default values assigned as specified by schema.
import { ensure } from 'ensurism';

// Succeed
ensure('foo', 'string'); // 'foo'
ensure(undefined, 'string'); // undefined
ensure(undefined, { type: 'string', default: 'foo' }, { assert: true }); // 'foo'

// Fail
ensure('foo', 'number');
ensure(undefined, 'string', { assert: true });

coerce

Coerces data to the root schema type, then validates the data against the schema by using ensure. Inner elements for objects and arrays will not be coerced.

  • Signature: coerce(data, schema, options)
  • Params:
    • data: the input data; undefined won't be coerced into the schema type.
    • schema: either a JSON Schema object with a type property, or a valid schema type, as a string.
    • options: optional object with optional properties:
      • name: a name to be used for error messages; if empty, schema.title will be used instead when available.
      • assert: whether to assert the final output value is not undefined.
  • Returns: a clone of the input data with, if it applies, the default values assigned as specified by schema.

The coercion rules from each data input type are as follows:

  • strings:
    • "string": returns the input string with quotes (") removed, if within quotes.
    • "number", "integer": fails if NaN.
    • "boolean": false for falsy value strings:
      • ""
      • "\"\""
      • "0"
      • "false"
      • "null"
      • "undefined"
      • "NaN"
    • "null": fails if not a falsy value string.
    • "array", "object": parses with JSON.parse; fails if not a JSON string.
  • numbers:
    • "string": a number string.
    • "number", "integer": a number.
    • "boolean": false for 0, true otherwise.
    • "null": fails if not 0.
    • "array", "object": it will fail.
  • boolean:
    • "string": "true" or "false".
    • "number", "integer": 0 for false; 1 for true.
    • "boolean": same as data source.
    • "null": fails if not false.
    • "array", "object": it will fail.
  • null:
    • "string": "null".
    • "number", "integer": 0.
    • "boolean": false.
    • "null": null.
    • "array", "object": it will fail.
  • array:
    • "string", "number", "integer", "boolean", "null": it will fail.
    • "array": same as data source.
    • "object": an object with index numbers as keys.
  • object:
    • "string", "number", "integer", "boolean", "null": it will fail.
    • "array": an array of object values.
    • "object": same as data source.
import { coerce } from 'ensurism';

// These will succeed
coerce('foo', 'string'); // 'foo'
coerce('"foo"', 'string'); // 'foo'
coerce('0', 'number'); // 0
coerce('foo', 'boolean'); // true
coerce('null', 'boolean'); // false
coerce('false', 'null'); // null
coerce('{ "foo": "bar" }', 'object'); // { foo: 'bar' }
coerce(10, 'boolean'); // true
coerce(0, 'null'); // null


// These will fail
coerce('foo', 'number');
coerce('foo', 'null');
coerce('1, 2, 3', 'array');
coerce(1, 'null');

select

Given a value and a selector object, it will return the value of the selector's property matching that value. If selector doesn't have such property but it does have a default key, it will return its value instead.

If a strategy other than "fallback" is specified, the selected value -if any- will be merged with the default value -if any- following the specified strategy.

  • Signature: select(data, selector, options)
  • Params:
    • data: property to select from selector.
    • selector: and object of values.
    • options: optional object with optional properties:
      • name: a name to be used for error messages.
      • assert: whether to assert the final output value is not undefined.
      • strategy: applied when there's both a selector.default and data for value -see merge strategies; one of:
        • "fallback": returns the data for selector[value] if available, otherwise returns selector.default. This is the default strategy.
        • "shallow": produces a shallow merge clone of the data.
        • "merge": produces a deep merge clone of the data, excluding arrays.
        • "deep": produces a deep merge clone of the data, including arrays.
import { select } from 'ensurism';

// These will succeed
select('foo', { foo: 'bar', bar: 'baz' }); // 'bar'
select('foo', { default: 'bar', bar: 'baz' }); // 'bar'
select('foo', { default: { bar: 'bar' }, foo: { baz: 'baz' } }, { strategy: 'shallow' }); // { bar: 'bar', baz: 'baz' }

// These will fail
select('foo', { bar: 'baz' }, { assert: true });

collect

A conveniency function for when you want to use any of the previous utils for several keys in an object, and expect the response to contain similarly named properties.

  • Signature:collect(data, collection, options)
  • Params:
    • data: an object of values.
    • collector: a function, taking an object with methods get, assert, take, ensure, coerce, and select as an argument. These methods have the same signature as the similarly named functions but omitting their first data argument.
    • options: optional object with optional properties:
      • failEarly: boolean, whether to fail as soon as one of the values throw, otherwise compacting all errors in a single error. Default: false.
  • Returns: an object as defined by collector.
import { collect } from 'ensurism';

const result = collect(
  {
    a: 'foo',
    b: 'bar',
    c: ['baz', 'foobar'],
    d: 'foobaz',
    e: 'barfoo',
    f: 'barbaz'
  },
  ({ get, assert, take, ensure, coerce, select }) => ({
    a: get(),
    b: assert(),
    c: take('first'),
    d: ensure('string'),
    e: coerce('boolean'),
    f: select({
      default: true,
      barbaz: false
    })
  })
);

// `result` would be an object such as:
({
  a: 'foo',
  b: 'bar',
  c: 'baz',
  d: 'foobaz',
  e: true,
  f: false
});