fast-json-stringify
Advanced tools
Comparing version 5.0.6 to 5.1.0
33
ajv.js
@@ -10,25 +10,18 @@ 'use strict' | ||
function buildAjv (options) { | ||
const ajvInstance = new Ajv({ ...options, strictSchema: false, validateSchema: false, uriResolver: fastUri }) | ||
const ajvInstance = new Ajv({ | ||
...options, | ||
strictSchema: false, | ||
validateSchema: false, | ||
allowUnionTypes: true, | ||
uriResolver: fastUri | ||
}) | ||
ajvFormats(ajvInstance) | ||
const validateDateTimeFormat = ajvFormats.get('date-time').validate | ||
const validateDateFormat = ajvFormats.get('date').validate | ||
const validateTimeFormat = ajvFormats.get('time').validate | ||
ajvInstance.addKeyword({ | ||
keyword: 'fjs_date_type', | ||
validate: (schema, date) => { | ||
if (date instanceof Date) { | ||
return true | ||
} | ||
if (schema === 'date-time') { | ||
return validateDateTimeFormat(date) | ||
} | ||
if (schema === 'date') { | ||
return validateDateFormat(date) | ||
} | ||
if (schema === 'time') { | ||
return validateTimeFormat(date) | ||
} | ||
return false | ||
keyword: 'fjs_type', | ||
type: 'object', | ||
errors: false, | ||
validate: (type, date) => { | ||
return date instanceof Date | ||
} | ||
@@ -35,0 +28,0 @@ }) |
85
index.js
@@ -5,3 +5,3 @@ 'use strict' | ||
const merge = require('deepmerge') | ||
const merge = require('@fastify/deepmerge')() | ||
const clone = require('rfdc')({ proto: true }) | ||
@@ -247,11 +247,2 @@ const fjsCloned = Symbol('fast-json-stringify.cloned') | ||
function getStringSerializer (format, nullable) { | ||
switch (format) { | ||
case 'date-time': return nullable ? 'serializer.asDatetimeNullable.bind(serializer)' : 'serializer.asDatetime.bind(serializer)' | ||
case 'date': return nullable ? 'serializer.asDateNullable.bind(serializer)' : 'serializer.asDate.bind(serializer)' | ||
case 'time': return nullable ? 'serializer.asTimeNullable.bind(serializer)' : 'serializer.asTime.bind(serializer)' | ||
default: return nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)' | ||
} | ||
} | ||
function addPatternProperties (location) { | ||
@@ -489,2 +480,12 @@ const schema = location.schema | ||
if (allOfSchema.fjs_type !== undefined) { | ||
if ( | ||
mergedSchema.fjs_type !== undefined && | ||
mergedSchema.fjs_type !== allOfSchema.fjs_type | ||
) { | ||
throw new Error('allOf schemas have different fjs_type values') | ||
} | ||
mergedSchema.fjs_type = allOfSchema.fjs_type | ||
} | ||
if (allOfSchema.allOf !== undefined) { | ||
@@ -777,2 +778,6 @@ mergeAllOfSchema(location, allOfSchema, mergedSchema) | ||
if (typeof schema === 'boolean') { | ||
return `json += JSON.stringify(${input})` | ||
} | ||
if (schema.$ref) { | ||
@@ -797,3 +802,3 @@ location = resolveRef(location, schema.$ref) | ||
const type = schema.type | ||
let type = schema.type | ||
const nullable = schema.nullable === true | ||
@@ -804,10 +809,12 @@ | ||
if (schema.fjs_type === 'string' && schema.format === undefined && Array.isArray(schema.type) && schema.type.length === 2) { | ||
type = 'string' | ||
} | ||
switch (type) { | ||
case 'null': | ||
code += ` | ||
json += serializer.asNull() | ||
` | ||
code += 'json += serializer.asNull()' | ||
break | ||
case 'string': { | ||
funcName = getStringSerializer(schema.format, nullable) | ||
funcName = nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)' | ||
code += `json += ${funcName}(${input})` | ||
@@ -829,3 +836,11 @@ break | ||
case 'object': | ||
funcName = buildObject(location) | ||
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)' | ||
} else { | ||
funcName = buildObject(location) | ||
} | ||
code += `json += ${funcName}(${input})` | ||
@@ -838,7 +853,3 @@ break | ||
case undefined: | ||
if (schema.fjs_date_type) { | ||
funcName = getStringSerializer(schema.fjs_date_type, nullable) | ||
code += `json += ${funcName}(${input})` | ||
break | ||
} else if (schema.anyOf || schema.oneOf) { | ||
if (schema.anyOf || schema.oneOf) { | ||
// beware: dereferenceOfRefs has side effects and changes schema.anyOf | ||
@@ -901,3 +912,3 @@ const type = schema.anyOf ? 'anyOf' : 'oneOf' | ||
code += ` | ||
${statement}(${input} === null || typeof ${input} === "${type}" || ${input} instanceof Date || ${input} instanceof RegExp || (typeof ${input} === "object" && Object.hasOwnProperty.call(${input}, "toString"))) | ||
${statement}(${input} === null || typeof ${input} === "${type}" || ${input} instanceof RegExp || (typeof ${input} === "object" && Object.hasOwnProperty.call(${input}, "toString"))) | ||
${nestedResult} | ||
@@ -921,2 +932,16 @@ ` | ||
} | ||
case 'object': { | ||
if (schema.fjs_type) { | ||
code += ` | ||
${statement}(${input} instanceof Date || ${input} === null) | ||
${nestedResult} | ||
` | ||
} else { | ||
code += ` | ||
${statement}(typeof ${input} === "object" || ${input} === null) | ||
${nestedResult} | ||
` | ||
} | ||
break | ||
} | ||
default: { | ||
@@ -949,3 +974,3 @@ code += ` | ||
// 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. | ||
// it needs to replace all occurrences of the string date format with a custom keyword fjs_type. | ||
// (see https://github.com/fastify/fast-json-stringify/pull/441) | ||
@@ -955,6 +980,12 @@ function extendDateTimeType (schema) { | ||
if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) { | ||
schema.fjs_date_type = schema.format | ||
delete schema.type | ||
delete schema.format | ||
if (schema.type === 'string') { | ||
schema.fjs_type = 'string' | ||
schema.type = ['string', 'object'] | ||
} else if ( | ||
Array.isArray(schema.type) && | ||
schema.type.includes('string') && | ||
!schema.type.includes('object') | ||
) { | ||
schema.fjs_type = 'string' | ||
schema.type.push('object') | ||
} | ||
@@ -961,0 +992,0 @@ for (const property in schema) { |
{ | ||
"name": "fast-json-stringify", | ||
"version": "5.0.6", | ||
"version": "5.1.0", | ||
"description": "Stringify your JSON at max speed", | ||
@@ -52,5 +52,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"@fastify/deepmerge": "^1.0.0", | ||
"ajv": "^8.10.0", | ||
"ajv-formats": "^2.1.1", | ||
"deepmerge": "^4.2.2", | ||
"fast-uri": "^2.1.0", | ||
@@ -57,0 +57,0 @@ "rfdc": "^1.2.0" |
@@ -72,20 +72,20 @@ 'use strict' | ||
asDatetime (date) { | ||
const quotes = '"' | ||
asDateTime (date) { | ||
if (date === null) return '""' | ||
if (date instanceof Date) { | ||
return quotes + date.toISOString() + quotes | ||
return '"' + date.toISOString() + '"' | ||
} | ||
return this.asString(date) | ||
throw new Error(`The value "${date}" cannot be converted to a date-time.`) | ||
} | ||
asDatetimeNullable (date) { | ||
return date === null ? 'null' : this.asDatetime(date) | ||
asDateTimeNullable (date) { | ||
return date === null ? 'null' : this.asDateTime(date) | ||
} | ||
asDate (date) { | ||
const quotes = '"' | ||
if (date === null) return '""' | ||
if (date instanceof Date) { | ||
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + quotes | ||
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + '"' | ||
} | ||
return this.asString(date) | ||
throw new Error(`The value "${date}" cannot be converted to a date.`) | ||
} | ||
@@ -98,7 +98,7 @@ | ||
asTime (date) { | ||
const quotes = '"' | ||
if (date === null) return '""' | ||
if (date instanceof Date) { | ||
return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + quotes | ||
return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + '"' | ||
} | ||
return this.asString(date) | ||
throw new Error(`The value "${date}" cannot be converted to a time.`) | ||
} | ||
@@ -105,0 +105,0 @@ |
@@ -362,1 +362,14 @@ 'use strict' | ||
}) | ||
test('returns JSON.stringify if schema type is boolean', t => { | ||
t.plan(1) | ||
const schema = { | ||
type: 'array', | ||
items: true | ||
} | ||
const array = [1, true, 'test'] | ||
const stringify = build(schema) | ||
t.equal(stringify(array), JSON.stringify(array)) | ||
}) |
@@ -311,3 +311,3 @@ 'use strict' | ||
t.test('type::array', t => { | ||
t.plan(3) | ||
t.plan(6) | ||
@@ -352,3 +352,3 @@ t.test('format::date-time', t => { | ||
t.test('format::time', t => { | ||
t.test('format::date', t => { | ||
t.plan(2) | ||
@@ -359,3 +359,3 @@ | ||
type: ['string'], | ||
format: 'time' | ||
format: 'date' | ||
} | ||
@@ -370,4 +370,63 @@ } | ||
t.equal(output, '{"updatedAt":""}') | ||
t.notOk(validate(JSON.parse(output)), 'an empty string is not a time format') | ||
t.notOk(validate(JSON.parse(output)), 'an empty string is not a date format') | ||
}) | ||
t.test('format::time, Date object', t => { | ||
t.plan(1) | ||
const schema = { | ||
oneOf: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
updatedAt: { | ||
type: ['string', 'number'], | ||
format: 'time' | ||
} | ||
} | ||
} | ||
] | ||
} | ||
const date = new Date() | ||
const input = { updatedAt: date } | ||
const { output } = serialize(schema, input) | ||
t.equal(output, JSON.stringify({ updatedAt: DateTime.fromJSDate(date).toFormat('HH:mm:ss') })) | ||
}) | ||
t.test('format::time, Date object', t => { | ||
t.plan(1) | ||
const schema = { | ||
oneOf: [ | ||
{ | ||
type: ['string', 'number'], | ||
format: 'time' | ||
} | ||
] | ||
} | ||
const date = new Date() | ||
const { output } = serialize(schema, date) | ||
t.equal(output, `"${DateTime.fromJSDate(date).toFormat('HH:mm:ss')}"`) | ||
}) | ||
t.test('format::time, Date object', t => { | ||
t.plan(1) | ||
const schema = { | ||
oneOf: [ | ||
{ | ||
type: ['string', 'number'], | ||
format: 'time' | ||
} | ||
] | ||
} | ||
const { output } = serialize(schema, 42) | ||
t.equal(output, JSON.stringify(42)) | ||
}) | ||
}) | ||
@@ -436,1 +495,38 @@ | ||
}) | ||
test('Validate Date object as string type', (t) => { | ||
t.plan(1) | ||
const schema = { | ||
oneOf: [ | ||
{ type: 'string' } | ||
] | ||
} | ||
const toStringify = new Date() | ||
const stringify = build(schema) | ||
const output = stringify(toStringify) | ||
t.equal(output, JSON.stringify(toStringify)) | ||
}) | ||
test('nullable date', (t) => { | ||
t.plan(1) | ||
const schema = { | ||
anyOf: [ | ||
{ | ||
format: 'date', | ||
type: 'string', | ||
nullable: true | ||
} | ||
] | ||
} | ||
const stringify = build(schema) | ||
const data = new Date() | ||
const result = stringify(data) | ||
t.same(result, `"${DateTime.fromJSDate(data).toISODate()}"`) | ||
}) |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
317216
11809
4
+ Added@fastify/deepmerge@^1.0.0
+ Added@fastify/deepmerge@1.3.0(transitive)
- Removeddeepmerge@^4.2.2
- Removeddeepmerge@4.3.1(transitive)