diff --git a/index.ts b/index.ts index e2d7500..76a5d52 100644 --- a/index.ts +++ b/index.ts @@ -17,7 +17,7 @@ import { Draft06, draft06Config } from "./lib/draft06"; import { Draft07, draft07Config } from "./lib/draft07"; import { Draft2019, draft2019Config } from "./lib/draft2019"; import { JsonEditor, draftJsonEditorConfig } from "./lib/jsoneditor"; -import { isJsonError } from "./lib/types"; +import { createNode, isJsonError, isSchemaNode } from "./lib/types"; const config = { strings }; @@ -25,6 +25,7 @@ export { config, createCustomError, createError, + createNode, // v10 Draft, Draft04, // core implementing draft04 specs draft04Config, // config implementing draft04 specs @@ -36,14 +37,15 @@ export { draft2019Config, // config implementing draft2019-09 specs draftJsonEditorConfig, // adjusted config of draft04 to better support the json-editor getTypeOf, // returns the javascript datatype - isDynamicSchema, // NEW + isDynamicSchema, // v8 isJsonError, + isSchemaNode, // v10 JsonEditor, // adjusted core of draft07 to better support the json-editor - mergeSchema, // NEW - reduceSchema, // NEW + mergeSchema, // v8 + reduceSchema, // v8 render, resolveAllOf, - resolveDynamicSchema, // NEW + resolveDynamicSchema, // v8 resolveOneOf, resolveOneOfFuzzy, resolveRef, @@ -63,7 +65,8 @@ import { JsonError, JsonValidator, JsonTypeValidator, - ErrorData + ErrorData, + SchemaNode } from "./lib/types"; import { JSType } from "./lib/getTypeOf"; @@ -79,5 +82,6 @@ export type { JsonSchema, JsonTypeValidator, JsonValidator, - JSType + JSType, + SchemaNode }; diff --git a/lib/draft2019/index.ts b/lib/draft2019/index.ts index e4b815f..69aa54d 100644 --- a/lib/draft2019/index.ts +++ b/lib/draft2019/index.ts @@ -14,7 +14,7 @@ import { resolveAnyOf } from "../features/anyOf"; import { resolveOneOf } from "../features/oneOf"; import createSchemaOf from "../createSchemaOf"; import getChildSchemaSelection from "../getChildSchemaSelection"; -import step from "./step"; +import step from "../step"; import TYPES from "../draft06/validation/type"; import validate from "../validate"; import { DraftConfig, Draft } from "../draft"; diff --git a/lib/draft2019/step.ts b/lib/draft2019/step.ts deleted file mode 100644 index e286b06..0000000 --- a/lib/draft2019/step.ts +++ /dev/null @@ -1,196 +0,0 @@ -import getTypeOf from "../getTypeOf"; -import createSchemaOf from "../createSchemaOf"; -import { JsonSchema, JsonError, isJsonError, SchemaNode } from "../types"; -import { reduceSchema } from "../reduceSchema"; - -type StepFunction = ( - node: SchemaNode, - key: string, - data: any -) => SchemaNode | JsonError | undefined; - -const stepType: Record = { - array: (node, key, data) => { - const { draft, schema, pointer } = node; - const itemValue = data?.[key]; - const itemsType = getTypeOf(schema.items); - - if (itemsType === "object") { - // @spec: ignore additionalItems, when items is schema-object - return reduceSchema(node.next(schema.items as JsonSchema, key), itemValue) - } - - if (itemsType === "array") { - // @draft >= 7 bool schema, items:[true, false] - if (schema.items[key] === true) { - return node.next(createSchemaOf(itemValue), key); - } - // @draft >= 7 bool schema, items:[true, false] - if (schema.items[key] === false) { - return draft.errors.invalidDataError({ - key, - value: itemValue, - pointer, - schema - }); - } - - if (schema.items[key]) { - return draft.resolveRef(node.next(schema.items[key] as JsonSchema, key)); - } - - if (schema.additionalItems === false) { - return draft.errors.additionalItemsError({ - key, - value: itemValue, - pointer, - schema - }); - } - - if (schema.additionalItems === true || schema.additionalItems === undefined) { - return node.next(createSchemaOf(itemValue), key); - } - - if (getTypeOf(schema.additionalItems) === "object") { - return node.next(schema.additionalItems, key); - } - - throw new Error( - `Invalid schema ${JSON.stringify(schema, null, 2)} for ${JSON.stringify( - data, - null, - 2 - )}` - ); - } - - if (schema.additionalItems !== false && itemValue) { - // @todo reevaluate: incomplete schema is created here - // @todo support additionalItems: {schema} - return node.next(createSchemaOf(itemValue), key); - } - - return new Error(`Invalid array schema for ${key} at ${pointer}`) as JsonError; - }, - - object: (node, key, data) => { - const { draft, pointer } = node; - const reduction = reduceSchema(node, data); - const schema = (reduction.schema ?? reduction) as JsonSchema; - - // @feature properties - const property = schema?.properties?.[key]; - if (property !== undefined) { - // @todo patternProperties also validate properties - - // @feature boolean schema - if (property === false) { - return draft.errors.forbiddenPropertyError({ - property: key, - value: data, - pointer, - schema - }); - - } else if (property === true) { - return node.next(createSchemaOf(data?.[key]), key); - } - - const targetNode = draft.resolveRef(node.next(property as JsonSchema, key)); - if (isJsonError(targetNode)) { - return targetNode; - } - // check if there is a oneOf selection, which must be resolved - if (targetNode && Array.isArray(targetNode.schema.oneOf)) { - // @special case: this is a mix of a schema and optional definitions - // we resolve the schema here and add the original schema to `oneOfSchema` - return draft.resolveOneOf(targetNode, data[key]); - } - return targetNode; - } - - // @feature patternProperties - const { patternProperties } = schema; - if (getTypeOf(patternProperties) === "object") { - // find matching property key - let regex; - const patterns = Object.keys(patternProperties); - for (let i = 0, l = patterns.length; i < l; i += 1) { - regex = new RegExp(patterns[i]); - if (regex.test(key)) { - return node.next(patternProperties[patterns[i]], key); - } - } - } - - // @feature additionalProperties - const { additionalProperties } = schema; - if (getTypeOf(additionalProperties) === "object") { - return node.next(schema.additionalProperties, key); - } - if (data && (additionalProperties === undefined || additionalProperties === true)) { - const generatedSchema = createSchemaOf(data[key]); - return generatedSchema ? node.next(generatedSchema, key) : undefined; - } - - return draft.errors.unknownPropertyError({ - property: key, - value: data, - pointer: `${pointer}`, - schema - }); - } -}; - -/** - * Returns the json-schema of the given object property or array item. - * e.g. it steps by one key into the data - * - * This helper determines the location of the property within the schema (additional properties, oneOf, ...) and - * returns the correct schema. - * - * @param draft - validator - * @param key - property-name or array-index - * @param schema - json schema of current data - * @param data - parent of key - * @param [pointer] - pointer to schema and data (parent of key) - * @return Schema or Error if failed resolving key - */ -export default function step( - node: SchemaNode, - key: string | number, - data?: any -): SchemaNode | JsonError { - const { draft, schema, pointer } = node; - const typeOfData = getTypeOf(data); - let schemaType = schema.type ?? typeOfData; - // @draft >= 4 ? - if (Array.isArray(schemaType)) { - if (!schemaType.includes(typeOfData)) { - return draft.errors.typeError({ - value: data, - pointer, - expected: schema.type, - received: typeOfData, - schema - }); - } - schemaType = typeOfData; - } - const stepFunction = stepType[schemaType]; - if (stepFunction) { - // GET SCHEMA - const schemaResult = stepFunction(node, `${key}`, data); - if (schemaResult === undefined) { - return draft.errors.schemaWarning({ - pointer, - value: data, - schema, - key - }); - } - return schemaResult; - } - return new Error(`Unsupported schema type ${schema.type} for key ${key}`) as JsonError; -} diff --git a/lib/step.ts b/lib/step.ts index 9eb73ce..21b0296 100644 --- a/lib/step.ts +++ b/lib/step.ts @@ -3,11 +3,7 @@ import createSchemaOf from "./createSchemaOf"; import { JsonSchema, JsonError, isJsonError, SchemaNode } from "./types"; import { reduceSchema } from "./reduceSchema"; -type StepFunction = ( - node: SchemaNode, - key: string, - data: any -) => SchemaNode | JsonError | undefined; +type StepFunction = (node: SchemaNode, key: string, data: any) => SchemaNode | JsonError | undefined; const stepType: Record = { array: (node, key, data) => { @@ -158,11 +154,7 @@ const stepType: Record = { * @param [pointer] - pointer to schema and data (parent of key) * @return Schema or Error if failed resolving key */ -export default function step( - node: SchemaNode, - key: string | number, - data?: any -): SchemaNode | JsonError { +export default function step(node: SchemaNode, key: string | number, data?: any): SchemaNode | JsonError { const { draft, schema, pointer } = node; const typeOfData = getTypeOf(data); let schemaType = schema.type ?? typeOfData; @@ -183,16 +175,11 @@ export default function step( const stepFunction = stepType[schemaType]; if (stepFunction) { - const schemaResult = stepFunction(node, `${key}`, data); - if (schemaResult === undefined) { - return draft.errors.schemaWarning({ - pointer, - value: data, - schema, - key - }); + const childNode = stepFunction(node, `${key}`, data); + if (childNode === undefined) { + return draft.errors.schemaWarning({ pointer, value: data, schema, key }); } - return schemaResult; + return childNode; } return new Error(`Unsupported schema type ${schema.type} for key ${key}`) as JsonError;