fast-json-stringify 5.0.5 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,15 +11,15 @@ jobs:
11
11
  permissions:
12
12
  contents: read
13
13
  outputs:
14
- PR-BENCH-12: ${{ steps.benchmark-pr.outputs.BENCH_RESULT12 }}
15
14
  PR-BENCH-14: ${{ steps.benchmark-pr.outputs.BENCH_RESULT14 }}
16
15
  PR-BENCH-16: ${{ steps.benchmark-pr.outputs.BENCH_RESULT16 }}
17
- MAIN-BENCH-12: ${{ steps.benchmark-main.outputs.BENCH_RESULT12 }}
16
+ PR-BENCH-18: ${{ steps.benchmark-pr.outputs.BENCH_RESULT18 }}
18
17
  MAIN-BENCH-14: ${{ steps.benchmark-main.outputs.BENCH_RESULT14 }}
19
18
  MAIN-BENCH-16: ${{ steps.benchmark-main.outputs.BENCH_RESULT16 }}
19
+ MAIN-BENCH-18: ${{ steps.benchmark-main.outputs.BENCH_RESULT18 }}
20
20
  strategy:
21
21
  matrix:
22
- node-version: [12, 14, 16]
22
+ node-version: [14, 16, 18]
23
23
  steps:
24
24
  - uses: actions/checkout@v3
25
25
  with:
@@ -75,38 +75,38 @@ jobs:
75
75
  with:
76
76
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
77
77
  message: |
78
- **Node**: 12
78
+ **Node**: 14
79
79
  **PR**:
80
80
  ```
81
- ${{ needs.benchmark.outputs.PR-BENCH-12 }}
81
+ ${{ needs.benchmark.outputs.PR-BENCH-14 }}
82
82
  ```
83
83
  **MAIN**:
84
84
  ```
85
- ${{ needs.benchmark.outputs.MAIN-BENCH-12 }}
85
+ ${{ needs.benchmark.outputs.MAIN-BENCH-14 }}
86
86
  ```
87
87
 
88
88
  ---
89
89
 
90
- **Node**: 14
90
+ **Node**: 16
91
91
  **PR**:
92
92
  ```
93
- ${{ needs.benchmark.outputs.PR-BENCH-14 }}
93
+ ${{ needs.benchmark.outputs.PR-BENCH-16 }}
94
94
  ```
95
95
  **MAIN**:
96
96
  ```
97
- ${{ needs.benchmark.outputs.MAIN-BENCH-14 }}
97
+ ${{ needs.benchmark.outputs.MAIN-BENCH-16 }}
98
98
  ```
99
99
 
100
100
  ---
101
101
 
102
- **Node**: 16
102
+ **Node**: 18
103
103
  **PR**:
104
104
  ```
105
- ${{ needs.benchmark.outputs.PR-BENCH-16 }}
105
+ ${{ needs.benchmark.outputs.PR-BENCH-18 }}
106
106
  ```
107
107
  **MAIN**:
108
108
  ```
109
- ${{ needs.benchmark.outputs.MAIN-BENCH-16 }}
109
+ ${{ needs.benchmark.outputs.MAIN-BENCH-18 }}
110
110
  ```
111
111
 
112
112
  - uses: actions-ecosystem/action-remove-labels@v1
@@ -13,3 +13,5 @@ on:
13
13
  jobs:
14
14
  test:
15
15
  uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
16
+ with:
17
+ license-check: true
@@ -213,6 +213,60 @@ const benchmarks = [
213
213
  n5: 42,
214
214
  b5: true
215
215
  }
