Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@exodus/schemasafe

Package Overview
Dependencies
Maintainers
60
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@exodus/schemasafe - npm Package Compare versions

Comparing version 1.0.0-rc.3 to 1.0.0-rc.4

168

index.d.ts

@@ -1,39 +0,153 @@

// This typings are experimental and known to be incomplete.
// These typings are experimental and known to be incomplete.
// Help wanted at https://github.com/ExodusMovement/schemasafe/issues/130
type Json = string | number | boolean | null | Array<Json> | { [id: string]: Json }
type Schema =
| true
| false
| {
// version
$schema?: string
$vocabulary?: string
// pointers
id?: string
$id?: string
$anchor?: string
$ref?: string
definitions?: { [id: string]: Schema }
$defs?: { [id: string]: Schema }
$recursiveRef?: string
$recursiveAnchor?: boolean
// generic
type?: string | Array<string>
required?: Array<string>
default?: Json
// constant values
enum?: Array<Json>
const?: Json
// logical checks
not?: Schema
allOf?: Array<Schema>
anyOf?: Array<Schema>
oneOf?: Array<Schema>
if?: Schema
then?: Schema
else?: Schema
// numbers
maximum?: number
minimum?: number
exclusiveMaximum?: number | boolean
exclusiveMinimum?: number | boolean
multipleOf?: number
divisibleBy?: number
// arrays, basic
items?: Schema | Array<Schema>
maxItems?: number
minItems?: number
additionalItems?: Schema
// arrays, complex
contains?: Schema
minContains?: number
maxContains?: number
uniqueItems?: boolean
// strings
maxLength?: number
minLength?: number
format?: string
pattern?: string
// strings content
contentEncoding?: string
contentMediaType?: string
contentSchema?: Schema
// objects
properties?: { [id: string]: Schema }
maxProperties?: number
minProperties?: number
additionalProperties?: Schema
patternProperties?: { [pattern: string]: Schema }
propertyNames?: Schema
dependencies?: { [id: string]: Array<string> | Schema }
dependentRequired?: { [id: string]: Array<string> }
dependentSchemas?: { [id: string]: Schema }
// see-through
unevaluatedProperties?: Schema
unevaluatedItems?: Schema
// Unused meta keywords not affecting validation (annotations and comments)
// https://json-schema.org/understanding-json-schema/reference/generic.html
// https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9
title?: string
description?: string
deprecated?: boolean
readOnly?: boolean
writeOnly?: boolean
examples?: Array<Json>
$comment?: string
// optimization hint and error filtering only, does not affect validation result
discriminator?: { propertyName: string; mapping?: { [value: string]: string } }
}
interface ValidationError {
keywordLocation: string;
instanceLocation: string;
keywordLocation: string
instanceLocation: string
}
interface Validate {
(value: any): boolean;
errors?: ValidationError[];
(value: Json): boolean
errors?: ValidationError[]
toModule(): string
toJSON(): Schema
}
interface ValidatorOptions {
mode?: string,
useDefaults?: boolean;
removeAdditional?: boolean;
includeErrors?: boolean;
allErrors?: boolean;
dryRun?: boolean;
allowUnusedKeywords?: boolean;
allowUnreachable?: boolean;
requireSchema?: boolean;
requireValidation?: boolean;
requireStringValidation?: boolean;
forbidNoopValues?: boolean;
complexityChecks?: boolean;
unmodifiedPrototypes?: boolean;
isJSON?: boolean;
$schemaDefault?: string | null;
formats?: any; // FIXME
weakFormats?: boolean;
extraFormats?: boolean;
schemas?: any; // FIXME
mode?: string
useDefaults?: boolean
removeAdditional?: boolean
includeErrors?: boolean
allErrors?: boolean
dryRun?: boolean
allowUnusedKeywords?: boolean
allowUnreachable?: boolean
requireSchema?: boolean
requireValidation?: boolean
requireStringValidation?: boolean
forbidNoopValues?: boolean
complexityChecks?: boolean
unmodifiedPrototypes?: boolean
isJSON?: boolean
jsonCheck?: boolean
$schemaDefault?: string | null
formats?: { [key: string]: RegExp | ((input: string) => boolean) }
weakFormats?: boolean
extraFormats?: boolean
schemas?: Map<string, Schema> | Array<Schema> | { [id: string]: Schema }
}
declare const validator: (schema: object, options?: ValidatorOptions) => Validate;
interface ParseResult {
valid: boolean
value?: Json
error?: string
errors?: ValidationError[]
}
export { validator, Validate, ValidationError, ValidatorOptions };
interface Parse {
(value: string): ParseResult
toModule(): string
toJSON(): Schema
}
declare const validator: (schema: Schema, options?: ValidatorOptions) => Validate
declare const parser: (schema: Schema, options?: ValidatorOptions) => Parse
export {
validator,
parser,
Validate,
ValidationError,
ValidatorOptions,
ParseResult,
Parse,
Json,
Schema,
}

