SkillAgentSearch skills...

Not

A minimal, blazing fast, TypeScript runtime type-checker. It is intuitive, API-centric, and customisable. A must-have for API payload sanitiser, type-checking, validation, error handing and messaging helper -- all in a small and neat pack.

Install / Use

/learn @calvintwr/Not
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Not.Js - "All-in-one" type checking, validation, error handling and messaging. npm version Build Status Coverage Status license install size

Not is the minimal and blazingly fast "implement-and-forget" runtime type-checking library written in TypeScript for instant API payload checking and sanitisation, with ready-to-use error response messages to your API requestors -- all in a small and neat pack.

import Not from 'you-are-not' // ES import syntax
const Not = require('you-are-not') // CJS require syntax

let schema    = { id: "number" } // endpoint only expects param "id"
let malicious = { id: 1, role: "admin" } //payload with malicious "role: admin"

let sanitised = Not.scrub(
    "objectName",
    schema
    payload
)
console.log(sanitised)
// outputs:
// { id: 1 }

role: "admin" is removed. Payload sanitised.

Why Not?

Not gives actionable error messages, so you know exactly what has gone wrong with your inputs/arguments/API. Use the messages directly as API replies. Build friendly APIs. Meet project deadlines.

Not.TS - "All-in-one" type checking, validation, error handling and messaging.

This module has no dependencies.

Installation

npm install --save you-are-not
import Not from 'you-are-not'

Simple Usage

1. For API input type-checking, validation, sanitisation and error messaging:

User makes a request with the following payload:

const payload = {
    id: 1,
    name: 2 // error made by requestor
}

API receiving payload defines a schema, followed by scrubbing the payload:

const schema = {
    id: 'number',
    name: 'string' // note that name is expected to be in `string`
}

let sanitised = Not.scrub(
    'payloadWithTypeError', // give your payload a name
    schema,
    payload,
    { exact: true } // use exact: true if you need the payload to match the schema 100%, else, additional properties will be removed without throwing errors.
)

Not throws an actionable error message ready for sending back to the requestor:

TypeError (NotTS): Wrong types provided. See `trace`.
    ... stack trace ...
{
  statusCode: 400,
  trace: [
    'Wrong Type (payloadWithTypeError.id): Expecting type `number` but got `string` with value of `1`.'
  ]
}

If you are using express or fastify, thrown errors can be seamlessly used for production:

//express
res.status(sanitised.statusCode)
res.send({
    message: `You have provided erroneous inputs. \n\nMore info:\n${sanitised.trace.join('\n')}`
})

//fastify
reply.code(sanitised.statusCode)
reply.send({
    message: `You have provided erroneous inputs. \n\nMore info:\n${sanitised.trace.join('\n')}`
})

This will produce a 400 error with the following message property in response body:

You have provided erroneous inputs.

More info:
Wrong Type (payloadWithTypeError.id): Expecting type `number` but got `string` with value of `1`.

Suppose additional properties are provided in possibly malicious payloads, they can be sanitised:

let payloadWithMaliciousPayload = {
    id: 1,
    name: "foo",
    role: "admin" // simulating malicious payload. this will be sanitised
}

var sanitised = Not.scrub(
    'payloadWithMaliciousPayload',
    schema,
    payloadWithTypeError
)

console.log(sanitised)
// outputs:
// {
//     id: 1,
//     name: "foo"
// }

role: "admin" is removed. Payload sanitised.

2. Lightweight type-checking

Besides being a payload sanitiser, Not is a type-checker under-the-hood.

import NotProto from 'you-are-not'
const Not = Not.create() // this creates another instance of Not
const not = Not.createNot() // this exposes a simplified #not with no overloads
const is = Not.createIs()
const notNerfed = Not.create({ throw: false }) // creates an instance that will not throw errors.

Use Not to cut down runtime type-checking verbiage. Instead of:

if (typeof foo !== 'string' ||
    typeof foo !== 'number' ||
    (typeof foo === 'number' && !isNaN(foo)) ||
    !Array.isArray(foo)
) {  throw Error("Not valid, but I don't know why.") }

You write:

not(['string', 'number', 'array'], foo)
// or
is(['string', 'number', 'array'], foo)

// code will reach here if the above don't error
startMyFunction()

When Not fails, it throws an error by default. You can pass throw: false to prevent throwing errors and handle them yourself:

