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 5.2.0 to 5.3.0

build/build-schema-validator.js

17

index.d.ts
import Ajv, { Options as AjvOptions } from "ajv"
declare namespace build {

@@ -192,11 +193,11 @@ interface BaseSchema {

declare function build(schema: build.AnySchema, options: StandaloneOption): string;
declare function build(schema: build.AnySchema, options?: build.Options): (doc: any) => any;
declare function build(schema: build.StringSchema, options?: build.Options): (doc: string) => string;
declare function build(schema: build.IntegerSchema | build.NumberSchema, options?: build.Options): (doc: number) => string;
declare function build(schema: build.NullSchema, options?: build.Options): (doc: null) => "null";
declare function build(schema: build.BooleanSchema, options?: build.Options): (doc: boolean) => string;
declare function build(schema: build.ArraySchema | build.TupleSchema, options?: build.Options): (doc: any[]) => string;
declare function build(schema: build.ObjectSchema, options?: build.Options): (doc: object) => string;
declare function build(schema: build.Schema, options?: build.Options): (doc: object | any[] | string | number | boolean | null) => string;
declare function build(schema: build.AnySchema, options?: build.Options): <TDoc = any>(doc: TDoc) => any;
declare function build(schema: build.StringSchema, options?: build.Options): <TDoc extends string = string>(doc: TDoc) => string;
declare function build(schema: build.IntegerSchema | build.NumberSchema, options?: build.Options): <TDoc extends number = number>(doc: TDoc) => string;
declare function build(schema: build.NullSchema, options?: build.Options): <TDoc extends null = null>(doc: TDoc) => "null";
declare function build(schema: build.BooleanSchema, options?: build.Options): <TDoc extends boolean = boolean>(doc: TDoc) => string;
declare function build(schema: build.ArraySchema | build.TupleSchema, options?: build.Options): <TDoc extends any[]= any[]>(doc: TDoc) => string;
declare function build(schema: build.ObjectSchema, options?: build.Options): <TDoc extends object = object>(doc: TDoc) => string;
declare function build(schema: build.Schema, options?: build.Options): <TDoc = object | any[] | string | number | boolean | null> (doc: TDoc) => string;
export = build;

@@ -7,9 +7,8 @@ 'use strict'

const clone = require('rfdc')({ proto: true })
const fjsCloned = Symbol('fast-json-stringify.cloned')
const { randomUUID } = require('crypto')
const validate = require('./schema-validator')
const Serializer = require('./serializer')
const Validator = require('./validator')
const RefResolver = require('./ref-resolver')
const validate = require('./lib/schema-validator')
const Serializer = require('./lib/serializer')
const Validator = require('./lib/validator')
const RefResolver = require('./lib/ref-resolver')

@@ -75,4 +74,3 @@ let largeArraySize = 2e4

const arrayItemsReferenceSerializersMap = new Map()
const objectReferenceSerializersMap = new Map()
const contextFunctionsNamesBySchema = new Map()

@@ -85,4 +83,3 @@ let rootSchemaId = null

function build (schema, options) {
arrayItemsReferenceSerializersMap.clear()
objectReferenceSerializersMap.clear()
contextFunctionsNamesBySchema.clear()

@@ -153,3 +150,8 @@ contextFunctions = []

if (options.mode === 'debug') {
return { code: dependenciesName.join('\n'), validator, ajv: validator.ajv }
return {
validator,
serializer,
code: dependenciesName.join('\n'),
ajv: validator.ajv
}
}

@@ -159,3 +161,3 @@

// lazy load
const buildStandaloneCode = require('./standalone')
const buildStandaloneCode = require('./lib/standalone')
return buildStandaloneCode(options, validator, contextFunctionCode)

@@ -172,4 +174,3 @@ }

contextFunctions = null
arrayItemsReferenceSerializersMap.clear()
objectReferenceSerializersMap.clear()
contextFunctionsNamesBySchema.clear()

@@ -547,8 +548,8 @@ return stringifyFunc

if (objectReferenceSerializersMap.has(schema)) {
return objectReferenceSerializersMap.get(schema)
if (contextFunctionsNamesBySchema.has(schema)) {
return contextFunctionsNamesBySchema.get(schema)
}
const functionName = generateFuncName()
objectReferenceSerializersMap.set(schema, functionName)
contextFunctionsNamesBySchema.set(schema, functionName)

@@ -560,9 +561,2 @@ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId

`
if (schema.nullable) {
functionCode += `
if (input === null) {
return 'null';
}
`
}

@@ -592,29 +586,19 @@ functionCode += `

function buildArray (location) {
let schema = location.schema
const schema = location.schema
// default to any items type
if (!schema.items) {
schema.items = {}
}
let itemsLocation = mergeLocation(location, 'items')
itemsLocation.schema = itemsLocation.schema || {}
if (schema.items.$ref) {
if (!schema[fjsCloned]) {
location.schema = clone(location.schema)
schema = location.schema
schema[fjsCloned] = true
}
location = resolveRef(location, schema.items.$ref)
itemsLocation = location
schema.items = location.schema
if (itemsLocation.schema.$ref) {
itemsLocation = resolveRef(itemsLocation, itemsLocation.schema.$ref)
}
if (arrayItemsReferenceSerializersMap.has(schema.items)) {
return arrayItemsReferenceSerializersMap.get(schema.items)
const itemsSchema = itemsLocation.schema
if (contextFunctionsNamesBySchema.has(schema)) {
return contextFunctionsNamesBySchema.get(schema)
}
const functionName = generateFuncName()
arrayItemsReferenceSerializersMap.set(schema.items, functionName)
contextFunctionsNamesBySchema.set(schema, functionName)

@@ -627,10 +611,2 @@ const schemaId = location.schemaId === rootSchemaId ? '' : location.schemaId

if (schema.nullable) {
functionCode += `
if (obj === null) {
return 'null';
}
`
}
functionCode += `

@@ -645,4 +621,4 @@ if (!Array.isArray(obj)) {

functionCode += `
if (arrayLength > ${schema.items.length}) {
throw new Error(\`Item at ${schema.items.length} does not match schema definition.\`)
if (arrayLength > ${itemsSchema.length}) {
throw new Error(\`Item at ${itemsSchema.length} does not match schema definition.\`)
}

@@ -664,5 +640,5 @@ `

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

@@ -687,3 +663,3 @@ functionCode += `

functionCode += `
for (let i = ${schema.items.length}; i < arrayLength; i++) {
for (let i = ${itemsSchema.length}; i < arrayLength; i++) {
let json = JSON.stringify(obj[i])

@@ -759,2 +735,133 @@ jsonOutput += json

function buildMultiTypeSerializer (location, input) {
const schema = location.schema
const types = schema.type.sort(t1 => t1 === 'null' ? -1 : 1)
let code = ''
const locationClone = clone(location)
types.forEach((type, index) => {
const statement = index === 0 ? 'if' : 'else if'
locationClone.schema.type = type
const nestedResult = buildSingleTypeSerializer(locationClone, input)
switch (type) {
case 'null':
code += `
${statement} (${input} === null)
${nestedResult}
`
break
case 'string': {
code += `
${statement}(
typeof ${input} === "string" ||
${input} === null ||
${input} instanceof Date ||
${input} instanceof RegExp ||
(
typeof ${input} === "object" &&
typeof ${input}.toString === "function" &&
${input}.toString !== Object.prototype.toString &&
!(${input} instanceof Date)
)
)
${nestedResult}
`
break
}
case 'array': {
code += `
${statement}(Array.isArray(${input}))
${nestedResult}
`
break
}
case 'integer': {
code += `
${statement}(Number.isInteger(${input}) || ${input} === null)
${nestedResult}
`
break
}
default: {
code += `
${statement}(typeof ${input} === "${type}" || ${input} === null)
${nestedResult}
`
break
}
}
})
code += `
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
`
return code
}
function buildSingleTypeSerializer (location, input) {
const schema = location.schema
switch (schema.type) {
case 'null':
return 'json += \'null\''
case 'string': {
if (schema.format === 'date-time') {
return `json += serializer.asDateTime(${input})`
} else if (schema.format === 'date') {
return `json += serializer.asDate(${input})`
} else if (schema.format === 'time') {
return `json += serializer.asTime(${input})`
} else {
return `json += serializer.asString(${input})`
}
}
case 'integer':
return `json += serializer.asInteger(${input})`
case 'number':
return `json += serializer.asNumber(${input})`
case 'boolean':
return `json += serializer.asBoolean(${input})`
case 'object': {
const funcName = buildObject(location)
return `json += ${funcName}(${input})`
}
case 'array': {
const funcName = buildArray(location)
return `json += ${funcName}(${input})`
}
case undefined:
return `json += JSON.stringify(${input})`
default:
throw new Error(`${schema.type} unsupported`)
}
}
function buildConstSerializer (location, input) {
const schema = location.schema
const type = schema.type
const hasNullType = Array.isArray(type) && type.includes('null')
let code = ''
if (hasNullType) {
code += `
if (${input} === null) {
json += 'null'
} else {
`
}
code += `json += '${JSON.stringify(schema.const)}'`
if (hasNullType) {
code += `
}
`
}
return code
}
function buildValue (location, input) {

@@ -787,163 +894,46 @@ let schema = location.schema

const type = schema.type
const nullable = schema.nullable === true || (Array.isArray(type) && type.includes('null'))
let code = ''
let funcName
if ('const' in schema) {
if (nullable) {
if (type === undefined && (schema.anyOf || schema.oneOf)) {
const type = schema.anyOf ? 'anyOf' : 'oneOf'
const anyOfLocation = mergeLocation(location, type)
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 += `
json += ${input} === null ? 'null' : '${JSON.stringify(schema.const)}'
${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
${nestedResult}
`
return code
}
code += `json += '${JSON.stringify(schema.const)}'`
code += `
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
`
return code
}
switch (type) {
case 'null':
code += 'json += serializer.asNull()'
break
case 'string': {
if (schema.format === 'date-time') {
funcName = nullable ? 'serializer.asDateTimeNullable.bind(serializer)' : 'serializer.asDateTime.bind(serializer)'
} else if (schema.format === 'date') {
funcName = nullable ? 'serializer.asDateNullable.bind(serializer)' : 'serializer.asDate.bind(serializer)'
} else if (schema.format === 'time') {
funcName = nullable ? 'serializer.asTimeNullable.bind(serializer)' : 'serializer.asTime.bind(serializer)'
const nullable = schema.nullable === true
if (nullable) {
code += `
if (${input} === null) {
json += 'null'
} else {
funcName = nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)'
}
code += `json += ${funcName}(${input})`
break
}
case 'integer':
funcName = nullable ? 'serializer.asIntegerNullable.bind(serializer)' : 'serializer.asInteger.bind(serializer)'
code += `json += ${funcName}(${input})`
break
case 'number':
funcName = nullable ? 'serializer.asNumberNullable.bind(serializer)' : 'serializer.asNumber.bind(serializer)'
code += `json += ${funcName}(${input})`
break
case 'boolean':
funcName = nullable ? 'serializer.asBooleanNullable.bind(serializer)' : 'serializer.asBoolean.bind(serializer)'
code += `json += ${funcName}(${input})`
break
case 'object':
funcName = buildObject(location)
code += `json += ${funcName}(${input})`
break
case 'array':
funcName = buildArray(location)
code += `json += ${funcName}(${input})`
break
case undefined:
if (schema.anyOf || schema.oneOf) {
// beware: dereferenceOfRefs has side effects and changes schema.anyOf
const type = schema.anyOf ? 'anyOf' : 'oneOf'
const anyOfLocation = mergeLocation(location, type)
`
}
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'}(validator.validate("${schemaRef}", ${input}))
${nestedResult}
`
}
if (schema.const !== undefined) {
code += buildConstSerializer(location, input)
} else if (Array.isArray(type)) {
code += buildMultiTypeSerializer(location, input)
} else {
code += buildSingleTypeSerializer(location, input)
}
code += `
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
`
} else if (isEmpty(schema)) {
code += `
json += JSON.stringify(${input})
`
} else {
code += `
json += JSON.stringify(${input})
`
if (nullable) {
code += `
}
break
default:
if (Array.isArray(type)) {
let sortedTypes = type
const nullable = schema.nullable === true || type.includes('null')
if (nullable) {
sortedTypes = sortedTypes.filter(type => type !== 'null')
code += `
if (${input} === null) {
json += null
} else {`
}
const locationClone = clone(location)
sortedTypes.forEach((type, index) => {
const statement = index === 0 ? 'if' : 'else if'
locationClone.schema.type = type
const nestedResult = buildValue(locationClone, input)
switch (type) {
case 'string': {
code += `
${statement}(
typeof ${input} === "string" ||
${input} === null ||
${input} instanceof Date ||
${input} instanceof RegExp ||
(
typeof ${input} === "object" &&
typeof ${input}.toString === "function" &&
${input}.toString !== Object.prototype.toString &&
!(${input} instanceof Date)
)
)
${nestedResult}
`
break
}
case 'array': {
code += `
${statement}(Array.isArray(${input}))
${nestedResult}
`
break
}
case 'integer': {
code += `
${statement}(Number.isInteger(${input}) || ${input} === null)
${nestedResult}
`
break
}
case 'object': {
code += `
${statement}(typeof ${input} === "object" || ${input} === null)
${nestedResult}
`
break
}
default: {
code += `
${statement}(typeof ${input} === "${type}" || ${input} === null)
${nestedResult}
`
break
}
}
})
code += `
else throw new Error(\`The value $\{JSON.stringify(${input})} does not match schema definition.\`)
`
if (nullable) {
code += `
}
`
}
} else {
throw new Error(`${type} unsupported`)
}
`
}

@@ -954,12 +944,2 @@

function isEmpty (schema) {
// eslint-disable-next-line
for (var key in schema) {
if (Object.prototype.hasOwnProperty.call(schema, key) && schema[key] !== undefined) {
return false
}
}
return true
}
module.exports = build

@@ -969,4 +949,3 @@

module.exports.restore = function ({ code, validator }) {
const serializer = new Serializer()
module.exports.restore = function ({ code, validator, serializer }) {
// eslint-disable-next-line

@@ -973,0 +952,0 @@ return (Function.apply(null, ['validator', 'serializer', code])

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

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

"test:lint": "standard",
"test:typescript": "tsc --project ./test/types/tsconfig.json",
"test:typescript": "tsc --project ./test/types/tsconfig.json && tsd",
"test:unit": "tap -J test/*.test.js test/**/*.test.js",

@@ -49,2 +49,3 @@ "test": "npm run test:lint && npm run test:unit && npm run test:typescript"

"tap": "^16.0.1",
"tsd": "^0.22.0",
"typescript": "^4.0.2",

@@ -66,3 +67,6 @@ "webpack": "^5.40.0"

},
"runkitExampleFilename": "example.js"
"runkitExampleFilename": "./examples/example.js",
"tsd": {
"directory": "test/types"
}
}

@@ -330,2 +330,27 @@ 'use strict'

test('different arrays with same item schemas', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
array1: {
type: 'array',
items: [{ type: 'string' }],
additionalItems: false
},
array2: {
type: 'array',
items: { $ref: '#/properties/array1/items' },
additionalItems: true
}
}
}
const stringify = build(schema)
const data = { array1: ['bar'], array2: ['foo', 'bar'] }
t.equal(stringify(data), '{"array1":["bar"],"array2":["foo","bar"]}')
})
const largeArray = new Array(2e4).fill({ a: 'test', b: 1 })

@@ -332,0 +357,0 @@ buildTest({

@@ -528,1 +528,20 @@ 'use strict'

})
test('non-date format should not affect data serialization (issue #491)', (t) => {
t.plan(1)
const schema = {
type: 'object',
properties: {
hello: {
type: 'string',
format: 'int64',
pattern: '^[0-9]*$'
}
}
}
const stringify = build(schema)
const data = { hello: 123n }
t.equal(stringify(data), '{"hello":"123"}')
})

@@ -7,3 +7,4 @@ 'use strict'

const Ajv = require('ajv').default
const Validator = require('../validator')
const Validator = require('../lib/validator')
const Serializer = require('../lib/serializer')

@@ -24,3 +25,3 @@ function build (opts) {

test('activate debug mode', t => {
t.plan(4)
t.plan(5)
const debugMode = build({ debugMode: true })

@@ -31,2 +32,3 @@

t.ok(debugMode.validator instanceof Validator)
t.ok(debugMode.serializer instanceof Serializer)
t.type(debugMode.code, 'string')

@@ -36,3 +38,3 @@ })

test('activate debug mode truthy', t => {
t.plan(4)
t.plan(5)

@@ -45,6 +47,7 @@ const debugMode = build({ debugMode: 'yes' })

t.ok(debugMode.validator instanceof Validator)
t.ok(debugMode.serializer instanceof Serializer)
})
test('to string auto-consistent', t => {
t.plan(5)
t.plan(6)
const debugMode = build({ debugMode: 1 })

@@ -55,2 +58,3 @@

t.ok(debugMode.ajv instanceof Ajv)
t.ok(debugMode.serializer instanceof Serializer)
t.ok(debugMode.validator instanceof Validator)

@@ -64,3 +68,3 @@

test('to string auto-consistent with ajv', t => {
t.plan(5)
t.plan(6)

@@ -85,2 +89,3 @@ const debugMode = fjs({

t.ok(debugMode.validator instanceof Validator)
t.ok(debugMode.serializer instanceof Serializer)

@@ -117,1 +122,9 @@ const compiled = fjs.restore(debugMode)

})
test('debug should restore the same serializer instance', t => {
t.plan(1)
const debugMode = fjs({ type: 'integer' }, { debugMode: 1, rounding: 'ceil' })
const compiled = fjs.restore(debugMode)
t.same(compiled(3.95), 4)
})
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