| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- function typeOf(value) {
- if (value === null) {
- return 'null'
- }
- if (Array.isArray(value)) {
- return 'array'
- }
- if (Number.isInteger(value)) {
- return 'integer'
- }
- return typeof value
- }
- function jsonPointerDecode(value) {
- return value.replace(/~1/g, '/').replace(/~0/g, '~')
- }
- function resolveRef(rootSchema, ref) {
- if (!ref.startsWith('#/')) {
- throw new Error(`Unsupported schema ref: ${ref}`)
- }
- return ref
- .slice(2)
- .split('/')
- .map(jsonPointerDecode)
- .reduce((node, key) => {
- if (!node || typeof node !== 'object' || !(key in node)) {
- throw new Error(`Missing schema ref: ${ref}`)
- }
- return node[key]
- }, rootSchema)
- }
- function pathFor(path, key) {
- return `${path}/${String(key).replace(/~/g, '~0').replace(/\//g, '~1')}`
- }
- function validateNode(value, schema, rootSchema, path, failures) {
- if (!schema || typeof schema !== 'object') {
- return
- }
- if (schema.$ref) {
- validateNode(value, resolveRef(rootSchema, schema.$ref), rootSchema, path, failures)
- return
- }
- if ('const' in schema && value !== schema.const) {
- failures.push(`${path} must equal ${JSON.stringify(schema.const)}`)
- }
- if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
- failures.push(`${path} must be one of ${schema.enum.map(item => JSON.stringify(item)).join(', ')}`)
- }
- if (schema.type) {
- const accepted = Array.isArray(schema.type) ? schema.type : [schema.type]
- const actual = typeOf(value)
- const ok = accepted.includes(actual) || (actual === 'integer' && accepted.includes('number'))
- if (!ok) {
- failures.push(`${path} type ${actual} is not ${accepted.join('|')}`)
- return
- }
- }
- if (schema.type === 'object' || (!schema.type && value && typeof value === 'object' && !Array.isArray(value))) {
- const objectValue = value && typeof value === 'object' && !Array.isArray(value) ? value : {}
- for (const requiredKey of schema.required || []) {
- if (!(requiredKey in objectValue)) {
- failures.push(`${path} missing required property ${requiredKey}`)
- }
- }
- const properties = schema.properties || {}
- for (const [key, childValue] of Object.entries(objectValue)) {
- if (properties[key]) {
- validateNode(childValue, properties[key], rootSchema, pathFor(path, key), failures)
- } else if (schema.additionalProperties === false) {
- failures.push(`${path} has unexpected property ${key}`)
- } else if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
- validateNode(childValue, schema.additionalProperties, rootSchema, pathFor(path, key), failures)
- }
- }
- }
- if (schema.type === 'array' && Array.isArray(value) && schema.items) {
- value.forEach((item, index) => {
- validateNode(item, schema.items, rootSchema, pathFor(path, index), failures)
- })
- }
- }
- export function validateJsonSchema(value, schema) {
- const failures = []
- validateNode(value, schema, schema, '#', failures)
- return failures
- }
|