Socket
Socket
Sign inDemoInstall

rttc

Package Overview
Dependencies
Maintainers
4
Versions
108
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rttc - npm Package Compare versions

Comparing version 7.0.1 to 7.1.0

lib/helpers/build-two-headed-schema-cursor.js

2

index.js

@@ -19,2 +19,4 @@ module.exports = {

reify: require('./lib/reify'),
intersection: require('./lib/intersection'),
union: require('./lib/union'),
};

@@ -21,0 +23,0 @@

55

lib/helpers/types.js

@@ -18,3 +18,3 @@ /**

var type = {
var TYPES = {

@@ -89,2 +89,5 @@

return '';
},
isExemplar: function (eg){
return TYPES.str.is(eg) && !TYPES.lamda.isExemplar(eg) && !TYPES.json.isExemplar(eg) && !TYPES.ref.isExemplar(eg);
}

@@ -126,5 +129,5 @@ },

if(type.number.is(v)) return v;
if(type.boolean.is(v)) return v ? 1 : 0;
if(type.string.is(v)) {
if(TYPES.number.is(v)) return v;
if(TYPES.boolean.is(v)) return v ? 1 : 0;
if(TYPES.string.is(v)) {

@@ -166,2 +169,5 @@ // Is this a string that appears to be a number?

return 0;
},
isExemplar: function (eg){
return TYPES.number.is(eg);
}

@@ -210,2 +216,5 @@ },

return false;
},
isExemplar: function (eg){
return TYPES.boolean.is(eg);
}

@@ -239,3 +248,3 @@ },

if (!_.isObject(v)) return false;
if (type.arr.is(v)) return false;
if (TYPES.array.is(v)) return false;
if (!_.isPlainObject(v)) return false;

@@ -286,2 +295,5 @@ // Reject readable streams

return {};
},
isExemplar: function (eg){
return TYPES.dictionary.is(eg);
}

@@ -320,2 +332,5 @@ },

return [];
},
isExemplar: function (eg){
return TYPES.array.is(eg);
}

@@ -364,2 +379,5 @@ },

return undefined;
},
isExemplar: function (eg){
return eg === '===' || eg === undefined;
}

@@ -437,2 +455,5 @@ },

return null;
},
isExemplar: function (eg){
return eg === '*';
}

@@ -480,2 +501,5 @@ },

return function () { throw new Error('Not implemented! (this function was automatically created by `rttc`'); };
},
isExemplar: function (eg){
return _.isString(eg) && eg.match(/(^-+>$)|(^=+>$)|(^<=+$)|(^<-+$)/);
}

@@ -493,13 +517,18 @@ },

// Aliases for backwards compat.
type.str = type.email = type.url = type.string;
type.bool = type.boolean;
type.arr = type.array;
type.integer = type.int;
type.float = type.number;
type.object = type.obj = type.dictionary;
// Abbreviation aliases for backwards compat.
TYPES.str = TYPES.string;
TYPES.num = TYPES.number;
TYPES.bool = TYPES.boolean;
TYPES.arr = TYPES.array;
TYPES.dict = TYPES.dictionary;
module.exports = type;
// (Mi)spelling aliases
TYPES.lambda = TYPES.lambda;
// Case-folding aliases
TYPES.JSON = TYPES.json;
module.exports = TYPES;

@@ -11,5 +11,5 @@ /**

/**
* Given a primitive value, return its type.
* Given a primitive exemplar, return its type.
* ________________________________________________________________________________
* @param {*} val there's that "mystery meat" again
* @param {*} eg there's that "mystery meat" again
* ________________________________________________________________________________

@@ -19,6 +19,6 @@ * @returns {String}

function getTypeOfPrimitive(val) {
function getTypeOfPrimitive(eg) {
// Check for `type: 'ref'` (===)
if (val === '===' || val === undefined) {
if (types.ref.isExemplar(eg)) {
return 'ref';

@@ -28,3 +28,3 @@ }

// Check for `type: 'lamda'` (->)
if (_.isString(val) && val.match(/(^-+>$)|(^=+>$)|(^<=+$)|(^<-+$)/)) {
if (types.lamda.isExemplar(eg)) {
return 'lamda';

@@ -34,3 +34,3 @@ }

// Check for `type: 'json'` (*)
if (val === '*') {
if (types.json.isExemplar(eg)){
return 'json';

@@ -40,3 +40,3 @@ }

// Check for string
if(types.str.is(val)) {
if (types.string.isExemplar(eg)) {
return 'string';

@@ -46,3 +46,3 @@ }

// Check for number
if(types.number.is(val)) {
if (types.number.isExemplar(eg)) {
return 'number';

@@ -52,3 +52,3 @@ }

// Check for boolean
if(types.bool.is(val)) {
if (types.boolean.isExemplar(eg)) {
return 'boolean';

@@ -63,3 +63,3 @@ }

/**
* Recursively create a new schema object from an example object.
* Recursively create a new type schema dictionary from an exemplar.
* ________________________________________________________________________________

@@ -73,3 +73,3 @@ * @param {Object} obj

function getSchemaOfObject(obj) {
if(!types.obj.is(obj)) return;
if(!types.dictionary.is(obj)) return;

@@ -81,6 +81,6 @@ var newObj = {};

var type;
if(types.arr.is(val)) {
type = getSchema(val);
if(types.array.is(val)) {
type = infer(val);
}
else if(types.obj.is(val)) {
else if(types.dictionary.is(val)) {
type = getSchemaOfObject(val);

@@ -101,22 +101,22 @@ }

/**
* Given an example, parse it to infer its schema.
* Given an exemplar, parse it to infer its type schema.
* ________________________________________________________________________________
* @param {*} example
* @param {*} eg
* ________________________________________________________________________________
* @returns {*} a schema object
* @returns {*} a type schema
*/
function getSchema(example) {
function infer(eg) {
// If the example isn't an object or array, we will derive its primitive type.
if(!types.obj.is(example) && !types.arr.is(example)) {
return getTypeOfPrimitive(example);
// If the exemplar isn't an object or array, we will derive its primitive type.
if(!types.dictionary.is(eg) && !types.array.is(eg)) {
return getTypeOfPrimitive(eg);
}
// If the example is an array, figure out what to do.
// If the exemplar is an array, figure out what to do.
// For now just check that it's an array.
if(types.arr.is(example)) {
if(types.array.is(eg)) {
// Ensure empty arrays are not recursively parsed.
if (example.length === 0) {
if (eg.length === 0) {
return [];

@@ -126,20 +126,20 @@ }

// Parse arrays of arrays
if (_.isArray(example[0])) {
return [getSchema(example[0])];
if (_.isArray(eg[0])) {
return [infer(eg[0])];
}
// Parse arrays of objects
if (_.isObject(example[0])) {
return [getSchemaOfObject(example[0])];
if (_.isObject(eg[0])) {
return [getSchemaOfObject(eg[0])];
}
// Parse arrays of primitives
return [getTypeOfPrimitive(example[0])];
return [getTypeOfPrimitive(eg[0])];
}
// Otherwise parse the object
return getSchemaOfObject(example);
return getSchemaOfObject(eg);
}
module.exports = getSchema;
module.exports = infer;
{
"name": "rttc",
"version": "7.0.1",
"version": "7.1.0",
"description": "Runtime type-checking for JavaScript.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -80,182 +80,60 @@ # rttc

> Note that all of the validation and coercion strategies used in this modules are recursive through the keys of plain old JavaScript objects and the indices of arrays.
Finally, note that all of the validation and coercion strategies used in this modules are recursive through the keys of plain old JavaScript objects and the indices of arrays.
## Types
## Usage
> Each type can be validated or coerced against. If coercion fails, the "base value" for the type will be used.
### Validation
> Also note that all types below may be expressed recursively within faceted dictionaries and patterned arrays. If those words don't make sense, keep reading, you'll see what I mean.
##### .validateStrict(expectedTypeSchema, actualValue)
Throws if the provided value is not the right type (recursive).
There are 10 different types recognized by `rttc`:
| type | rttc example notation | base value |
|-------------------------|--------------------------|-------------------------------------|
| string | `'any string like this'` | `''`
| number | `1337` _(any number)_ | `0`
| boolean | `false` _(or `true`)_ | `false`
| lamda | `function anyFunction(){ /* any function */ }` | `function () { throw new Error('Not implemented! (this function was automatically created by `rttc`'); };`
| generic dictionary | `{}` | `{}` _(empty dictionary)_
| generic array | `[]` | `[]` _(empty array)_
| json | `'*'` | `null`
| ref | `'==='` | `undefined`
| faceted dictionary (recursive) | `{...}` _(i.e. w/ keys)_ | `{...}` (w/ all expected keys and _their_ base values)
| pattern array (recursive) | `[...]` _(i.e. w/ 1 item)_ | `[]` _(empty array)_
##### .validate(expectedTypeSchema, actualValue)
Either returns a (potentially "lightly" coerced) version of the value that was accepted, or it throws. The "lightly" coerced value turns `"3"` into `3`, `"true"` into `true`, `-4.5` into `"-4.5"`, etc.
### Strings
`example: 'stuff'`
### Munging
The **string** type accepts any string.
### Numbers
##### .coerce(expectedTypeSchema, actualValue)
`example: 323`
ALWAYS returns an acceptable version of the value, even if it has to mangle it to get there (i.e. by using the "base value" for the expected type. More on that below.)
The **number** type accepts numbers like `0`, `-4`, or `235.3`. Anathemas like `Infinity`, `-Infinity`, `NaN`, and `-0` are all coerced to zero.
### Booleans
##### .hydrate(value, [_typeSchema_=`undefined`])
`example: false`
This function will use the provided `typeSchema` to figure out where "lamda" values (functions) are expected, then will use `eval()` to bring them back to life. Use with care.
The **boolean** type accepts `true` or `false`.
### Lamdas
##### .dehydrate(value, [_allowNull_=`false`])
`example: ->`
This takes care of a few serialization edge-cases, such as:
The **lamda** type accepts any function.
+ stringifies functions, regexps, and errors (grabs the `.stack` property)
+ replacing circular references with a string (e.g. `[Circular]`)
+ replaces `-Infinity`, `Infinity`, and `NaN` with 0
+ strips keys and array items with `undefined` or `null` values. If `allowNull` is set to true, `null` values will not be stripped from the encoded string.
### Generic dictionaries
##### .parse(stringifiedValue, [_typeSchema_=`undefined`], [_unsafeMode_=`false`])
Parse a stringified value back into a usable value.
This is basically just a variation on JSON.parse that calls `rttc.hydrate()` first if `unsafeMode` is enabled.
##### .stringify(value, [_allowNull_=`false`])
Encode a value into a string.
This is basically just a variation on JSON.stringify that calls `rttc.dehydrate()` first.
##### .parseHuman(stringFromHuman, [_typeSchema_=`undefined`], [_unsafeMode_=`false`])
Parse a string from a human into something usable. If provided, `typeSchema` will be used to make a better guess. If `unsafeMode` is enabled, lamda functions will be hydrated.
##### .stringifyHuman(value, typeSchema)
The inverse of `.parseHuman()`, this function encodes a string that, if run through `.parseHuman()` would result in the given value.
##### .reify(typeSchema)
Given a type schema, strip out generics ("ref", "json", {}, and []) to convert it into a strict type. In other words, this makes a type schema "strict", and the result of this function always passes `rttc.isStrictType()`.
### Assertions
##### .isEqual(firstValue, secondValue, [_expectedTypeSchema_=`undefined`])
Determine whether two values are equivalent using `_.isEqual()`, but also look for expected `lamda` values in the optional type schema and call `toString()` on functions before comparing them.
> This is the method used by `rttc`'s own tests to validate that expected values and actual values match.
##### .infer(exampleValue)
Guess the type schema from an example value.
##### .isStrictType(typeSchema, [recursive=false])
Determine whether the given type schema is "strict" (meaning it is a string, number, boolean, lamda, faceted dictionary, or patterned array). If second argument (`recursive`) is set to `true`, then also recursively check the subkeys of faceted dictionaries and patterns of arrays in the type schema.
| type | is strict? |
|-------------------------|---------------------|
| string | yes _(always)_ |
| number | yes _(always)_ |
| boolean | yes _(always)_ |
| lamda | yes _(always)_ |
| `{}` (generic) | no |
| `[]` (generic) | no |
| `{...}` (faceted) | yes _(maybe recursively)_ |
| `[...]` (patterned) | yes _(maybe recursively)_ |
| json | no |
| ref | no |
### Utilities
##### .sample(typeSchema, [n=2])
Given a type schema, return an array of up to `n` unique sample values that would validate against it (in random order). `n` defaults to 2 if left undefined.
##### .getDisplayType(value)
Given a value, return its type as a human-readable string (this is not limited to rttc types-- it can return strings like `"Error"` and `"Date"`)
##### .compile(value)
Given a value, return a human-readable string which represents it. This string is equivalent to a JavaScript code snippet which would accurately represent the value in code.
This is a lot like `util.inspect(val, false, null)`, but it also has special handling for Errors, Dates, RegExps, and Functions (using `dehydrate()` with `allowNull` enabled.) The biggest difference is that everything you get from `rttc.compile()` is ready for use as values in `*`, `{}`, or `[]` type machines, Treeline, Angular's rendering engine, and JavaScript code in general (i.e. if you were to append it on the right-hand side of `var x = `, or if you ran `eval()` on it)
Note that undefined values in arrays and undefined values of keys in dictionaries will be stripped out, and circular references will be handled as they are in `util.inspect(val, false, null)`
Useful for:
+ generating code samples
+ in particular for bootstrapping data on server-rendered views for access by client-side JavaScript
+ error messages,
+ debugging
+ user interfaces
Here's a table listing notable differences between `util.inspect()` and `rttc.compile()` for reference:
| value | util.inspect() | rttc.compile() |
|--------------------------|-------------------------------------------|--------------------------------------|
| a function | `[Function: foo]` | `'function foo (){}'` |
| a Date | `Tue May 26 2015 20:05:37 GMT-0500 (CDT)` | `'2015-05-27T01:06:37.072Z'` |
| a RegExp | `/foo/gi` | `'/foo/gi/'` |
| an Error | `[Error]` | `'Error\n at repl:1:24\n...'` |
| a deeply nested thing | `{ a: { b: { c: [Object] } } }` | `{ a: { b: { c: { d: {} } } } }` |
| a circular thing | `{ y: { z: [Circular] } }` | `{ y: { z: '[Circular ~]' } }` |
| undefined | `undefined` | `null` |
| [undefined] | `[undefined]` | [] |
| {foo: undefined} | `{foo: undefined}` | {} |
| Infinity | `Infinity` | `0` |
| -Infinity | `-Infinity` | `0` |
| NaN | `NaN` | `0` |
| Readable (Node stream) | `{ _readableState: { highWaterMar..}}` | `null` |
| Buffer (Node bytestring) | `<Buffer 61 62 63>` | `null` |
## Types
Here are the various types recognized by `rttc`. They are recursive within faceted dictionaries and patterned arrays. If those words don't make sense, keep reading, you'll see what I mean.
#### Strings
`example: 'stuff'`
#### Numbers
`example: 323`
#### Booleans
`example: false`
#### Generic dictionaries
`example: {}`
The **generic dictionary** type is a dictionary type schema with no keys.
The **generic dictionary** type accepts any JSON-serializable dictionary.
Dictionaries that have been validated/coerced against the generic dictionary type:

@@ -272,3 +150,3 @@ + will have no prototypal properties, getters, or setters, as well as a complete deficit of any other sort of deceit, lies, or magic

#### Faceted dictionaries
### Faceted dictionaries

@@ -299,3 +177,3 @@ `example: {...}`

#### Generic arrays
### Generic arrays

@@ -318,3 +196,3 @@ `example: []`

#### Patterned arrays
### Patterned arrays

@@ -354,3 +232,3 @@ `example: ['Margaret']`

#### Generic JSON
### Generic JSON

@@ -365,3 +243,3 @@ `example: '*'`

#### Mutable reference ("ref")
### Mutable reference ("ref")

@@ -375,18 +253,2 @@ `example: '==='`

## Base values
As mentioned above, every type has a base value.
+ For the "string" type, base value is `""`
+ For the "number" type, base value is `0`
+ For the "boolean" type, base value is `false`
+ For the "lamda" type (`'->'`), base value is a function that uses the standard machine fn signature and triggers its "error" callback w/ a message about being the rttc default (e.g. `function(inputs,exits,env) { return exits.error(new Error('not implemented')); }`)
+ For the generic dictionary type (`{}`) or a faceted dictionary type (e.g. `{foo:'bar'}`), the base value is `{}`.
+ For the generic array type (`[]`), or a faceted/homogenous array type (e.g. `[3]` or `[{age:48,name: 'Nico'}]`), the base value is `[]`
+ For the "json" type (`'*'`), base value is `null`.
+ For the "ref" type (`'==='`), base value is `undefined`.
> Note that, for both arrays and dictionaries, any keys in the schema will get the base value for their type (and their keys for their type, etc. -- recursive)
## Edge-cases

@@ -423,2 +285,178 @@

##### Base values
As mentioned above, every type has a base value.
+ For the "string" type, base value is `""`
+ For the "number" type, base value is `0`
+ For the "boolean" type, base value is `false`
+ For the "lamda" type (`'->'`), base value is a function that uses the standard machine fn signature and triggers its "error" callback w/ a message about being the rttc default (e.g. `function(inputs,exits,env) { return exits.error(new Error('not implemented')); }`)
+ For the generic dictionary type (`{}`) or a faceted dictionary type (e.g. `{foo:'bar'}`), the base value is `{}`.
+ For the generic array type (`[]`), or a faceted/homogenous array type (e.g. `[3]` or `[{age:48,name: 'Nico'}]`), the base value is `[]`
+ For the "json" type (`'*'`), base value is `null`.
+ For the "ref" type (`'==='`), base value is `undefined`.
> Note that, for both arrays and dictionaries, any keys in the schema will get the base value for their type (and their keys for their type, etc. -- recursive)
## Methods
This package exposes a number of different utility methods. If you're interested in using any of these directly, we highly recommend you consider looking at [machinepack-rttc](http://node-machine.org/machinepack-rttc), which provides a higher-level abstraction with better documentation.
> The low-level reference below assumes you are willing/able to dig into the source code of this module for more information. So continue at your own risk!
### Validation
##### .validateStrict(expectedTypeSchema, actualValue)
Throws if the provided value is not the right type (recursive).
##### .validate(expectedTypeSchema, actualValue)
Either returns a (potentially "lightly" coerced) version of the value that was accepted, or it throws. The "lightly" coerced value turns `"3"` into `3`, `"true"` into `true`, `-4.5` into `"-4.5"`, etc.
### Munging
##### .coerce(expectedTypeSchema, actualValue)
ALWAYS returns an acceptable version of the value, even if it has to mangle it to get there (i.e. by using the "base value" for the expected type. More on that below.)
##### .hydrate(value, [_typeSchema_=`undefined`])
This function will use the provided `typeSchema` to figure out where "lamda" values (functions) are expected, then will use `eval()` to bring them back to life. Use with care.
##### .dehydrate(value, [_allowNull_=`false`])
This takes care of a few serialization edge-cases, such as:
+ stringifies functions, regexps, and errors (grabs the `.stack` property)
+ replacing circular references with a string (e.g. `[Circular]`)
+ replaces `-Infinity`, `Infinity`, and `NaN` with 0
+ strips keys and array items with `undefined` or `null` values. If `allowNull` is set to true, `null` values will not be stripped from the encoded string.
##### .parse(stringifiedValue, [_typeSchema_=`undefined`], [_unsafeMode_=`false`])
Parse a stringified value back into a usable value.
This is basically just a variation on JSON.parse that calls `rttc.hydrate()` first if `unsafeMode` is enabled.
##### .stringify(value, [_allowNull_=`false`])
Encode a value into a string.
This is basically just a variation on JSON.stringify that calls `rttc.dehydrate()` first.
##### .parseHuman(stringFromHuman, [_typeSchema_=`undefined`], [_unsafeMode_=`false`])
Parse a string from a human into something usable. If provided, `typeSchema` will be used to make a better guess. If `unsafeMode` is enabled, lamda functions will be hydrated.
##### .stringifyHuman(value, typeSchema)
The inverse of `.parseHuman()`, this function encodes a string that, if run through `.parseHuman()` would result in the given value.
##### .reify(typeSchema)
Given a type schema, strip out generics ("ref", "json", {}, and []) to convert it into a strict type. In other words, this makes a type schema "strict", and the result of this function always passes `rttc.isStrictType()`.
### Assertions
##### .isEqual(firstValue, secondValue, [_expectedTypeSchema_=`undefined`])
Determine whether two values are equivalent using `_.isEqual()`, but also look for expected `lamda` values in the optional type schema and call `toString()` on functions before comparing them.
> This is the method used by `rttc`'s own tests to validate that expected values and actual values match.
##### .infer(exampleValue)
Guess the type schema from an example value.
##### .isStrictType(typeSchema, [recursive=false])
Determine whether the given type schema is "strict" (meaning it is a string, number, boolean, lamda, faceted dictionary, or patterned array). If second argument (`recursive`) is set to `true`, then also recursively check the subkeys of faceted dictionaries and patterns of arrays in the type schema.
| type | is strict? |
|-------------------------|---------------------|
| string | yes _(always)_ |
| number | yes _(always)_ |
| boolean | yes _(always)_ |
| lamda | yes _(always)_ |
| `{}` (generic) | no |
| `[]` (generic) | no |
| `{...}` (faceted) | yes _(maybe recursively)_ |
| `[...]` (patterned) | yes _(maybe recursively)_ |
| json | no |
| ref | no |
### Utilities
##### .sample(typeSchema, [n=2])
Given a type schema, return an array of up to `n` unique sample values that would validate against it (in random order). `n` defaults to 2 if left undefined.
##### .getDisplayType(value)
Given a value, return its type as a human-readable string (this is not limited to rttc types-- it can return strings like `"Error"` and `"Date"`)
##### .compile(value)
Given a value, return a human-readable string which represents it. This string is equivalent to a JavaScript code snippet which would accurately represent the value in code.
This is a lot like `util.inspect(val, false, null)`, but it also has special handling for Errors, Dates, RegExps, and Functions (using `dehydrate()` with `allowNull` enabled.) The biggest difference is that everything you get from `rttc.compile()` is ready for use as values in `*`, `{}`, or `[]` type machines, Treeline, Angular's rendering engine, and JavaScript code in general (i.e. if you were to append it on the right-hand side of `var x = `, or if you ran `eval()` on it)
Note that undefined values in arrays and undefined values of keys in dictionaries will be stripped out, and circular references will be handled as they are in `util.inspect(val, false, null)`
Useful for:
+ generating code samples
+ in particular for bootstrapping data on server-rendered views for access by client-side JavaScript
+ error messages,
+ debugging
+ user interfaces
Here's a table listing notable differences between `util.inspect()` and `rttc.compile()` for reference:
| value | util.inspect() | rttc.compile() |
|--------------------------|-------------------------------------------|--------------------------------------|
| a function | `[Function: foo]` | `'function foo (){}'` |
| a Date | `Tue May 26 2015 20:05:37 GMT-0500 (CDT)` | `'2015-05-27T01:06:37.072Z'` |
| a RegExp | `/foo/gi` | `'/foo/gi/'` |
| an Error | `[Error]` | `'Error\n at repl:1:24\n...'` |
| a deeply nested thing | `{ a: { b: { c: [Object] } } }` | `{ a: { b: { c: { d: {} } } } }` |
| a circular thing | `{ y: { z: [Circular] } }` | `{ y: { z: '[Circular ~]' } }` |
| undefined | `undefined` | `null` |
| [undefined] | `[undefined]` | [] |
| {foo: undefined} | `{foo: undefined}` | {} |
| Infinity | `Infinity` | `0` |
| -Infinity | `-Infinity` | `0` |
| NaN | `NaN` | `0` |
| Readable (Node stream) | `{ _readableState: { highWaterMar..}}` | `null` |
| Buffer (Node bytestring) | `<Buffer 61 62 63>` | `null` |
<!--
## More examples

@@ -599,2 +637,3 @@

-->

@@ -601,0 +640,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc