22
33/* eslint no-prototype-builtins: 0 */
44
5- const Ajv = require ( 'ajv' )
5+ const Ajv = require ( 'ajv' ) . default
66const merge = require ( 'deepmerge' )
77const clone = require ( 'rfdc' ) ( { proto : true } )
88const fjsCloned = Symbol ( 'fast-json-stringify.cloned' )
@@ -87,6 +87,40 @@ function build (schema, options) {
8787 ${ $asBoolean . toString ( ) }
8888 ${ $asBooleanNullable . toString ( ) }
8989
90+
91+ /**
92+ * Used by schemas that are dependant on calling 'ajv.validate' during runtime,
93+ * it stores the value of the '$id' property of the schema (if it has it) inside
94+ * a cache which is used to figure out if the schema was compiled into a validator
95+ * by ajv on a previous call, if it was then the '$id' string will be used to
96+ * invoke 'ajv.validate', this allows:
97+ *
98+ * 1. Schemas that depend on ajv.validate calls to leverage ajv caching system.
99+ * 2. To avoid errors, since directly invoking 'ajv.validate' with the same
100+ * schema (that contains an '$id' property) twice will throw an error.
101+ */
102+ const $validateWithAjv = (function() {
103+ const cache = new Set()
104+
105+ return function (schema, target) {
106+ const id = schema.$id
107+
108+ if (!id) {
109+ return ajv.validate(schema, target)
110+ }
111+
112+ const cached = cache.has(id)
113+
114+ if (cached) {
115+ return ajv.validate(id, target)
116+ } else {
117+ cache.add(id)
118+ return ajv.validate(schema, target)
119+ }
120+ }
121+ })()
122+
123+
90124 var isLong = ${ isLong ? isLong . toString ( ) : false }
91125
92126 function parseInteger(int) { return Math.${ intParseFunctionName } (int) }
@@ -870,7 +904,7 @@ function addIfThenElse (location, name) {
870904 let mergedLocation = mergeLocation ( location , { schema : merged } )
871905
872906 code += `
873- valid = ajv.validate (${ JSON . stringify ( i ) } , obj)
907+ valid = $validateWithAjv (${ JSON . stringify ( i ) } , obj)
874908 if (valid) {
875909 `
876910 if ( merged . if && merged . then ) {
@@ -1189,7 +1223,7 @@ function nested (laterCode, name, key, location, subKey, isArray) {
11891223 // 2. `nested`, through `buildCode`, replaces any reference in object properties with the actual schema
11901224 // (see https://github.com/fastify/fast-json-stringify/blob/6da3b3e8ac24b1ca5578223adedb4083b7adf8db/index.js#L631)
11911225 code += `
1192- ${ index === 0 ? 'if' : 'else if' } (ajv.validate (${ JSON . stringify ( location . schema ) } , obj${ accessor } ))
1226+ ${ index === 0 ? 'if' : 'else if' } ($validateWithAjv (${ JSON . stringify ( location . schema ) } , obj${ accessor } ))
11931227 ${ nestedResult . code }
11941228 `
11951229 laterCode = nestedResult . laterCode
@@ -1205,7 +1239,7 @@ function nested (laterCode, name, key, location, subKey, isArray) {
12051239
12061240 // see comment on anyOf about derefencing the schema before calling ajv.validate
12071241 code += `
1208- ${ index === 0 ? 'if' : 'else if' } (ajv.validate (${ JSON . stringify ( location . schema ) } , obj${ accessor } ))
1242+ ${ index === 0 ? 'if' : 'else if' } ($validateWithAjv (${ JSON . stringify ( location . schema ) } , obj${ accessor } ))
12091243 ${ nestedResult . code }
12101244 `
12111245 laterCode = nestedResult . laterCode
0 commit comments