Socket
Socket
Sign inDemoInstall

fast-json-stringify

Package Overview
Dependencies
Maintainers
9
Versions
160
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fast-json-stringify - npm Package Compare versions

Comparing version 4.2.0 to 5.0.0

2

ajv.js

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

function buildAjv (options) {
const ajvInstance = new Ajv({ ...options, strictSchema: false, uriResolver: fastUri })
const ajvInstance = new Ajv({ ...options, strictSchema: false, validateSchema: false, uriResolver: fastUri })
ajvFormats(ajvInstance)

@@ -13,0 +13,0 @@

@@ -15,3 +15,2 @@ 'use strict'

let largeArraySize = 2e4
let stringSimilarity = null
let largeArrayMechanism = 'default'

@@ -45,14 +44,44 @@ const validLargeArrayMechanisms = [

function mergeLocation (source, dest) {
function mergeLocation (location, key) {
return {
schema: dest.schema || source.schema,
root: dest.root || source.root,
externalSchema: dest.externalSchema || source.externalSchema
schema: location.schema[key],
schemaId: location.schemaId,
jsonPointer: location.jsonPointer + '/' + key
}
}
function resolveRef (location, ref) {
let hashIndex = ref.indexOf('#')
if (hashIndex === -1) {
hashIndex = ref.length
}
const schemaId = ref.slice(0, hashIndex) || location.schemaId
const jsonPointer = ref.slice(hashIndex)
const schemaRef = schemaId + jsonPointer
let ajvSchema
try {
ajvSchema = ajvInstance.getSchema(schemaRef)
} catch (error) {
throw new Error(`Cannot find reference "${ref}"`)
}
if (ajvSchema === undefined) {
throw new Error(`Cannot find reference "${ref}"`)
}
const schema = ajvSchema.schema
if (schema.$ref !== undefined) {
return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
}
return { schema, schemaId, jsonPointer }
}
const arrayItemsReferenceSerializersMap = new Map()
const objectReferenceSerializersMap = new Map()
const schemaReferenceMap = new Map()
let rootSchemaId = null
let ajvInstance = null

@@ -62,5 +91,6 @@ let contextFunctions = null

function build (schema, options) {
schema = clone(schema)
arrayItemsReferenceSerializersMap.clear()
objectReferenceSerializersMap.clear()
schemaReferenceMap.clear()

@@ -71,8 +101,24 @@ contextFunctions = []

ajvInstance = buildAjv(options.ajv)
rootSchemaId = schema.$id || randomUUID()
isValidSchema(schema)
extendDateTimeType(schema)
ajvInstance.addSchema(schema, rootSchemaId)
if (options.schema) {
// eslint-disable-next-line
for (var key of Object.keys(options.schema)) {
isValidSchema(options.schema[key], key)
options.schema = clone(options.schema)
for (const key of Object.keys(options.schema)) {
const externalSchema = options.schema[key]
isValidSchema(externalSchema, key)
extendDateTimeType(externalSchema)
let schemaKey = externalSchema.$id || key
if (externalSchema.$id !== undefined && externalSchema.$id[0] === '#') {
schemaKey = key + externalSchema.$id // relative URI
}
if (ajvInstance.getSchema(schemaKey) === undefined) {
ajvInstance.addSchema(externalSchema, schemaKey)
}
}

@@ -105,19 +151,5 @@ }

let location = {
schema,
root: schema,
externalSchema: options.schema
}
const location = { schema, schemaId: rootSchemaId, jsonPointer: '#' }
const code = buildValue(location, 'input')
if (schema.$ref) {
location = refFinder(schema.$ref, location)
schema = location.schema
}
if (schema.type === undefined) {
schema.type = inferTypeByKeyword(schema)
}
const code = buildValue('main', 'input', location)
const contextFunctionCode = `

@@ -154,6 +186,6 @@ function main (input) {

ajvInstance = null
rootSchemaId = null
contextFunctions = null
arrayItemsReferenceSerializersMap.clear()
objectReferenceSerializersMap.clear()
schemaReferenceMap.clear()

@@ -239,6 +271,7 @@ return stringifyFunc

Object.keys(pp).forEach((regex, index) => {
let ppLocation = mergeLocation(location, { schema: pp[regex] })
const patternPropertiesLocation = mergeLocation(location, 'patternProperties')
Object.keys(pp).forEach((regex) => {
let ppLocation = mergeLocation(patternPropertiesLocation, regex)
if (pp[regex].$ref) {
ppLocation = refFinder(pp[regex].$ref, location)
ppLocation = resolveRef(ppLocation, pp[regex].$ref)
pp[regex] = ppLocation.schema

@@ -253,3 +286,3 @@ }

const valueCode = buildValue('', 'obj[keys[i]]', ppLocation)
const valueCode = buildValue(ppLocation, 'obj[keys[i]]')
code += `

@@ -287,9 +320,9 @@ if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {

}
let apLocation = mergeLocation(location, { schema: ap })
let apLocation = mergeLocation(location, 'additionalProperties')
if (ap.$ref) {
apLocation = refFinder(ap.$ref, location)
apLocation = resolveRef(location, ap.$ref)
ap = apLocation.schema
}
const valueCode = buildValue('', 'obj[keys[i]]', apLocation)
const valueCode = buildValue(apLocation, 'obj[keys[i]]')

@@ -316,136 +349,5 @@ code += `

function idFinder (schema, searchedId) {
let objSchema
const explore = (schema, searchedId) => {
Object.keys(schema || {}).forEach((key, i, a) => {
if (key === '$id' && schema[key] === searchedId) {
objSchema = schema
} else if (objSchema === undefined && typeof schema[key] === 'object') {
explore(schema[key], searchedId)
}
})
}
explore(schema, searchedId)
return objSchema
}
function refFinder (ref, location) {
const externalSchema = location.externalSchema
let root = location.root
let schema = location.schema
if (externalSchema && externalSchema[ref]) {
return {
schema: externalSchema[ref],
root: externalSchema[ref],
externalSchema
}
}
// Split file from walk
ref = ref.split('#')
// Check schemaReferenceMap for $id entry
if (ref[0] && schemaReferenceMap.has(ref[0])) {
schema = schemaReferenceMap.get(ref[0])
root = schemaReferenceMap.get(ref[0])
if (schema.$ref) {
return refFinder(schema.$ref, {
schema,
root,
externalSchema
})
}
} else if (ref[0]) { // If external file
schema = externalSchema[ref[0]]
root = externalSchema[ref[0]]
if (schema === undefined) {
findBadKey(externalSchema, [ref[0]])
}
if (schema.$ref) {
return refFinder(schema.$ref, {
schema,
root,
externalSchema
})
}
}
let code = 'return schema'
// If it has a path
if (ref[1]) {
// ref[1] could contain a JSON pointer - ex: /definitions/num
// or plain name fragment id without suffix # - ex: customId
const walk = ref[1].split('/')
if (walk.length === 1) {
const targetId = `#${ref[1]}`
let dereferenced = idFinder(schema, targetId)
if (dereferenced === undefined && !ref[0]) {
// eslint-disable-next-line
for (var key of Object.keys(externalSchema)) {
dereferenced = idFinder(externalSchema[key], targetId)
if (dereferenced !== undefined) {
root = externalSchema[key]
break
}
}
}
return {
schema: dereferenced,
root,
externalSchema
}
} else {
// eslint-disable-next-line
for (var i = 1; i < walk.length; i++) {
code += `[${JSON.stringify(walk[i])}]`
}
}
}
let result
try {
result = (new Function('schema', code))(root)
} catch (err) {}
if (result === undefined && ref[1]) {
const walk = ref[1].split('/')
findBadKey(schema, walk.slice(1))
}
if (result.$ref) {
return refFinder(result.$ref, {
schema,
root,
externalSchema
})
}
return {
schema: result,
root,
externalSchema
}
function findBadKey (obj, keys) {
if (keys.length === 0) return null
const key = keys.shift()
if (obj[key] === undefined) {
stringSimilarity = stringSimilarity || require('string-similarity')
const { bestMatch } = stringSimilarity.findBestMatch(key, Object.keys(obj))
if (bestMatch.rating >= 0.5) {
throw new Error(`Cannot find reference ${JSON.stringify(key)}, did you mean ${JSON.stringify(bestMatch.target)}?`)
} else {
throw new Error(`Cannot find reference ${JSON.stringify(key)}`)
}
}
return findBadKey(obj[key], keys)
}
}
function buildCode (location, locationPath) {
function buildCode (location) {
if (location.schema.$ref) {
location = refFinder(location.schema.$ref, location)
location = resolveRef(location, location.schema.$ref)
}

@@ -458,6 +360,7 @@

const propertiesLocation = mergeLocation(location, 'properties')
Object.keys(schema.properties || {}).forEach((key) => {
let propertyLocation = mergeLocation(location, { schema: schema.properties[key] })
let propertyLocation = mergeLocation(propertiesLocation, key)
if (schema.properties[key].$ref) {
propertyLocation = refFinder(schema.properties[key].$ref, location)
propertyLocation = resolveRef(location, schema.properties[key].$ref)
schema.properties[key] = propertyLocation.schema

@@ -478,3 +381,3 @@ }

code += buildValue(locationPath + key, `obj[${JSON.stringify(key)}]`, mergeLocation(propertyLocation, { schema: schema.properties[key] }))
code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)

@@ -509,5 +412,10 @@ const defaultValue = schema.properties[key].default

function mergeAllOfSchema (location, schema, mergedSchema) {
for (let allOfSchema of schema.allOf) {
const allOfLocation = mergeLocation(location, 'allOf')
for (let i = 0; i < schema.allOf.length; i++) {
let allOfSchema = schema.allOf[i]
if (allOfSchema.$ref) {
allOfSchema = refFinder(allOfSchema.$ref, mergeLocation(location, { schema: allOfSchema })).schema
const allOfSchemaLocation = mergeLocation(allOfLocation, i)
allOfSchema = resolveRef(allOfSchemaLocation, allOfSchema.$ref).schema
}

@@ -599,5 +507,5 @@

function buildInnerObject (location, locationPath) {
function buildInnerObject (location) {
const schema = location.schema
let code = buildCode(location, locationPath)
let code = buildCode(location)
if (schema.patternProperties) {

@@ -611,35 +519,32 @@ code += addPatternProperties(location)

function addIfThenElse (location, locationPath) {
let code = ''
function addIfThenElse (location) {
const schema = merge({}, location.schema)
const thenSchema = schema.then
const elseSchema = schema.else || { additionalProperties: true }
const schema = location.schema
const copy = merge({}, schema)
const i = copy.if
const then = copy.then
const e = copy.else ? copy.else : { additionalProperties: true }
delete copy.if
delete copy.then
delete copy.else
let merged = merge(copy, then)
let mergedLocation = mergeLocation(location, { schema: merged })
delete schema.if
delete schema.then
delete schema.else
const schemaKey = i.$id || randomUUID()
ajvInstance.addSchema(i, schemaKey)
const ifLocation = mergeLocation(location, 'if')
const ifSchemaRef = ifLocation.schemaId + ifLocation.jsonPointer
code += `
valid = ajv.validate("${schemaKey}", obj)
if (valid) {
let code = `
if (ajv.validate("${ifSchemaRef}", obj)) {
`
if (merged.if && merged.then) {
code += addIfThenElse(mergedLocation, locationPath + 'Then')
}
code += buildInnerObject(mergedLocation, locationPath + 'Then')
const thenLocation = mergeLocation(location, 'then')
thenLocation.schema = merge(schema, thenSchema)
if (thenSchema.if && thenSchema.then) {
code += addIfThenElse(thenLocation)
}
code += buildInnerObject(thenLocation)
code += `
}
`
merged = merge(copy, e)
mergedLocation = mergeLocation(mergedLocation, { schema: merged })
const elseLocation = mergeLocation(location, 'else')
elseLocation.schema = merge(schema, elseSchema)
code += `

@@ -649,8 +554,6 @@ else {

if (merged.if && merged.then) {
code += addIfThenElse(mergedLocation, locationPath + 'Else')
if (elseSchema.if && elseSchema.then) {
code += addIfThenElse(elseLocation)
}
code += buildInnerObject(mergedLocation, locationPath + 'Else')
code += buildInnerObject(elseLocation)
code += `

@@ -669,7 +572,4 @@ }

function buildObject (location, locationPath) {
function buildObject (location) {
const schema = location.schema
if (schema.$id !== undefined) {
schemaReferenceMap.set(schema.$id, schema)
}

@@ -683,5 +583,6 @@ if (objectReferenceSerializersMap.has(schema)) {

const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
let functionCode = `
function ${functionName} (input) {
// ${locationPath}
// ${schemaId + location.jsonPointer}
`

@@ -704,8 +605,5 @@ if (schema.nullable) {

if (schema.if && schema.then) {
functionCode += `
var valid
`
rCode = addIfThenElse(location, locationPath)
rCode = addIfThenElse(location)
} else {
rCode = buildInnerObject(location, locationPath)
rCode = buildInnerObject(location)
}

@@ -724,7 +622,4 @@

function buildArray (location, locationPath) {
function buildArray (location) {
let schema = location.schema
if (schema.$id !== undefined) {
schemaReferenceMap.set(schema.$id, schema)
}

@@ -736,2 +631,4 @@ // default to any items type

let itemsLocation = mergeLocation(location, 'items')
if (schema.items.$ref) {

@@ -744,3 +641,4 @@ if (!schema[fjsCloned]) {

location = refFinder(schema.items.$ref, location)
location = resolveRef(location, schema.items.$ref)
itemsLocation = location
schema.items = location.schema

@@ -756,5 +654,6 @@ }

const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId
let functionCode = `
function ${functionName} (obj) {
// ${locationPath}
// ${schemaId + location.jsonPointer}
`

@@ -797,7 +696,6 @@

const accessor = '[i]'
if (Array.isArray(schema.items)) {
for (let i = 0; i < schema.items.length; i++) {
const item = schema.items[i]
const tmpRes = buildValue(locationPath + accessor + i, `obj[${i}]`, mergeLocation(location, { schema: item }))
const tmpRes = buildValue(mergeLocation(itemsLocation, i), `obj[${i}]`)
functionCode += `

@@ -830,3 +728,3 @@ if (${i} < arrayLength) {

} else {
const code = buildValue(locationPath + accessor, 'obj[i]', mergeLocation(location, { schema: schema.items }))
const code = buildValue(itemsLocation, 'obj[i]')
functionCode += `

@@ -888,26 +786,2 @@ for (let i = 0; i < arrayLength; i++) {

function dereferenceOfRefs (location, type) {
if (!location.schema[fjsCloned]) {
const schemaClone = clone(location.schema)
schemaClone[fjsCloned] = true
location.schema = schemaClone
}
const schema = location.schema
const locations = []
schema[type].forEach((s, index) => {
// follow the refs
let sLocation = mergeLocation(location, { schema: s })
while (s.$ref) {
sLocation = refFinder(s.$ref, sLocation)
schema[type][index] = sLocation.schema
s = schema[type][index]
}
locations[index] = sLocation
})
return locations
}
let genFuncNameCounter = 0

@@ -918,7 +792,8 @@ function generateFuncName () {

function buildValue (locationPath, input, location) {
function buildValue (location, input) {
let schema = location.schema
if (schema.$ref) {
schema = refFinder(schema.$ref, location)
location = resolveRef(location, schema.$ref)
schema = location.schema
}

@@ -970,37 +845,28 @@

case 'object':
funcName = buildObject(location, locationPath)
funcName = buildObject(location)
code += `json += ${funcName}(${input})`
break
case 'array':
funcName = buildArray(location, locationPath)
funcName = buildArray(location)
code += `json += ${funcName}(${input})`
break
case undefined:
if (schema.anyOf || schema.oneOf) {
if (schema.fjs_date_type) {
funcName = getStringSerializer(schema.fjs_date_type, nullable)
code += `json += ${funcName}(${input})`
break
} else if (schema.anyOf || schema.oneOf) {
// beware: dereferenceOfRefs has side effects and changes schema.anyOf
const locations = dereferenceOfRefs(location, schema.anyOf ? 'anyOf' : 'oneOf')
locations.forEach((location, index) => {
const nestedResult = buildValue(locationPath + 'i' + index, input, location)
// Since we are only passing the relevant schema to ajv.validate, it needs to be full dereferenced
// otherwise any $ref pointing to an external schema would result in an error.
// Full dereference of the schema happens as side effect of two functions:
// 1. `dereferenceOfRefs` loops through the `schema.anyOf`` array and replaces any top level reference
// with the actual schema
// 2. `buildValue`, through `buildCode`, replaces any reference in object properties with the actual schema
// (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
const type = schema.anyOf ? 'anyOf' : 'oneOf'
const anyOfLocation = mergeLocation(location, type)
// Ajv does not support js date format. In order to properly validate objects containing a date,
// it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
// (see https://github.com/fastify/fast-json-stringify/pull/441)
const extendedSchema = clone(location.schema)
extendDateTimeType(extendedSchema)
const schemaKey = location.schema.$id || randomUUID()
ajvInstance.addSchema(extendedSchema, schemaKey)
for (let index = 0; index < location.schema[type].length; index++) {
const optionLocation = mergeLocation(anyOfLocation, index)
const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
const nestedResult = buildValue(optionLocation, input)
code += `
${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaKey}", ${input}))
${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaRef}", ${input}))
${nestedResult}
`
})
}

@@ -1042,6 +908,7 @@ code += `

const locationClone = clone(location)
sortedTypes.forEach((type, index) => {
const statement = index === 0 ? 'if' : 'else if'
const tempSchema = Object.assign({}, schema, { type })
const nestedResult = buildValue(locationPath, input, mergeLocation(location, { schema: tempSchema }))
locationClone.schema.type = type
const nestedResult = buildValue(locationClone, input)
switch (type) {

@@ -1095,3 +962,8 @@ case 'string': {

// Ajv does not support js date format. In order to properly validate objects containing a date,
// it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
// (see https://github.com/fastify/fast-json-stringify/pull/441)
function extendDateTimeType (schema) {
if (schema === null) return
if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) {

@@ -1098,0 +970,0 @@ schema.fjs_date_type = schema.format

{
"name": "fast-json-stringify",
"version": "4.2.0",
"version": "5.0.0",
"description": "Stringify your JSON at max speed",

@@ -40,3 +40,3 @@ "main": "index.js",

"compile-json-stringify": "^0.1.2",
"inquirer": "^8.2.4",
"inquirer": "^9.0.0",
"is-my-json-valid": "^2.20.0",

@@ -57,9 +57,5 @@ "luxon": "^2.4.0",

"deepmerge": "^4.2.2",
"fast-uri": "^2.0.0",
"rfdc": "^1.2.0",
"string-similarity": "^4.0.1"
"fast-uri": "^2.1.0",
"rfdc": "^1.2.0"
},
"engines": {
"node": ">= 10.0.0"
},
"standard": {

@@ -66,0 +62,0 @@ "ignore": [

@@ -70,40 +70,40 @@ 'use strict'

asDatetime (date, skipQuotes) {
const quotes = skipQuotes === true ? '' : '"'
asDatetime (date) {
const quotes = '"'
if (date instanceof Date) {
return quotes + date.toISOString() + quotes
}
return this.asString(date, skipQuotes)
return this.asString(date)
}
asDatetimeNullable (date, skipQuotes) {
return date === null ? 'null' : this.asDatetime(date, skipQuotes)
asDatetimeNullable (date) {
return date === null ? 'null' : this.asDatetime(date)
}
asDate (date, skipQuotes) {
const quotes = skipQuotes === true ? '' : '"'
asDate (date) {
const quotes = '"'
if (date instanceof Date) {
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + quotes
}
return this.asString(date, skipQuotes)
return this.asString(date)
}
asDateNullable (date, skipQuotes) {
return date === null ? 'null' : this.asDate(date, skipQuotes)
asDateNullable (date) {
return date === null ? 'null' : this.asDate(date)
}
asTime (date, skipQuotes) {
const quotes = skipQuotes === true ? '' : '"'
asTime (date) {
const quotes = '"'
if (date instanceof Date) {
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + quotes
}
return this.asString(date, skipQuotes)
return this.asString(date)
}
asTimeNullable (date, skipQuotes) {
return date === null ? 'null' : this.asTime(date, skipQuotes)
asTimeNullable (date) {
return date === null ? 'null' : this.asTime(date)
}
asString (str, skipQuotes) {
const quotes = skipQuotes === true ? '' : '"'
asString (str) {
const quotes = '"'
if (str instanceof Date) {

@@ -118,7 +118,2 @@ return quotes + str.toISOString() + quotes

}
// If we skipQuotes it means that we are using it as test
// no need to test the string length for the render
if (skipQuotes) {
return str
}

@@ -125,0 +120,0 @@ if (str.length < 42) {

@@ -405,8 +405,10 @@ 'use strict'

second: {
id2: {
$id: '#id2',
type: 'object',
properties: {
id2: {
type: 'integer'
definitions: {
id2: {
$id: '#id2',
type: 'object',
properties: {
id2: {
type: 'integer'
}
}

@@ -426,3 +428,3 @@ }

{
$ref: 'second#id2'
$ref: 'second#/definitions/id2'
}

@@ -429,0 +431,0 @@ ]

@@ -434,6 +434,6 @@ 'use strict'

first: {
$ref: '#first-schema'
$ref: 'first#first-schema'
},
second: {
$ref: '#second-schema'
$ref: 'second#second-schema'
}

@@ -461,2 +461,207 @@ }

test('external reference to $id', (t) => {
t.plan(2)
const externalSchema = {
first: {
$id: 'external-reference',
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
}
const schema = {
type: 'object',
properties: {
first: {
$ref: 'external-reference'
}
}
}
const object = { first: { str: 'test' } }
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"first":{"str":"test"}}')
})
test('external reference to key#id', (t) => {
t.plan(2)
const externalSchema = {
first: {
$id: '#external-reference',
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
}
const schema = {
type: 'object',
properties: {
first: {
$ref: 'first#external-reference'
}
}
}
const object = { first: { str: 'test' } }
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"first":{"str":"test"}}')
})
test('external and inner reference', (t) => {
t.plan(2)
const externalSchema = {
first: {
$id: 'reference',
$ref: '#reference',
definitions: {
inner: {
$id: '#reference',
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
}
}
}
const schema = {
type: 'object',
properties: {
first: {
$ref: 'reference'
}
}
}
const object = { first: { str: 'test' } }
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"first":{"str":"test"}}')
})
test('external reference to key', (t) => {
t.plan(2)
const externalSchema = {
first: {
$id: 'external-reference',
type: 'object',
properties: {
str: {
type: 'string'
}
}
}
}
const schema = {
type: 'object',
properties: {
first: {
$ref: 'external-reference'
}
}
}
const object = { first: { str: 'test' } }
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"first":{"str":"test"}}')
})
test('ref external - plain name fragment', (t) => {
t.plan(2)
const externalSchema = {
first: {
$id: 'first-schema',
type: 'object',
properties: {
str: {
type: 'string'
}
}
},
second: {
definitions: {
second: {
$id: 'second-schema',
type: 'object',
properties: {
int: {
type: 'integer'
}
}
}
}
}
}
const schema = {
title: 'object with $ref to external plain name fragment',
type: 'object',
properties: {
first: {
$ref: 'first-schema'
},
second: {
$ref: 'second-schema'
}
}
}
const object = {
first: {
str: 'test'
},
second: {
int: 42
}
}
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"first":{"str":"test"},"second":{"int":42}}')
})
test('ref external - duplicate plain name fragment', (t) => {

@@ -508,3 +713,3 @@ t.plan(2)

other: {
$ref: '#otherSchema'
$ref: 'other#otherSchema'
}

@@ -899,3 +1104,3 @@ }

type: 'object',
$ref: 'numbers#/definitions/num'
$ref: 'numbers'
}

@@ -985,28 +1190,2 @@

test('ref in definition with exact match', (t) => {
t.plan(2)
const externalSchema = {
'#/definitions/foo': {
type: 'string'
}
}
const schema = {
type: 'object',
properties: {
foo: { $ref: '#/definitions/foo' }
}
}
const object = { foo: 'foo' }
const stringify = build(schema, { schema: externalSchema })
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, '{"foo":"foo"}')
})
test('Bad key', t => {

@@ -1034,3 +1213,3 @@ t.test('Find match', t => {

} catch (err) {
t.equal(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
t.equal(err.message, 'Cannot find reference "#/definitions/porjectId"')
}

@@ -1060,3 +1239,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "foobar"')
t.equal(err.message, 'Cannot find reference "#/definitions/foobar"')
}

@@ -1091,3 +1270,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "porjectId", did you mean "projectId"?')
t.equal(err.message, 'Cannot find reference "external#/definitions/porjectId"')
}

@@ -1122,3 +1301,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "foobar"')
t.equal(err.message, 'Cannot find reference "external#/definitions/foobar"')
}

@@ -1153,3 +1332,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
t.equal(err.message, 'Cannot find reference "external#/deifnitions/projectId"')
}

@@ -1179,3 +1358,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "deifnitions", did you mean "definitions"?')
t.equal(err.message, 'Cannot find reference "#/deifnitions/projectId"')
}

@@ -1210,3 +1389,3 @@ })

} catch (err) {
t.equal(err.message, 'Cannot find reference "extrenal", did you mean "external"?')
t.equal(err.message, 'Cannot find reference "extrenal#/definitions/projectId"')
}

@@ -1264,1 +1443,395 @@ })

})
test('Reference through multiple definitions', (t) => {
t.plan(2)
const schema = {
$ref: '#/definitions/A',
definitions: {
A: {
type: 'object',
additionalProperties: false,
properties: { a: { anyOf: [{ $ref: '#/definitions/B' }] } },
required: ['a']
},
B: {
type: 'object',
properties: { b: { anyOf: [{ $ref: '#/definitions/C' }] } },
required: ['b'],
additionalProperties: false
},
C: {
type: 'object',
properties: { c: { type: 'string', const: 'd' } },
required: ['c'],
additionalProperties: false
}
}
}
const object = { a: { b: { c: 'd' } } }
const stringify = build(schema)
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, JSON.stringify(object))
})
test('issue #350', (t) => {
t.plan(2)
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
firstName: { $ref: '#foo' },
lastName: { $ref: '#foo' },
nested: {
type: 'object',
properties: {
firstName: { $ref: '#foo' },
lastName: { $ref: '#foo' }
}
}
},
definitions: {
foo: {
$id: '#foo',
type: 'string'
}
}
}
const object = {
firstName: 'Matteo',
lastName: 'Collina',
nested: {
firstName: 'Matteo',
lastName: 'Collina'
}
}
const stringify = build(schema)
const output = stringify(object)
JSON.parse(output)
t.pass()
t.equal(output, JSON.stringify(object))
})
test('deep union type', (t) => {
t.plan(1)
const stringify = build({
schema: {
type: 'array',
items: {
oneOf: [
{
$ref: 'components#/schemas/IDirectory'
},
{
$ref: 'components#/schemas/IImageFile'
},
{
$ref: 'components#/schemas/ITextFile'
},
{
$ref: 'components#/schemas/IZipFile'
}
]
},
nullable: false
},
components: {
schemas: {
IDirectory: {
$id: 'IDirectory',
$recursiveAnchor: true,
type: 'object',
properties: {
children: {
type: 'array',
items: {
oneOf: [
{
$recursiveRef: '#'
},
{
$ref: 'components#/schemas/IImageFile'
},
{
$ref: 'components#/schemas/ITextFile'
},
{
$ref: 'components#/schemas/IZipFile'
}
]
},
nullable: false
},
type: {
type: 'string',
nullable: false
},
id: {
type: 'string',
nullable: false
},
name: {
type: 'string',
nullable: false
}
},
nullable: false,
required: [
'children',
'type',
'id',
'name'
]
},
IImageFile: {
$id: 'IImageFile',
type: 'object',
properties: {
width: {
type: 'number',
nullable: false
},
height: {
type: 'number',
nullable: false
},
url: {
type: 'string',
nullable: false
},
extension: {
type: 'string',
nullable: false
},
size: {
type: 'number',
nullable: false
},
type: {
type: 'string',
nullable: false
},
id: {
type: 'string',
nullable: false
},
name: {
type: 'string',
nullable: false
}
},
nullable: false,
required: [
'width',
'height',
'url',
'extension',
'size',
'type',
'id',
'name'
]
},
ITextFile: {
$id: 'ITextFile',
type: 'object',
properties: {
content: {
type: 'string',
nullable: false
},
extension: {
type: 'string',
nullable: false
},
size: {
type: 'number',
nullable: false
},
type: {
type: 'string',
nullable: false
},
id: {
type: 'string',
nullable: false
},
name: {
type: 'string',
nullable: false
}
},
nullable: false,
required: [
'content',
'extension',
'size',
'type',
'id',
'name'
]
},
IZipFile: {
$id: 'IZipFile',
type: 'object',
properties: {
files: {
type: 'number',
nullable: false
},
extension: {
type: 'string',
nullable: false
},
size: {
type: 'number',
nullable: false
},
type: {
type: 'string',
nullable: false
},
id: {
type: 'string',
nullable: false
},
name: {
type: 'string',
nullable: false
}
},
nullable: false,
required: [
'files',
'extension',
'size',
'type',
'id',
'name'
]
}
}
}
})
const obj = [
{
type: 'directory',
id: '7b1068a4-dd6e-474a-8d85-09a2d77639cb',
name: 'ixcWGOKI',
children: [
{
type: 'directory',
id: '5883e17c-b207-46d4-ad2d-be72249711ce',
name: 'vecQwFGS',
children: []
},
{
type: 'file',
id: '670b6556-a610-4a48-8a16-9c2da97a0d18',
name: 'eStFddzX',
extension: 'jpg',
size: 7,
width: 300,
height: 1200,
url: 'https://github.com/samchon/typescript-json'
},
{
type: 'file',
id: '85dc796d-9593-4833-b1a1-addc8ebf74ea',
name: 'kTdUfwRJ',
extension: 'ts',
size: 86,
content: 'console.log("Hello world");'
},
{
type: 'file',
id: '8933c86a-7a1e-4d4a-b0a6-17d6896fdf89',
name: 'NBPkefUG',
extension: 'zip',
size: 22,
files: 20
}
]
}
]
t.equal(JSON.stringify(obj), stringify(obj))
})
test('ref with same id in properties', (t) => {
t.plan(2)
const externalSchema = {
ObjectId: {
$id: 'ObjectId',
type: 'string'
},
File: {
$id: 'File',
type: 'object',
properties: {
_id: { $ref: 'ObjectId' },
name: { type: 'string' },
owner: { $ref: 'ObjectId' }
}
}
}
t.test('anyOf', (t) => {
t.plan(1)
const schema = {
$id: 'Article',
type: 'object',
properties: {
_id: { $ref: 'ObjectId' },
image: {
anyOf: [
{ $ref: 'File' },
{ type: 'null' }
]
}
}
}
const stringify = build(schema, { schema: externalSchema })
const output = stringify({ _id: 'foo', image: { _id: 'bar', name: 'hello', owner: 'baz' } })
t.equal(output, '{"_id":"foo","image":{"_id":"bar","name":"hello","owner":"baz"}}')
})
t.test('oneOf', (t) => {
t.plan(1)
const schema = {
$id: 'Article',
type: 'object',
properties: {
_id: { $ref: 'ObjectId' },
image: {
oneOf: [
{ $ref: 'File' },
{ type: 'null' }
]
}
}
}
const stringify = build(schema, { schema: externalSchema })
const output = stringify({ _id: 'foo', image: { _id: 'bar', name: 'hello', owner: 'baz' } })
t.equal(output, '{"_id":"foo","image":{"_id":"bar","name":"hello","owner":"baz"}}')
})
})
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