7

package.json
{
"name": "@exodus/schemasafe",
"version": "1.0.0-rc.3",
"version": "1.0.0-rc.4",
"description": "JSON Safe Parser & Schema Validator",

@@ -31,3 +31,3 @@ "license": "MIT",

"scripts": {
"lint": "prettier --list-different '**/*.js'&& eslint .",
"lint": "prettier --list-different '**/*.js' && eslint .",
"format": "prettier --write '**/*.js'",

@@ -56,2 +56,5 @@ "coverage": "c8 --reporter=lcov --reporter=text npm run test",

},
"resolutions": {
"tap-spec/tap-out/trim": "^1.0.1"
},
"keywords": [

@@ -58,0 +61,0 @@ "JSON",

@@ -60,3 +60,3 @@ # `@exodus/schemasafe`

Or use the [parser mode](./doc/Parser-not-validator.md) (running in
Or use the [parser API](./doc/Parser-not-validator.md) (running in
[strong mode](./doc/Strong-mode.md) by default):

@@ -80,6 +80,9 @@

console.log('returns { valid: true, value }:', parse('{"hello": "world" }'))
console.log('returns { valid: false }:', parse('{}'))
console.log(parse('{"hello": "world" }')) // { valid: true, value: { hello: 'world' } }
console.log(parse('{}')) // { valid: false }
```
Parser API is recommended, because this way you can avoid handling unvalidated JSON objects in
non-string form at all in your code.
## Options

@@ -92,3 +95,3 @@

`@exodus/schemasafe` supports the formats specified in JSON schema v4 (such as date-time).
If you want to add your own custom formats pass them as the formats options to the validator
If you want to add your own custom formats pass them as the formats options to the validator:

@@ -98,11 +101,22 @@ ```js

type: 'string',
format: 'no-foo'
}, {
formats: {
'no-foo': (str) => !str.includes('foo'),
}
})
console.log(validate('test')) // true
console.log(validate('foo')) // false
const parse = parser({
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'string',
format: 'only-a'
}, {
formats: {
'only-a': /^a+$/
'only-a': /^a+$/,
}
})
console.log(validate('aa')) // true
console.log(validate('ab')) // false
console.log(parse('"aa"')) // { valid: true, value: 'aa' }
console.log(parse('"ab"')) // { valid: false }
```

@@ -159,2 +173,26 @@

Or, similarly, with parser API:
```js
const schema = {
$schema: 'https://json-schema.org/draft/2019-09/schema',
type: 'object',
required: ['hello'],
properties: {
hello: {
type: 'string',
pattern: '^[a-z]+$',
}
},
additionalProperties: false,
}
const parse = parser(schema, { includeErrors: true })
console.log(parse('{ "hello": 100 }'));
// { valid: false,
// error: 'JSON validation failed for type at #/hello',
// errors: [ { keywordLocation: '#/properties/hello/type', instanceLocation: '#/hello' } ]
// }
```
Only the first error is reported by default unless `allErrors` option is also set to `true` in

@@ -209,2 +247,16 @@ addition to `includeErrors`.

## Contributing
Get a fully set up development environment with:
```sh
git clone https://github.com/ExodusMovement/schemasafe
cd schemasafe
git submodule update --init --recursive
yarn
yarn lint
yarn test
```
## Previous work

@@ -220,5 +272,5 @@

versions.
## License
[MIT](./LICENSE)

@@ -47,2 +47,13 @@ 'use strict'

const rootMeta = new WeakMap()
const generateMeta = (root, $schema, enforce, requireSchema) => {
if ($schema) {
const version = $schema.replace(/^http:\/\//, 'https://').replace(/#$/, '')
enforce(schemaVersions.includes(version), 'Unexpected schema version:', version)
rootMeta.set(root, { exclusiveRefs: schemaIsOlderThan(version, 'draft/2019-09') })
} else {
enforce(!requireSchema, '[requireSchema] $schema is required')
rootMeta.set(root, {})
}
}
const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {

@@ -144,3 +155,3 @@ const {

const recursiveAnchor = schema && schema.$recursiveAnchor === true
const getMeta = () => rootMeta.get(root) || {}
const getMeta = () => rootMeta.get(root)
const basePathStack = basePathRoot ? [basePathRoot] : []

@@ -194,2 +205,3 @@ const visit = (errors, history, current, node, schemaPath, trace = {}, { constProp } = {}) => {

const complex = (msg, arg) => enforce(!complexityChecks, `[complexityChecks] ${msg}`, arg)
const saveMeta = ($sch) => generateMeta(root, $sch || $schemaDefault, enforce, requireSchema)

@@ -243,8 +255,3 @@ // evaluated tracing

if (node === root) {
const $schema = get('$schema', 'string') || $schemaDefault
if ($schema) {
const version = $schema.replace(/^http:\/\//, 'https://').replace(/#$/, '')
enforce(schemaVersions.includes(version), 'Unexpected schema version:', version)
rootMeta.set(root, { exclusiveRefs: schemaIsOlderThan(version, 'draft/2019-09') })
} else enforce(!requireSchema, '[requireSchema] $schema is required')
saveMeta(get('$schema', 'string'))
handle('$vocabulary', ['object'], ($vocabulary) => {

@@ -257,3 +264,3 @@ for (const [vocab, flag] of Object.entries($vocabulary)) {

})
}
} else if (!getMeta()) saveMeta(root.$schema)

@@ -354,3 +361,4 @@ handle('examples', ['array'], null) // unused, meta-only

// Those checks will need to be skipped if another error is set in this block before those ones
const haveComplex = node.uniqueItems || node.pattern || node.patternProperties || node.format
const havePattern = node.pattern && !noopRegExps.has(node.pattern) // we won't generate code for noop
const haveComplex = node.uniqueItems || havePattern || node.patternProperties || node.format
const prev = allErrors && haveComplex ? gensym('prev') : null

@@ -625,5 +633,6 @@ const prevWrap = (shouldWrap, writeBody) =>

handle('propertyNames', ['object', 'boolean'], (names) => {
handle('propertyNames', ['object', 'boolean'], (s) => {
forObjectKeys(current, (sub, key) => {
const nameSchema = typeof names === 'object' ? { type: 'string', ...names } : names
// Add default type for non-ref schemas, so strong mode is fine with omitting it
const nameSchema = typeof s === 'object' && !s.$ref ? { type: 'string', ...s } : s
const nameprop = Object.freeze({ name: key, errorParent: sub, type: 'string' })

@@ -710,4 +719,5 @@ rule(nameprop, nameSchema, subPath('propertyNames'))

const checkConst = () => {
if (handle('const', ['jsonval'], (val) => safenot(compare(name, val)))) return true
return handle('enum', ['array'], (vals) => {
const handledConst = handle('const', ['jsonval'], (val) => safenot(compare(name, val)))
if (handledConst && !allowUnusedKeywords) return true // enum can't be present, this is rechecked by allowUnusedKeywords
const handledEnum = handle('enum', ['array'], (vals) => {
const objects = vals.filter((value) => value && typeof value === 'object')

@@ -717,2 +727,3 @@ const primitive = vals.filter((value) => !(value && typeof value === 'object'))

})
return handledConst || handledEnum
}

@@ -922,7 +933,10 @@

if (checkConst()) {
// const/enum shouldn't have any other validation rules except for already checked type/$ref
enforce(unused.size === 0, 'Unexpected keywords mixed with const or enum:', [...unused])
const typeKeys = [...types.keys()] // we don't extract type from const/enum, it's enough that we know that it's present
evaluateDelta({ properties: [true], items: Infinity, type: typeKeys, fullstring: true }) // everything is evaluated for const
return
if (!allowUnusedKeywords) {
// const/enum shouldn't have any other validation rules except for already checked type/$ref
enforce(unused.size === 0, 'Unexpected keywords mixed with const or enum:', [...unused])
// If it does though, we should not short-circuit validation. This could be optimized by extracting types, but not significant
return
}
}

@@ -955,2 +969,12 @@

if (!sub && sub !== false) fail('failed to resolve $ref:', $ref)
if (sub.type) {
// This could be done better, but for now we check only the direct type in the $ref
const type = Array.isArray(sub.type) ? sub.type : [sub.type]
evaluateDelta({ type })
if (requireValidation) {
// If validation is required, then $ref is guranteed to validate all items and properties
if (type.includes('array')) evaluateDelta({ items: Infinity })
if (type.includes('object')) evaluateDelta({ properties: [true] })
}
}
const n = getref(sub) || compileSchema(sub, subRoot, opts, scope, path)

@@ -957,0 +981,0 @@ return applyRef(n, { path: ['$ref'] })

@@ -57,3 +57,3 @@ 'use strict'

} catch ({ message }) {
return { valid: false, message }
return { valid: false, error: message }
}

@@ -79,2 +79,3 @@ }

].join('\n')
parse.toJSON = () => schema
return parse

@@ -81,0 +82,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