Files
powerpoint-toolbox/node_modules/@exodus/schemasafe/src/index.js
T
2025-03-07 19:22:02 +01:00

99 lines
3.6 KiB
JavaScript

'use strict'
const genfun = require('./generate-function')
const { buildSchemas } = require('./pointer')
const { compile } = require('./compile')
const { deepEqual } = require('./scope-functions')
const jsonCheckWithErrors = (validate) =>
function validateIsJSON(data) {
if (!deepEqual(data, JSON.parse(JSON.stringify(data)))) {
validateIsJSON.errors = [{ instanceLocation: '#', error: 'not JSON compatible' }]
return false
}
const res = validate(data)
validateIsJSON.errors = validate.errors
return res
}
const jsonCheckWithoutErrors = (validate) => (data) =>
deepEqual(data, JSON.parse(JSON.stringify(data))) && validate(data)
const validator = (
schema,
{ parse = false, multi = false, jsonCheck = false, isJSON = false, schemas = [], ...opts } = {}
) => {
if (jsonCheck && isJSON) throw new Error('Can not specify both isJSON and jsonCheck options')
if (parse && (jsonCheck || isJSON))
throw new Error('jsonCheck and isJSON options are not applicable in parser mode')
const mode = parse ? 'strong' : 'default' // strong mode is default in parser, can be overriden
const willJSON = isJSON || jsonCheck || parse
const arg = multi ? schema : [schema]
const options = { mode, ...opts, schemas: buildSchemas(schemas, arg), isJSON: willJSON }
const { scope, refs } = compile(arg, options) // only a single ref
if (opts.dryRun) return
if (opts.lint) return scope.lintErrors
const fun = genfun()
if (parse) {
scope.parseWrap = opts.includeErrors ? parseWithErrors : parseWithoutErrors
} else if (jsonCheck) {
scope.deepEqual = deepEqual
scope.jsonCheckWrap = opts.includeErrors ? jsonCheckWithErrors : jsonCheckWithoutErrors
}
if (multi) {
fun.write('[')
for (const ref of refs.slice(0, -1)) fun.write('%s,', ref)
if (refs.length > 0) fun.write('%s', refs[refs.length - 1])
fun.write(']')
if (parse) fun.write('.map(parseWrap)')
else if (jsonCheck) fun.write('.map(jsonCheckWrap)')
} else {
if (parse) fun.write('parseWrap(%s)', refs[0])
else if (jsonCheck) fun.write('jsonCheckWrap(%s)', refs[0])
else fun.write('%s', refs[0])
}
const validate = fun.makeFunction(scope)
validate.toModule = ({ semi = true } = {}) => fun.makeModule(scope) + (semi ? ';' : '')
validate.toJSON = () => schema
return validate
}
const parseWithErrors = (validate) => (src) => {
if (typeof src !== 'string') return { valid: false, error: 'Input is not a string' }
try {
const value = JSON.parse(src)
if (!validate(value)) {
const { keywordLocation, instanceLocation } = validate.errors[0]
const keyword = keywordLocation.slice(keywordLocation.lastIndexOf('/') + 1)
const error = `JSON validation failed for ${keyword} at ${instanceLocation}`
return { valid: false, error, errors: validate.errors }
}
return { valid: true, value }
} catch ({ message }) {
return { valid: false, error: message }
}
}
const parseWithoutErrors = (validate) => (src) => {
if (typeof src !== 'string') return { valid: false }
try {
const value = JSON.parse(src)
if (!validate(value)) return { valid: false }
return { valid: true, value }
} catch (e) {
return { valid: false }
}
}
const parser = function(schema, { parse = true, ...opts } = {}) {
if (!parse) throw new Error('can not disable parse in parser')
return validator(schema, { parse, ...opts })
}
const lint = function(schema, { lint: lintOption = true, ...opts } = {}) {
if (!lintOption) throw new Error('can not disable lint option in lint()')
return validator(schema, { lint: lintOption, ...opts })
}
module.exports = { validator, parser, lint }