216
+ },
217
+ {
218
+ name: 'object with const string property',
219
+ schema: {
220
+ type: 'object',
221
+ properties: {
222
+ a: { const: 'const string' }
223
+ }
224
+ },
225
+ input: { a: 'const string' }
226
+ },
227
+ {
228
+ name: 'object with const number property',
229
+ schema: {
230
+ type: 'object',
231
+ properties: {
232
+ a: { const: 1 }
233
+ }
234
+ },
235
+ input: { a: 1 }
236
+ },
237
+ {
238
+ name: 'object with const bool property',
239
+ schema: {
240
+ type: 'object',
241
+ properties: {
242
+ a: { const: true }
243
+ }
244
+ },
245
+ input: { a: true }
246
+ },
247
+ {
248
+ name: 'object with const object property',
249
+ schema: {
250
+ type: 'object',
251
+ properties: {
252
+ foo: { const: { bar: 'baz' } }
253
+ }
254
+ },
255
+ input: {
256
+ foo: { bar: 'baz' }
257
+ }
258
+ },
259
+ {
260
+ name: 'object with const null property',
261
+ schema: {
262
+ type: 'object',
263
+ properties: {
264
+ foo: { const: null }
265
+ }
266
+ },
267
+ input: {
268
+ foo: null
269
+ }
216
270
  }
217
271
  ]
218
272
 
package/index.js CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  /* eslint no-prototype-builtins: 0 */
4
4
 
5
- const merge = require('deepmerge')
5
+ const merge = require('@fastify/deepmerge')()
6
6
  const clone = require('rfdc')({ proto: true })
7
7
  const fjsCloned = Symbol('fast-json-stringify.cloned')
8
8
  const { randomUUID } = require('crypto')
9
9
 
10
10
  const validate = require('./schema-validator')
11
11
  const Serializer = require('./serializer')
12
- const buildAjv = require('./ajv')
12
+ const Validator = require('./validator')
13
+ const RefResolver = require('./ref-resolver')
13
14
 
14
15
  let largeArraySize = 2e4
15
16
  let largeArrayMechanism = 'default'
@@ -55,22 +56,14 @@ function resolveRef (location, ref) {
55
56
  }
56
57
 
57
58
  const schemaId = ref.slice(0, hashIndex) || location.schemaId
58
- const jsonPointer = ref.slice(hashIndex)
59
+ const jsonPointer = ref.slice(hashIndex) || '#'
59
60
 
60
- const schemaRef = schemaId + jsonPointer
61
+ const schema = refResolver.getSchema(schemaId, jsonPointer)
61
62
 
