Socket
Socket
Sign inDemoInstall

json-e

Package Overview
Dependencies
Maintainers
10
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-e - npm Package Compare versions

Comparing version 2.1.1 to 2.2.1

6

package.json
{
"name": "json-e",
"version": "2.1.1",
"version": "2.2.1",
"description": "json parameterization module inspired from json-parameterization",

@@ -21,5 +21,3 @@ "main": "./src/index.js",

"license": "MPL-2.0",
"dependencies": {
"es6-error": "^4.0.1"
},
"dependencies": {},
"devDependencies": {

@@ -26,0 +24,0 @@ "assume": "^1.4.1",

@@ -40,8 +40,31 @@ # [JSON-e](https://taskcluster.github.io/json-e)

var context = {"foo": function(x) { return x + 2; }};
console.log(jsone(template, context));
// -> 3
console.log(jsone(template, context)); // -> 3
```
# Language
*NOTE*: Context functions are called synchronously. Any complex asynchronous
operations should be handled before rendering the template.
*NOTE*: If the template is untrusted, it can pass arbitrary data to functions
in the context, which must guard against such behavior.
The Python distribution exposes a `render` function:
```python
import jsone
var template = {"a": {"$eval": "foo.bar"}}
var context = {"foo": {"bar": "zoo"}}
print(jsone(template, contxt)) # -> {"a": "zoo"}
```
and also allows custom functions in the context:
```python
var template = {"$eval": "foo(1)"}
var context = {"foo": lambda x: x + 2}
print(jsone(template, contxt)) # -> 3
```
# Language Reference
The examples here are given in YAML for ease of reading. Of course, the

@@ -62,2 +85,4 @@ rendering operation takes place on the parsed data, so the input format is

## String Interpolation
The simplest form of substitution occurs within strings, using `${..}`:

@@ -131,14 +156,2 @@

### Truthiness
Many values can be evaluated in context where booleans are required,
not just booleans themselves. JSON-e defines the following values as false.
Anything else will be true.
```yaml
context: {a: null, b: [], c: {}, d: "", e: 0, f: false}
template: {$if: 'a || b || c || d || e || f', then: "uh oh", else: "falsy" }
result: "falsy"
```
### `$if` - `then` - `else`

@@ -197,5 +210,5 @@

The `$fromNow` operator is a shorthand for the built-in function `fromNow`. It
creates a JSON (ISO 8601) datestamp for a time relative to the current time.
The offset is specified by a sequence of number/unit pairs in a string. For
example:
creates a JSON (ISO 8601) datestamp for a time relative to the current time or,
if `from` is given, from that time. The offset is specified by a sequence of
number/unit pairs in a string. For example:

@@ -208,2 +221,8 @@ ```yaml

```yaml
context: {}
template: {$fromNow: '1 hour', from: '2017-01-19T16:27:20.974Z'}
result: '2017-01-19T17:27:20.974Z'
```
The available units are `day`, `hour`, and `minute`, for all of which a plural

@@ -294,5 +313,6 @@ is also accepted.

## Escaping operators
### Escaping operators
You can use `$$` to escape json-e operators. For example:
All property names starting with `$` are reserved for JSON-e.
You can use `$$` to escape such properties:

@@ -305,43 +325,188 @@ ```yaml

## Expressions
## Truthiness
Expression are given in a simple Python- or JavaScript-like language. It
supports the following:
Many values can be evaluated in context where booleans are required,
not just booleans themselves. JSON-e defines the following values as false.
Anything else will be true.
* Numeric literals (decimal only)
* String literals (enclosed in `'` or `"`, with no escaping)
* Arrays in JSON format (`[.., ..]`)
* Objects in JS format: `{"foo": 10}` or `{foo: 10}`
* Parentheses for grouping (`(a + b) * c`)
* Arithmetic on integers (`+`, `-`, `*`, `/`, `**` for exponentiation), with unary `-` and `+`
* String concatenation (`+`)
* Comparison of strings to strings or numbers to numbers (`<`, `<=`, `>`, `>=`)
* Equality of anything (`==`, `!=`)
* Boolean operators (`||`, `&&`, `!`)
* Identifiers referring to variables (matching `/[a-zA-Z_][a-zA-Z_0-9]*/`)
* Object property access: `obj.prop` or `obj["prop"]`
* `obj,prop` is an error if there is no such property; in the same case `obj["prop"]` evaluates to `null`.
* Array and string indexing and slicing with Python semantics
* `array[1]` -- second element of array (zero-indexed)
* `array[1:4]` -- second through fourth elements of the array (the slice includes the left index and excludes the right index)
* `array[1:]` -- second through last element of the array
* `array[:3]` -- first through third element of the array
* `array[-2:]` -- the last two elements of the array
* `array[:-1]` -- all but the last element of the array
* `string[3]` -- fourth character of the string
* `string[-4:]` -- all but the last four characters of the string
* Containment operator:
* `"string" in object` -- true if the object has the given property
* `"string" in array` -- true if the string is an array element
* `number in array` -- true if the number is an array element
* `"string" in "another string"` -- true if the first string is a substring of the second
* Function invocation: `fn(arg1, arg2)`
```yaml
context: {a: null, b: [], c: {}, d: "", e: 0, f: false}
template: {$if: 'a || b || c || d || e || f', then: "uh oh", else: "falsy" }
result: "falsy"
```
### Built-In Functions
## Expression Syntax
The expression language provides a laundry-list of built-in functions. Library
users can easily add additional functions, or override the built-ins, as part
Expression are given in a simple Python- or JavaScript-like expression
language. Its data types are limited to JSON types plus function objects.
### Literals
Literals are similar to those for JSON. Numeric literals only accept integer
and decimal notation. Strings do not support any kind of escaping. The use of
`\n` and `\t` in the example below depends on the YAML parser to expand the
escapes.
```yaml
context: {}
template:
- {$eval: "1.3"}
- {$eval: "'abc'"}
- {$eval: '"abc"'}
- {$eval: "'\n\t'"}
result:
- 1.3
- "abc"
- "abc"
- "\n\t"
```
Array and object literals also look much like JSON, with bare identifiers
allowed as keys like in Javascript:
```yaml
context: {}
template:
- {$eval: '[1, 2, "three"]'}
- {$eval: '{foo: 1, "bar": 2}'}
result:
- [1, 2, "three"]
- {"foo": 1, "bar": 2}
```
### Context References
Bare identifiers refer to items from the context or to built-ins (described below).
```yaml
context: {x: 'quick', z: 'sort'}
template: {$eval: '[x, z, x+z]'}
reslut: ['quick', 'sort', 'quicksort']
```
### Arithmetic Operations
The usual arithmetic operators are all defined, with typical associativity and
precedence:
```yaml
context: {x: 10, z: 20, s: "face", t: "plant"}
template:
- {$eval: 'x + z'}
- {$eval: 's + t'}
- {$eval: 'z - x'}
- {$eval: 'x * z'}
- {$eval: 'z / x'}
- {$eval: 'z ** 2'}
- {$eval: '(z / x) ** 2'}
result:
- 30
- "faceplant"
- 10
- 200
- 2
- 400
- 4
```
Note that strings can be concatenated with `+`, but none of the other operators
apply.
### Comparison Operations
Comparisons work as expected. Equality is "deep" in the sense of doing
comparisons of the contents of data structures.
```yaml
context: {x: -10, z: 10, deep: [1, [3, {a: 5}]]}
template:
- {$eval: 'x < z'}
- {$eval: 'x <= z'}
- {$eval: 'x > z'}
- {$eval: 'x >= z'}
- {$eval: 'deep == [1, [3, {a: 5}]]'}
- {$eval: 'deep != [1, [3, {a: 5}]]'}
result: [true, true, false, false, true, false]
```
### Boolean Operations
Boolean operations use C- and Javascript-style symbls `||`, `&&`, and `!`:
```yaml
context: {}
template: {$eval: '!(false || false) && true'}
result: true
```
### Object Property Access
Like Javascript, object properties can be accessed either with array-index
syntax or with dot syntax. Unlike Javascript, `obj.prop` is an error if `obj`
does not have `prop`, while `obj['prop']` will evaulate to `null`.
```yaml
context: {v: {a: 'apple', b: 'bananna', c: 'carrot'}}
template: {$eval: 'v.a + v["b"]'}
result: 'applebananna'
````
### Indexing and Slicing
Strings and arrays can be indexed and sliced using a Python-like indexing
scheme. Negative indexes are counted from the end of the value. Slices are
treated as "half-open", meaning that the result contains the first index and
does not contain the second index. A "backward" slice with the start index
greater than the end index is treated as empty.
```yaml
context: {array: ['a', 'b', 'c', 'd', 'e'], string: 'abcde'}
template:
- {$eval: '[array[1], string[1]]'}
- {$eval: '[array[1:4], string[1:4]]'}
- {$eval: '[array[2:], string[2:]]'}
- {$eval: '[array[:2], string[:2]]'}
- {$eval: '[array[4:2], string[4:2]]'}
- {$eval: '[array[-2], string[-2]]'}
- {$eval: '[array[-2:], string[-2:]]'}
- {$eval: '[array[:-3], string[:-3]]'}
result:
- ['b', 'b']
- [['b', 'c', 'd'], 'bcd']
- [['c', 'd', 'e'], 'cde']
- [['a', 'b'], 'ab']
- [[], '']
- ['d', 'd']
- [['d', 'e'], 'de']
- [['a', 'b'], 'ab']
```
### Containment Operation
The `in` keyword can be used to check for containment: a property in an object,
an element in an array, or a substring in a string.
```yaml
context: {}
template:
- {$eval: '"foo" in {foo: 1, bar: 2}'}
- {$eval: '"foo" in ["foo", "bar"]'}
- {$eval: '"foo" in "foobar"'}
result: [true, true, true]
```
### Function Invocation
Function calls are made with the usual `fn(arg1, arg2)` syntax. Functions are
not JSON data, so they cannot be created in JSON-e, but they can be provided as
built-ins or in the context and called from JSON-e.
#### Built-In Functions and Variables
The expression language provides a laundry-list of built-in functions/variables. Library
users can easily add additional functions/variables, or override the built-ins, as part
of the context.
* `fromNow(x)` -- JSON datestamp for a time relative to the current time
* `fromNow(offset)` or `fromNow(offset, reference)` -- JSON datestamp for a time relative to the current time or, if given, the reference time
* `now` -- the datestamp at the start of evaluation of the template. This is used implicitly as `from` in all fromNow calls. Override to set a different time.
* `min(a, b, ..)` -- the smallest of the arguments

@@ -354,30 +519,3 @@ * `max(a, b, ..)` -- the largest of the arguments

### Custom Functions
The context supplied to JSON-e can contain JS function objects. These will be
available just like the built-in functions are. For example:
```js
var context = {
imageData: function(img) {
return ...;
},
};
var template = {
title: "Trip to Hawaii",
thumbnail: {$eval: 'imageData("hawaii")'},
};
return jsone(template, context);
```
NOTE: Context functions are called synchronously. Any complex asynchronous
operations should be handled before rendering the template.
NOTE: If the template is untrusted, it can pass arbitrary data to functions
in the context, which must guard against such behavior. For example, if the
`imageData` function above reads data from a file, it must sanitize the
filename before opening it.
# Development and testing

@@ -411,1 +549,11 @@

```
## Development Notes
### Making a Release
* Update the version, commit, and tag -- `npm patch` (or minor or major, depending)
* Push to release the JS version -- `git push && git push --tags`
* Release to PyPi:
* `python setup.py sdist`
* `twine upload dist/json-e-<version>.tar.gz`

@@ -22,100 +22,103 @@ var {BuiltinError} = require('./error');

let builtins = {};
module.exports = (context) => {
let builtins = {};
let define = (name, context, {
argumentTests = [],
minArgs = false,
variadic = null,
invoke,
}) => context[name] = (...args) => {
if (!variadic && args.length < argumentTests.length) {
throw builtinError(`builtin: ${name}`, `${args.toString()}, too few arguments`);
}
let define = (name, context, {
argumentTests = [],
minArgs = false,
variadic = null,
invoke,
}) => context[name] = (...args) => {
if (!variadic && args.length < argumentTests.length) {
throw builtinError(`builtin: ${name}`, `${args.toString()}, too few arguments`);
}
if (minArgs && args.length < minArgs) {
throw builtinError(`builtin: ${name}: expected at least ${minArgs} arguments`);
}
if (minArgs && args.length < minArgs) {
throw builtinError(`builtin: ${name}: expected at least ${minArgs} arguments`);
}
if (variadic) {
argumentTests = args.map(() => variadic);
}
if (variadic) {
argumentTests = args.map(() => variadic);
}
args.forEach((arg, i) => {
if (!argumentTests[i].split('|').some(test => types[test](arg))) {
throw builtinError(`builtin: ${name}`, `argument ${i + 1} to be ${argumentTests[i]} found ${typeof arg}`);
args.forEach((arg, i) => {
if (!argumentTests[i].split('|').some(test => types[test](arg))) {
throw builtinError(`builtin: ${name}`, `argument ${i + 1} to be ${argumentTests[i]} found ${typeof arg}`);
}
});
return invoke(...args);
};
// Math functions
['max', 'min'].forEach(name => {
if (Math[name] == undefined) {
throw new Error(`${name} in Math undefined`);
}
define(name, builtins, {
minArgs: 1,
variadic: 'number',
invoke: (...args) => Math[name](...args),
});
});
return invoke(...args);
};
['sqrt', 'ceil', 'floor', 'abs'].forEach(name => {
if (Math[name] == undefined) {
throw new Error(`${name} in Math undefined`);
}
define(name, builtins, {
argumentTests: ['number'],
invoke: num => Math[name](num),
});
});
// Math functions
['max', 'min'].forEach(name => {
if (Math[name] == undefined) {
throw new Error(`${name} in Math undefined`);
}
define(name, builtins, {
minArgs: 1,
variadic: 'number',
invoke: (...args) => Math[name](...args),
// String manipulation
define('lowercase', builtins, {
argumentTests: ['string'],
invoke: str => str.toLowerCase(),
});
});
['sqrt', 'ceil', 'floor', 'abs'].forEach(name => {
if (Math[name] == undefined) {
throw new Error(`${name} in Math undefined`);
}
define(name, builtins, {
argumentTests: ['number'],
invoke: num => Math[name](num),
define('uppercase', builtins, {
argumentTests: ['string'],
invoke: str => str.toUpperCase(),
});
});
// String manipulation
define('lowercase', builtins, {
argumentTests: ['string'],
invoke: str => str.toLowerCase(),
});
define('str', builtins, {
argumentTests: ['string|number|boolean|array|null'],
invoke: obj => {
if (obj === null) {
return 'null';
}
return obj.toString();
},
});
define('uppercase', builtins, {
argumentTests: ['string'],
invoke: str => str.toUpperCase(),
});
define('len', builtins, {
argumentTests: ['string|array'],
invoke: obj => obj.length,
});
define('str', builtins, {
argumentTests: ['string|number|boolean|array|null'],
invoke: obj => {
if (obj === null) {
return 'null';
}
return obj.toString();
},
});
// Miscellaneous
define('fromNow', builtins, {
variadic: 'string',
minArgs: 1,
invoke: (str, reference) => fromNow(str, reference || context.now),
});
define('len', builtins, {
argumentTests: ['string|array'],
invoke: obj => obj.length,
});
// Miscellaneous
define('fromNow', builtins, {
argumentTests: ['string'],
invoke: str => fromNow(str),
});
define('typeof', builtins, {
argumentTests: ['string|number|boolean|array|object|null|function'],
invoke: x => {
for (type of ['string', 'number', 'boolean', 'array', 'object', 'function']) {
if (types[type](x)) {
return type;
define('typeof', builtins, {
argumentTests: ['string|number|boolean|array|object|null|function'],
invoke: x => {
for (type of ['string', 'number', 'boolean', 'array', 'object', 'function']) {
if (types[type](x)) {
return type;
}
}
}
if (types['null'](x)) {
return null;
}
throw builtinError('builtin: typeof', `argument ${x} to be a valid json-e type. found ${typeof arg}`);
},
});
if (types['null'](x)) {
return null;
}
throw builtinError('builtin: typeof', `argument ${x} to be a valid json-e type. found ${typeof arg}`);
},
});
module.exports = builtins;
return Object.assign({}, builtins, context);
};

@@ -1,14 +0,29 @@

var ExtendableError = require('es6-error');
class JSONTemplateError extends Error {
constructor(message) {
super(message);
this.location = [];
}
class SyntaxError extends ExtendableError {
constructor(message, {start, end}) {
add_location(loc) {
this.location.unshift(loc);
}
toString() {
if (this.location.length) {
return `${this.name} at template${this.location.join('')}: ${this.message}`;
} else {
return `${this.name}: ${this.message}`;
}
}
}
class SyntaxError extends JSONTemplateError {
constructor(message) {
super(message);
this.message = message;
this.name = 'SyntaxError';
this.message = message;
this.start = start;
this.end = end;
}
}
class BaseError extends ExtendableError {
class BaseError extends JSONTemplateError {
constructor(message) {

@@ -42,2 +57,2 @@ super(message);

module.exports = {SyntaxError, InterpreterError, TemplateError, BuiltinError};
module.exports = {JSONTemplateError, SyntaxError, InterpreterError, TemplateError, BuiltinError};

@@ -37,3 +37,3 @@ // Regular expression matching:

// Render timespan fromNow as JSON timestamp
module.exports = (timespan = '', reference = new Date()) => {
module.exports = (timespan = '', reference) => {
let offset = parseTime(timespan);

@@ -45,2 +45,8 @@

if (reference) {
reference = new Date(reference);
} else {
reference = new Date();
}
var retval = new Date(

@@ -47,0 +53,0 @@ reference.getTime()

@@ -9,4 +9,4 @@ var interpreter = require('./interpreter');

} = require('./type-utils');
var builtins = require('./builtins');
var {TemplateError} = require('./error');
var addBuiltins = require('./builtins');
var {JSONTemplateError, TemplateError} = require('./error');

@@ -17,4 +17,2 @@ let flattenDeep = (a) => {

let jsonTemplateError = (msg, template) => new TemplateError(msg + JSON.stringify(template, null, '\t'));
let interpolate = (string, context) => {

@@ -30,3 +28,4 @@ let result = '';

if (isArray(v.result) || isObject(v.result)) {
throw new TemplateError('cannot interpolate array/object: ' + string);
let input = remaining.slice(offset + 2, offset + v.offset);
throw new TemplateError(`interpolation of '${input}' produced an array or object`);
}

@@ -58,5 +57,2 @@

let value = render(template['$eval'], context);
if (!isString(value)) {
throw jsonTemplateError('$eval can evaluate string expressions only\n', template);
}
return interpreter.parse(value, context);

@@ -69,3 +65,3 @@ };

if (!isArray(value)) {
throw jsonTemplateError('$flatten requires array as value\n', template);
throw new TemplateError('$flatten value must evaluate to an array');
}

@@ -80,3 +76,3 @@

if (!isArray(value)) {
throw jsonTemplateError('$flattenDeep requires array as value\n', template);
throw new TemplateError('$flattenDeep value must evaluate to an array');
}

@@ -89,6 +85,10 @@

let value = render(template['$fromNow'], context);
let reference = context.now;
if (template['from']) {
reference = render(template['from'], context);
}
if (!isString(value)) {
throw jsonTemplateError('$fromNow can evaluate string expressions only\n', template);
throw new TemplateError('$fromNow expects a string');
}
return fromNow(value);
return fromNow(value, reference);
};

@@ -98,3 +98,3 @@

if (!isString(template['$if'])) {
throw jsonTemplateError('$if can evaluate string expressions only\n', template);
throw new TemplateError('$if can evaluate string expressions only');
}

@@ -117,7 +117,7 @@ if (isTruthy(interpreter.parse(template['$if'], context))) {

if (!isObject(variables)) {
throw jsonTemplateError('$let operator requires an object as the context\n', template);
throw new TemplateError('$let value must evaluate to an object');
}
if (template.in == undefined) {
throw jsonTemplateError('$let operator requires `in` clause\n', template);
throw new TemplateError('$let operator requires an `in` clause');
}

@@ -131,7 +131,7 @@

if (!isArray(value) && !isObject(value)) {
throw jsonTemplateError('$map requires array or object as value\n', template);
throw new TemplateError('$map value must evaluate to an array or object');
}
if (Object.keys(template).length !== 2) {
throw jsonTemplateError('$map requires cannot have more than two properties\n', template);
throw new TemplateError('$map requires cannot have more than two properties');
}

@@ -142,3 +142,3 @@

if (!match) {
throw jsonTemplateError('$map requires each(identifier) syntax\n', template);
throw new TemplateError('$map requires each(identifier) syntax');
}

@@ -169,3 +169,3 @@

if (!isArray(value) || value.some(o => !isObject(o))) {
throw jsonTemplateError('$merge requires array as value\n', template);
throw new TemplateError('$merge value must evaluate to an array of objects');
}

@@ -180,7 +180,7 @@

if (!isArray(value) && !isArray(template['$reverse'])) {
throw jsonTemplateError('$reverse value must evaluate to an array\n', template);
throw new TemplateError('$reverse value must evaluate to an array of objects');
}
if (!isArray(value)) {
throw jsonTemplateError('$reverse requires array as value\n', template);
throw new TemplateError('$reverse requires array as value');
}

@@ -193,3 +193,3 @@ return value.reverse();

if (!isArray(value)) {
throw jsonTemplateError('$sort requires array as value\n', template);
throw new TemplateError('$sort requires array as value');
}

@@ -211,3 +211,3 @@

if (needBy) {
throw jsonTemplateError('$sort requires by(identifier) for sorting arrays of objects/arrays\n', template);
throw new TemplateError('$sort requires by(identifier) for sorting arrays of objects/arrays');
}

@@ -225,3 +225,3 @@ by = value => value;

tagged.some(e => eltType !== typeof e[0])) {
throw jsonTemplateError('$sort requires all sorted values have the same type', template);
throw new TemplateError('$sort requires all sorted values have the same type');
}

@@ -250,3 +250,12 @@ }

if (isArray(template)) {
return template.map((v) => render(v, context)).filter((v) => v !== deleteMarker);
return template.map((v, i) => {
try {
return render(v, context);
} catch (err) {
if (err instanceof JSONTemplateError) {
err.add_location(`[${i}]`);
}
throw err;
}
}).filter((v) => v !== deleteMarker);
}

@@ -256,3 +265,3 @@

if (matches.length > 1) {
throw jsonTemplateError('only one operator allowed\n', template);
throw new TemplateError('only one operator allowed');
}

@@ -266,3 +275,15 @@ if (matches.length === 1) {

for (let key of Object.keys(template)) {
let value = render(template[key], context);
let value;
try {
value = render(template[key], context);
} catch (err) {
if (err instanceof JSONTemplateError) {
if (/^[a-zA-Z][a-zA-Z0-9]*$/.test(key)) {
err.add_location(`.${key}`);
} else {
err.add_location(`[${JSON.stringify(key)}]`);
}
}
throw err;
}
if (value !== deleteMarker) {

@@ -280,5 +301,7 @@ if (key.startsWith('$$') && operators.hasOwnProperty(key.substr(1))) {

module.exports = (template, context = {}) => {
let test = Object.keys(context).every(v => /^[a-zA-Z_][a-zA-Z0-9_]*$/.exec(v)[0]);
context = Object.assign({}, builtins, context);
assert(test, 'top level keys of context must follow /[a-zA-Z_][a-zA-Z0-9_]*/');
let test = Object.keys(context).every(v => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(v));
if (!test) {
throw new TemplateError('top level keys of context must follow /[a-zA-Z_][a-zA-Z0-9_]*/');
}
context = addBuiltins(Object.assign({}, {now: new Date()}, context));
let result = render(template, context);

@@ -285,0 +308,0 @@ if (result === deleteMarker) {

@@ -8,3 +8,4 @@ /*

var assert = require('assert');
var {SyntaxError} = require('./error');
var {isString} = require('./type-utils');
var {SyntaxError, TemplateError} = require('./error');

@@ -45,2 +46,5 @@ let syntaxRuleError = (token, expects) => new SyntaxError(`Found '${token.value}' expected '${expects}'`, token);

parse(source, context = {}, offset = 0) {
if (!isString(source)) {
throw new TemplateError('expression to be evaluated must be a string');
}
let ctx = new Context(this, source, context, offset);

@@ -59,3 +63,8 @@ let result = ctx.parse();

let next = ctx.attempt();
if (next.kind !== terminator) {
if (!next) {
// string ended without the terminator
let errorLocation = source.length;
throw new SyntaxError(`Found end of string, expected ${terminator}`,
{start: errorLocation, end: errorLocation});
} else if (next.kind !== terminator) {
throw syntaxRuleError(next, terminator);

@@ -62,0 +71,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