22
33/* eslint no-prototype-builtins: 0 */
44
5- const Ajv = require ( 'ajv' )
6- const fastUri = require ( 'fast-uri' )
7- const ajvFormats = require ( 'ajv-formats' )
85const merge = require ( 'deepmerge' )
96const clone = require ( 'rfdc' ) ( { proto : true } )
107const fjsCloned = Symbol ( 'fast-json-stringify.cloned' )
118const { randomUUID } = require ( 'crypto' )
129
1310const validate = require ( './schema-validator' )
11+ const Serializer = require ( './serializer' )
12+ const buildAjv = require ( './ajv' )
1413
1514let largeArraySize = 2e4
1615let stringSimilarity = null
@@ -57,173 +56,6 @@ const schemaReferenceMap = new Map()
5756let ajvInstance = null
5857let contextFunctions = null
5958
60- class Serializer {
61- constructor ( options = { } ) {
62- switch ( options . rounding ) {
63- case 'floor' :
64- this . parseInteger = Math . floor
65- break
66- case 'ceil' :
67- this . parseInteger = Math . ceil
68- break
69- case 'round' :
70- this . parseInteger = Math . round
71- break
72- default :
73- this . parseInteger = Math . trunc
74- break
75- }
76- }
77-
78- asAny ( i ) {
79- return JSON . stringify ( i )
80- }
81-
82- asNull ( ) {
83- return 'null'
84- }
85-
86- asInteger ( i ) {
87- if ( typeof i === 'bigint' ) {
88- return i . toString ( )
89- } else if ( Number . isInteger ( i ) ) {
90- return '' + i
91- } else {
92- /* eslint no-undef: "off" */
93- const integer = this . parseInteger ( i )
94- if ( Number . isNaN ( integer ) ) {
95- throw new Error ( `The value "${ i } " cannot be converted to an integer.` )
96- } else {
97- return '' + integer
98- }
99- }
100- }
101-
102- asIntegerNullable ( i ) {
103- return i === null ? 'null' : this . asInteger ( i )
104- }
105-
106- asNumber ( i ) {
107- const num = Number ( i )
108- if ( Number . isNaN ( num ) ) {
109- throw new Error ( `The value "${ i } " cannot be converted to a number.` )
110- } else {
111- return '' + num
112- }
113- }
114-
115- asNumberNullable ( i ) {
116- return i === null ? 'null' : this . asNumber ( i )
117- }
118-
119- asBoolean ( bool ) {
120- return bool && 'true' || 'false' // eslint-disable-line
121- }
122-
123- asBooleanNullable ( bool ) {
124- return bool === null ? 'null' : this . asBoolean ( bool )
125- }
126-
127- asDatetime ( date , skipQuotes ) {
128- const quotes = skipQuotes === true ? '' : '"'
129- if ( date instanceof Date ) {
130- return quotes + date . toISOString ( ) + quotes
131- }
132- return this . asString ( date , skipQuotes )
133- }
134-
135- asDatetimeNullable ( date , skipQuotes ) {
136- return date === null ? 'null' : this . asDatetime ( date , skipQuotes )
137- }
138-
139- asDate ( date , skipQuotes ) {
140- const quotes = skipQuotes === true ? '' : '"'
141- if ( date instanceof Date ) {
142- return quotes + new Date ( date . getTime ( ) - ( date . getTimezoneOffset ( ) * 60000 ) ) . toISOString ( ) . slice ( 0 , 10 ) + quotes
143- }
144- return this . asString ( date , skipQuotes )
145- }
146-
147- asDateNullable ( date , skipQuotes ) {
148- return date === null ? 'null' : this . asDate ( date , skipQuotes )
149- }
150-
151- asTime ( date , skipQuotes ) {
152- const quotes = skipQuotes === true ? '' : '"'
153- if ( date instanceof Date ) {
154- return quotes + new Date ( date . getTime ( ) - ( date . getTimezoneOffset ( ) * 60000 ) ) . toISOString ( ) . slice ( 11 , 19 ) + quotes
155- }
156- return this . asString ( date , skipQuotes )
157- }
158-
159- asTimeNullable ( date , skipQuotes ) {
160- return date === null ? 'null' : this . asTime ( date , skipQuotes )
161- }
162-
163- asString ( str , skipQuotes ) {
164- const quotes = skipQuotes === true ? '' : '"'
165- if ( str instanceof Date ) {
166- return quotes + str . toISOString ( ) + quotes
167- } else if ( str === null ) {
168- return quotes + quotes
169- } else if ( str instanceof RegExp ) {
170- str = str . source
171- } else if ( typeof str !== 'string' ) {
172- str = str . toString ( )
173- }
174- // If we skipQuotes it means that we are using it as test
175- // no need to test the string length for the render
176- if ( skipQuotes ) {
177- return str
178- }
179-
180- if ( str . length < 42 ) {
181- return this . asStringSmall ( str )
182- } else {
183- return JSON . stringify ( str )
184- }
185- }
186-
187- asStringNullable ( str ) {
188- return str === null ? 'null' : this . asString ( str )
189- }
190-
191- // magically escape strings for json
192- // relying on their charCodeAt
193- // everything below 32 needs JSON.stringify()
194- // every string that contain surrogate needs JSON.stringify()
195- // 34 and 92 happens all the time, so we
196- // have a fast case for them
197- asStringSmall ( str ) {
198- const l = str . length
199- let result = ''
200- let last = 0
201- let found = false
202- let surrogateFound = false
203- let point = 255
204- // eslint-disable-next-line
205- for ( var i = 0 ; i < l && point >= 32 ; i ++ ) {
206- point = str . charCodeAt ( i )
207- if ( point >= 0xD800 && point <= 0xDFFF ) {
208- // The current character is a surrogate.
209- surrogateFound = true
210- }
211- if ( point === 34 || point === 92 ) {
212- result += str . slice ( last , i ) + '\\'
213- last = i
214- found = true
215- }
216- }
217-
218- if ( ! found ) {
219- result = str
220- } else {
221- result += str . slice ( last )
222- }
223- return ( ( point < 32 ) || ( surrogateFound === true ) ) ? JSON . stringify ( str ) : '"' + result + '"'
224- }
225- }
226-
22759function build ( schema , options ) {
22860 arrayItemsReferenceSerializersMap . clear ( )
22961 objectReferenceSerializersMap . clear ( )
@@ -232,31 +64,7 @@ function build (schema, options) {
23264 contextFunctions = [ ]
23365 options = options || { }
23466
235- ajvInstance = new Ajv ( { ...options . ajv , strictSchema : false , uriResolver : fastUri } )
236- ajvFormats ( ajvInstance )
237-
238- const validateDateTimeFormat = ajvFormats . get ( 'date-time' ) . validate
239- const validateDateFormat = ajvFormats . get ( 'date' ) . validate
240- const validateTimeFormat = ajvFormats . get ( 'time' ) . validate
241-
242- ajvInstance . addKeyword ( {
243- keyword : 'fjs_date_type' ,
244- validate : ( schema , date ) => {
245- if ( date instanceof Date ) {
246- return true
247- }
248- if ( schema === 'date-time' ) {
249- return validateDateTimeFormat ( date )
250- }
251- if ( schema === 'date' ) {
252- return validateDateFormat ( date )
253- }
254- if ( schema === 'time' ) {
255- return validateTimeFormat ( date )
256- }
257- return false
258- }
259- } )
67+ ajvInstance = buildAjv ( options . ajv )
26068
26169 isValidSchema ( schema )
26270 if ( options . schema ) {
@@ -320,9 +128,29 @@ function build (schema, options) {
320128 const dependenciesName = [ 'ajv' , 'serializer' , contextFunctionCode ]
321129
322130 if ( options . debugMode ) {
131+ options . mode = 'debug'
132+ }
133+
134+ if ( options . mode === 'debug' ) {
323135 return { code : dependenciesName . join ( '\n' ) , ajv : ajvInstance }
324136 }
325137
138+ if ( options . mode === 'standalone' ) {
139+ return `
140+ 'use strict'
141+
142+ const Serializer = require('fast-json-stringify/serializer')
143+ const buildAjv = require('fast-json-stringify/ajv')
144+
145+ const serializer = new Serializer(${ JSON . stringify ( options || { } ) } )
146+ const ajv = buildAjv(${ JSON . stringify ( options . ajv || { } ) } )
147+
148+ ${ contextFunctionCode . replace ( 'return main' , '' ) }
149+
150+ module.exports = main
151+ `
152+ }
153+
326154 /* eslint no-new-func: "off" */
327155 const contextFunc = new Function ( 'ajv' , 'serializer' , contextFunctionCode )
328156 const stringifyFunc = contextFunc ( ajvInstance , serializer )
0 commit comments