62
- let ajvSchema
63
- try {
64
- ajvSchema = ajvInstance.getSchema(schemaRef)
65
- } catch (error) {
63
+ if (schema === undefined) {
66
64
  throw new Error(`Cannot find reference "${ref}"`)
67
65
  }
68
66
 
69
- if (ajvSchema === undefined) {
70
- throw new Error(`Cannot find reference "${ref}"`)
71
- }
72
-
73
- const schema = ajvSchema.schema
74
67
  if (schema.$ref !== undefined) {
75
68
  return resolveRef({ schema, schemaId, jsonPointer }, schema.$ref)
76
69
  }
@@ -82,41 +75,31 @@ const arrayItemsReferenceSerializersMap = new Map()
82
75
  const objectReferenceSerializersMap = new Map()
83
76
 
84
77
  let rootSchemaId = null
85
- let ajvInstance = null
78
+ let refResolver = null
79
+ let validator = null
86
80
  let contextFunctions = null
87
81
 
88
82
  function build (schema, options) {
89
- schema = clone(schema)
90
-
91
83
  arrayItemsReferenceSerializersMap.clear()
92
84
  objectReferenceSerializersMap.clear()
93
85
 
94
86
  contextFunctions = []
95
87
  options = options || {}
96
88
 
97
- ajvInstance = buildAjv(options.ajv)
89
+ refResolver = new RefResolver()
90
+ validator = new Validator(options.ajv)
91
+
98
92
  rootSchemaId = schema.$id || randomUUID()
99
93
 
100
94
  isValidSchema(schema)
101
- extendDateTimeType(schema)
102
- ajvInstance.addSchema(schema, rootSchemaId)
95
+ validator.addSchema(schema, rootSchemaId)
96
+ refResolver.addSchema(schema, rootSchemaId)
103
97
 
104
98
  if (options.schema) {
105
- const externalSchemas = clone(options.schema)
106
-
107
- for (const key of Object.keys(externalSchemas)) {
108
- const externalSchema = externalSchemas[key]
109
- isValidSchema(externalSchema, key)
110
- extendDateTimeType(externalSchema)
111
-
112
- let schemaKey = externalSchema.$id || key
113
- if (externalSchema.$id !== undefined && externalSchema.$id[0] === '#') {
114
- schemaKey = key + externalSchema.$id // relative URI
115
- }
116
-
117
- if (ajvInstance.getSchema(schemaKey) === undefined) {
118
- ajvInstance.addSchema(externalSchema, schemaKey)
119
- }
99
+ for (const key of Object.keys(options.schema)) {
100
+ isValidSchema(options.schema[key], key)
101
+ validator.addSchema(options.schema[key], key)
102
+ refResolver.addSchema(options.schema[key], key)
120
103
  }
121
104
  }
122
105
 
@@ -157,27 +140,28 @@ function build (schema, options) {
157
140
  return main
158
141
  `
159
142
 
160
- const dependenciesName = ['ajv', 'serializer', contextFunctionCode]
143
+ const dependenciesName = ['validator', 'serializer', contextFunctionCode]
161
144
 
162
145
  if (options.debugMode) {
163
146
  options.mode = 'debug'
164
147
  }
165
148
 
166
149
  if (options.mode === 'debug') {
167
- return { code: dependenciesName.join('\n'), ajv: ajvInstance }
150
+ return { code: dependenciesName.join('\n'), validator, ajv: validator.ajv }
168
151
  }
169
152
 
170
153
  if (options.mode === 'standalone') {
171
154
  // lazy load
172
155
  const buildStandaloneCode = require('./standalone')
173
- return buildStandaloneCode(options, ajvInstance, contextFunctionCode)
156
+ return buildStandaloneCode(options, validator, contextFunctionCode)
174
157
  }
175
158
 
176
159
  /* eslint no-new-func: "off" */
177
- const contextFunc = new Function('ajv', 'serializer', contextFunctionCode)
178
- const stringifyFunc = contextFunc(ajvInstance, serializer)
160
+ const contextFunc = new Function('validator', 'serializer', contextFunctionCode)
161
+ const stringifyFunc = contextFunc(validator, serializer)
179
162
 
180
- ajvInstance = null
163
+ refResolver = null
164
+ validator = null
181
165
  rootSchemaId = null
182
166
  contextFunctions = null
183
167
  arrayItemsReferenceSerializersMap.clear()
@@ -243,15 +227,6 @@ function inferTypeByKeyword (schema) {
243
227
  return schema.type
244
228
  }
245
229
 
246
- function getStringSerializer (format, nullable) {
247
- switch (format) {
248
- case 'date-time': return nullable ? 'serializer.asDatetimeNullable.bind(serializer)' : 'serializer.asDatetime.bind(serializer)'
249
- case 'date': return nullable ? 'serializer.asDateNullable.bind(serializer)' : 'serializer.asDate.bind(serializer)'
250
- case 'time': return nullable ? 'serializer.asTimeNullable.bind(serializer)' : 'serializer.asTime.bind(serializer)'
251
- default: return nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)'
252
- }
253
- }
254
-
255
230
  function addPatternProperties (location) {
256
231
  const schema = location.schema
257
232
  const pp = schema.patternProperties
@@ -350,17 +325,16 @@ function buildCode (location) {
350
325
  const propertiesLocation = mergeLocation(location, 'properties')
351
326
  Object.keys(schema.properties || {}).forEach((key) => {
352
327
  let propertyLocation = mergeLocation(propertiesLocation, key)
353
- if (schema.properties[key].$ref) {
354
- propertyLocation = resolveRef(location, schema.properties[key].$ref)
355
- schema.properties[key] = propertyLocation.schema
328
+ if (propertyLocation.$ref) {
329
+ propertyLocation = resolveRef(location, propertyLocation.$ref)
356
330
  }
357
331
 
358
- // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
359
- // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
360
-
361
332
  const sanitized = JSON.stringify(key)
362
333
  const asString = JSON.stringify(sanitized)
363
334
 
335
+ // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
336
+ // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
337
+
364
338
  code += `
365
339
  if (obj[${sanitized}] !== undefined) {
366
340
  ${addComma}
@@ -369,7 +343,7 @@ function buildCode (location) {
369
343
 
370
344
  code += buildValue(propertyLocation, `obj[${JSON.stringify(key)}]`)
371
345
 
372
- const defaultValue = schema.properties[key].default
346
+ const defaultValue = propertyLocation.schema.default
373
347
  if (defaultValue !== undefined) {
374
348
  code += `
375
349
  } else {
@@ -491,7 +465,8 @@ function mergeAllOfSchema (location, schema, mergedSchema) {
491
465
  delete mergedSchema.allOf
492
466
 
493
467
  mergedSchema.$id = `merged_${randomUUID()}`
494
- ajvInstance.addSchema(mergedSchema)
468
+ validator.addSchema(mergedSchema)
469
+ refResolver.addSchema(mergedSchema)
495
470
  location.schemaId = mergedSchema.$id
496
471
  location.jsonPointer = '#'
497
472
  }
@@ -520,7 +495,7 @@ function addIfThenElse (location) {
520
495
  const ifSchemaRef = ifLocation.schemaId + ifLocation.jsonPointer
521
496
 
522
497
  let code = `
523
- if (ajv.validate("${ifSchemaRef}", obj)) {
498
+ if (validator.validate("${ifSchemaRef}", obj)) {
524
499
  `
525
500
 
526
501
  const thenLocation = mergeLocation(location, 'then')
@@ -771,6 +746,10 @@ function generateFuncName () {
771
746
  function buildValue (location, input) {
772
747
  let schema = location.schema
773
748
 
749
+ if (typeof schema === 'boolean') {
750
+ return `json += JSON.stringify(${input})`
751
+ }
752
+
774
753
  if (schema.$ref) {
775
754
  location = resolveRef(location, schema.$ref)
776
755
  schema = location.schema
@@ -791,19 +770,36 @@ function buildValue (location, input) {
791
770
  }
792
771
 
793
772
  const type = schema.type
794
- const nullable = schema.nullable === true
773
+ const nullable = schema.nullable === true || (Array.isArray(type) && type.includes('null'))
795
774
 
796
775
  let code = ''
797
776
  let funcName
798
777
 
799
- switch (type) {
800
- case 'null':
778
+ if ('const' in schema) {
779
+ if (nullable) {
801
780
  code += `
802
- json += serializer.asNull()
781
+ json += ${input} === null ? 'null' : '${JSON.stringify(schema.const)}'
803
782
  `
783
+ return code
784
+ }
785
+ code += `json += '${JSON.stringify(schema.const)}'`
786
+ return code
787
+ }
788
+
789
+ switch (type) {
790
+ case 'null':
791
+ code += 'json += serializer.asNull()'
804
792
  break
805
793
  case 'string': {
806
- funcName = getStringSerializer(schema.format, nullable)
794
+ if (schema.format === 'date-time') {
795
+ funcName = nullable ? 'serializer.asDateTimeNullable.bind(serializer)' : 'serializer.asDateTime.bind(serializer)'
796
+ } else if (schema.format === 'date') {
797
+ funcName = nullable ? 'serializer.asDateNullable.bind(serializer)' : 'serializer.asDate.bind(serializer)'
798
+ } else if (schema.format === 'time') {
799
+ funcName = nullable ? 'serializer.asTimeNullable.bind(serializer)' : 'serializer.asTime.bind(serializer)'
800
+ } else {
801
+ funcName = nullable ? 'serializer.asStringNullable.bind(serializer)' : 'serializer.asString.bind(serializer)'
802
+ }
807
803
  code += `json += ${funcName}(${input})`
808
804
  break
809
805
  }
@@ -828,11 +824,7 @@ function buildValue (location, input) {
828
824
  code += `json += ${funcName}(${input})`
829
825
  break
830
826
  case undefined:
831
- if (schema.fjs_date_type) {
832
- funcName = getStringSerializer(schema.fjs_date_type, nullable)
833
- code += `json += ${funcName}(${input})`
834
- break
835
- } else if (schema.anyOf || schema.oneOf) {
827
+ if (schema.anyOf || schema.oneOf) {
836
828
  // beware: dereferenceOfRefs has side effects and changes schema.anyOf
837
829
  const type = schema.anyOf ? 'anyOf' : 'oneOf'
838
830
  const anyOfLocation = mergeLocation(location, type)
@@ -842,7 +834,7 @@ function buildValue (location, input) {
842
834
  const schemaRef = optionLocation.schemaId + optionLocation.jsonPointer
843
835
  const nestedResult = buildValue(optionLocation, input)
844
836
  code += `
845
- ${index === 0 ? 'if' : 'else if'}(ajv.validate("${schemaRef}", ${input}))
837
+ ${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input}))
846
838
  ${nestedResult}
847
839
  `
848
840
  }
@@ -854,19 +846,10 @@ function buildValue (location, input) {
854
846
  code += `
855
847
  json += JSON.stringify(${input})
856
848
  `
857
- } else if ('const' in schema) {
858
- code += `
859
- if(ajv.validate(${JSON.stringify(schema)}, ${input}))
860
- json += '${JSON.stringify(schema.const)}'
861
- else
862
- throw new Error(\`Item $\{JSON.stringify(${input})} does not match schema definition.\`)
863
- `
864
- } else if (schema.type === undefined) {
849
+ } else {
865
850
  code += `
866
851
  json += JSON.stringify(${input})
867
852
  `
868
- } else {
869
- throw new Error(`${schema.type} unsupported`)
870
853
  }
871
854
  break
872
855
  default:
@@ -890,7 +873,18 @@ function buildValue (location, input) {
890
873
  switch (type) {
891
874
  case 'string': {
892
875
  code += `
893
- ${statement}(${input} === null || typeof ${input} === "${type}" || ${input} instanceof Date || ${input} instanceof RegExp || (typeof ${input} === "object" && Object.hasOwnProperty.call(${input}, "toString")))
876
+ ${statement}(
877
+ typeof ${input} === "string" ||
878
+ ${input} === null ||
879
+ ${input} instanceof Date ||
880
+ ${input} instanceof RegExp ||
881
+ (
882
+ typeof ${input} === "object" &&
883
+ typeof ${input}.toString === "function" &&
884
+ ${input}.toString !== Object.prototype.toString &&
885
+ !(${input} instanceof Date)
886
+ )
887
+ )
894
888
  ${nestedResult}
895
889
  `
896
890
  break
@@ -909,6 +903,13 @@ function buildValue (location, input) {
909
903
  `
910
904
  break
911
905
  }
906
+ case 'object': {
907
+ code += `
908
+ ${statement}(typeof ${input} === "object" || ${input} === null)
909
+ ${nestedResult}
910
+ `
911
+ break
912
+ }
912
913
  default: {
913
914
  code += `
914
915
  ${statement}(typeof ${input} === "${type}" || ${input} === null)
@@ -935,28 +936,10 @@ function buildValue (location, input) {
935
936
  return code
936
937
  }
937
938
 
938
- // Ajv does not support js date format. In order to properly validate objects containing a date,
939
- // it needs to replace all occurrences of the string date format with a custom keyword fjs_date_type.
940
- // (see https://github.com/fastify/fast-json-stringify/pull/441)
941
- function extendDateTimeType (schema) {
942
- if (schema === null) return
943
-
944
- if (schema.type === 'string' && ['date-time', 'date', 'time'].includes(schema.format)) {
945
- schema.fjs_date_type = schema.format
946
- delete schema.type
947
- delete schema.format
948
- }
949
- for (const property in schema) {
950
- if (typeof schema[property] === 'object') {
951
- extendDateTimeType(schema[property])
952
- }
953
- }
954
- }
955
-
956
939
  function isEmpty (schema) {
957
940
  // eslint-disable-next-line
958
941
  for (var key in schema) {
959
- if (schema.hasOwnProperty(key) && schema[key] !== undefined) {
942
+ if (Object.prototype.hasOwnProperty.call(schema, key) && schema[key] !== undefined) {
960
943
  return false
961
944
  }
962
945
  }
@@ -967,9 +950,9 @@ module.exports = build
967
950
 
968
951
  module.exports.validLargeArrayMechanisms = validLargeArrayMechanisms
969
952
 
970
- module.exports.restore = function ({ code, ajv }) {
953
+ module.exports.restore = function ({ code, validator }) {
971
954
  const serializer = new Serializer()
972
955
  // eslint-disable-next-line
973
- return (Function.apply(null, ['ajv', 'serializer', code])
974
- .apply(null, [ajv, serializer]))
956
+ return (Function.apply(null, ['validator', 'serializer', code])
957
+ .apply(null, [validator, serializer]))
975
958
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "5.0.5",
3
+ "version": "5.2.0",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -33,13 +33,13 @@
33
33
  },
34
34
  "homepage": "https://github.com/fastify/fast-json-stringify#readme",
35
35
  "devDependencies": {
36
- "@sinclair/typebox": "^0.23.3",
36
+ "@fastify/pre-commit": "^2.0.2",
37
+ "@sinclair/typebox": "^0.24.9",
37
38
  "benchmark": "^2.1.4",
38
39
  "cli-select": "^1.1.2",
39
40
  "compile-json-stringify": "^0.1.2",
40
41
  "is-my-json-valid": "^2.20.0",
41
- "luxon": "^2.4.0",
42
- "pre-commit": "^1.2.2",
42
+ "luxon": "^3.0.1",
43
43
  "proxyquire": "^2.1.3",
44
44
  "semver": "^7.1.0",
45
45
  "simple-git": "^3.7.1",
@@ -49,9 +49,10 @@
49
49
  "webpack": "^5.40.0"
50
50
  },
51
51
  "dependencies": {
52
+ "@fastify/deepmerge": "^1.0.0",
52
53
  "ajv": "^8.10.0",
53
54
  "ajv-formats": "^2.1.1",
54
- "deepmerge": "^4.2.2",
55
+ "fast-deep-equal": "^3.1.3",
55
56
  "fast-uri": "^2.1.0",
56
57
  "rfdc": "^1.2.0"
57
58
  },
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+
3
+ const deepEqual = require('fast-deep-equal')
4
+
5
+ class RefResolver {
6
+ constructor () {
7
+ this.schemas = {}
8
+ }
9
+
10
+ addSchema (schema, schemaId) {
11
+ if (schema.$id !== undefined && schema.$id.charAt(0) !== '#') {
12
+ schemaId = schema.$id
13
+ }
14
+ if (this.getSchema(schemaId) === undefined) {
15
+ this.insertSchemaBySchemaId(schema, schemaId)
16
+ this.insertSchemaSubschemas(schema, schemaId)
17
+ }
18
+ }
19
+
20
+ getSchema (schemaId, jsonPointer = '#') {
21
+ const schema = this.schemas[schemaId]
22
+ if (schema === undefined) {
23
+ return undefined
24
+ }
25
+ if (schema.anchors[jsonPointer] !== undefined) {
26
+ return schema.anchors[jsonPointer]
27
+ }
28
+ return getDataByJSONPointer(schema.schema, jsonPointer)
29
+ }
30
+
31
+ insertSchemaBySchemaId (schema, schemaId) {
32
+ if (
33
+ this.schemas[schemaId] !== undefined &&
34
+ !deepEqual(schema, this.schemas[schemaId].schema)
35
+ ) {
36
+ throw new Error(`There is already another schema with id ${schemaId}`)
37
+ }
38
+ this.schemas[schemaId] = { schema, anchors: {} }
39
+ }
40
+
41
+ insertSchemaByAnchor (schema, schemaId, anchor) {
42
+ const { anchors } = this.schemas[schemaId]
43
+ if (
44
+ anchors[anchor] !== undefined &&
45
+ !deepEqual(schema, anchors[anchor])
46
+ ) {
47
+ throw new Error(`There is already another schema with id ${schemaId}#${anchor}`)
48
+ }
49
+ anchors[anchor] = schema
50
+ }
51
+
52
+ insertSchemaSubschemas (schema, rootSchemaId) {
53
+ const schemaId = schema.$id
54
+ if (schemaId !== undefined && typeof schemaId === 'string') {
55
+ if (schemaId.charAt(0) === '#') {
56
+ this.insertSchemaByAnchor(schema, rootSchemaId, schemaId)
57
+ } else {
58
+ this.insertSchemaBySchemaId(schema, schemaId)
59
+ rootSchemaId = schemaId
60
+ }
61
+ }
62
+
63
+ for (const key in schema) {
64
+ if (typeof schema[key] === 'object' && schema[key] !== null) {
65
+ this.insertSchemaSubschemas(schema[key], rootSchemaId)
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ function getDataByJSONPointer (data, jsonPointer) {
72
+ const parts = jsonPointer.split('/')
73
+ let current = data
74
+ for (const part of parts) {
75
+ if (part === '' || part === '#') continue
76
+ if (typeof current !== 'object' || current === null) {
77
+ return undefined
78
+ }
79
+ current = current[part]
80
+ }
81
+ return current
82
+ }
83
+
84
+ module.exports = RefResolver
package/serializer.js CHANGED
@@ -69,24 +69,30 @@ module.exports = class Serializer {
69
69
  return bool === null ? 'null' : this.asBoolean(bool)
70
70
  }
71
71
 
72
- asDatetime (date) {
73
- const quotes = '"'
72
+ asDateTime (date) {
73
+ if (date === null) return '""'
74
74
  if (date instanceof Date) {
75
- return quotes + date.toISOString() + quotes
75
+ return '"' + date.toISOString() + '"'
76
+ }
77
+ if (typeof date === 'string') {
78
+ return '"' + date + '"'
76
79
  }
77
- return this.asString(date)
80
+ throw new Error(`The value "${date}" cannot be converted to a date-time.`)
78
81
  }
79
82
 
80
- asDatetimeNullable (date) {
81
- return date === null ? 'null' : this.asDatetime(date)
83
+ asDateTimeNullable (date) {
84
+ return date === null ? 'null' : this.asDateTime(date)
82
85
  }
83
86
 
84
87
  asDate (date) {
85
- const quotes = '"'
88
+ if (date === null) return '""'
86
89
  if (date instanceof Date) {
87
- return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + quotes
90
+ return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(0, 10) + '"'
88
91
  }
89
- return this.asString(date)
92
+ if (typeof date === 'string') {
93
+ return '"' + date + '"'
94
+ }
95
+ throw new Error(`The value "${date}" cannot be converted to a date.`)
90
96
  }
91
97
 
92
98
  asDateNullable (date) {
@@ -94,11 +100,14 @@ module.exports = class Serializer {
94
100
  }
95
101
 
96
102
  asTime (date) {
97
- const quotes = '"'
103
+ if (date === null) return '""'
98
104
  if (date instanceof Date) {
99
- return quotes + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + quotes
105
+ return '"' + new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString().slice(11, 19) + '"'
106
+ }
107
+ if (typeof date === 'string') {
108
+ return '"' + date + '"'
100
109
  }
101
- return this.asString(date)
110
+ throw new Error(`The value "${date}" cannot be converted to a time.`)
102
111
  }
103
112
 
104
113
  asTimeNullable (date) {