const not = Not.createNot({ throw: false })
// instead of throwing, `not` will return string

let input  = ['a', 'sentence']
let result = not('string', input) // returns a string, which can evaluate `true`

if (result) input = input.join(' ')
// so you can do your own error handling, or transformation

// code below can safely use `input` as string :)
input.toLowerCase()

Full Usage

1. Valid types

The valid types you can check for are:

Primitives:
'string'
'number'
'array'
'object'
'function'
'boolean'
'null'
'undefined'
'symbol'
'nan' // this is an opinion. NaN should not be of type number in the literal sense.

Aggregated:
'optional' // which means 'null' and 'undefined'

Other custom types:
'integer'

2. #scrub/#checkObject

#checkObject is #scrub under the hood. Use #scrub for simplified usage (example above), and #checkObject when you want more control.

Not.scrub(objectName, schema, payload, options)

Not.checkObject(objectName, schema, payload, callback/options)

objectName: (string) Name of object.

schema: (object) An object depicting your schema.

payload: (object) The payload to check for.

options (#scrub): (object | optional). Define exact: true if you want to throw an error if there are additional properties.

callback/options (#checkObject): (object | optional). See example below:

// callback
Not.checkObject(objectName, schema, payload, (errors, payload) => { /* handle errors yourself*/ })

// options
Not.checkObject(objectName, schema, payload, {
    callback: (errors, payload) => { /* handle errors yourself*/ },
    returnPayload: true/false, // define if you need the payload returned. if not requires, switch to false for better performance
    exact: true/false // if true, will throw errors if there are additiona properties
})

Defining Schema

// you can use optional notations like this:
"info?": {
    gender: 'string',
    "age?": 'number'
}
//is same as
info__optional: {
    gender: 'string',
    age__optional: 'number'
}
//is same as
info__optional: {
    gender: 'string',
    age: ['number', 'optional']
}

Check for multiple type by passing an array:

info: {
    age: ['number', 'string'], // age can be of type number or string
    email: ['email'] // suppose you have created your own email validation checking. To create your own types, check examples below.
}

#checkObject advanced usage

  1. If callback/options is a callback function, it will run the callback:
Not.checkObject(name, schema, payload, function(errors) {
    // do something with errors.
})

(Note: When callback is provided, Not assumes you want to handle things yourself, and will not throw errors regardless of the throw flag.)

  1. If callback/options is { returnPayload: true }, #checkObject returns (a) the sanitised payload (object) when check passes, or (b) an array of errors if check fails:
let sanitised = Not.checkObject(
    name,
    schema,
    payload,
    { returnPayload: true }
)
if (Array.isArray(sanitised) {
    // do something with the errors
    return
}
// or continue using the sanitised payload.
DB.find(sanitised)
  1. If callback/options is { callback: function() {}, returnPayload: true }:
let callback = function(errors, payload) {
    if(errors.length > 0) {
        // do something with the errors
        return
    }

    DB.find(payload)
}

Not.checkObject(
    name,
    schema,
    payload,
    {
        returnPayload: true,
        callback: callback
    }
)

3. Not as simple type checker

You can also check for multiple types by passing an array. This is useful when you want your API to accept both string and number:

let not = Not.create()
let id        = "123"
let anotherId = 123
let emailOptional = undefined

not(['string', 'number'], id)
not(['string', 'number'], anotherId)
not(['optional', 'string'], emailOptional)

// code reaches this point when all checks passed

4. Methods Available

The Not prototype has the following methods available:

Not.scrub(objectName, schema, payload)
Not.checkObject(objectName, schema, payload, options)

Not.not(expect, got, name, note)
Not.is(expect, got, name, note)

Not.lodge(expect, got, name, note)
Not.resolve([callback]) // this is used with #lodge.

Not.defineType(options)

5. Methods: #not and #is

Not.not(expect, got, name, note)
Not.is(expect, got, name, note)

expect: (string or array of strings) The types to check for (see below on "3. Types to check for".)

got: (any) This is the the subject/candidate/payload you are checking.

name: (string | optional) You can define a name of the subject/candidate/payload, which will be included in the error message.

note: (string |

View on GitHub
GitHub Stars75
CategoryDevelopment
Updated1y ago
Forks2

Languages

JavaScript

Security Score

65/100

Audited on Jul 23, 2024

No findings