{"id":14155587,"url":"https://github.com/sagold/json-schema-library","last_synced_at":"2025-05-15T04:02:59.456Z","repository":{"id":12984047,"uuid":"73305475","full_name":"sagold/json-schema-library","owner":"sagold","description":"Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation","archived":false,"fork":false,"pushed_at":"2025-05-03T08:05:56.000Z","size":3814,"stargazers_count":207,"open_issues_count":4,"forks_count":19,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-03T08:23:49.209Z","etag":null,"topics":["customizable","json-schema","library","npm-package","pointer","traversal","validation"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sagold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-11-09T17:24:44.000Z","updated_at":"2025-05-03T08:06:00.000Z","dependencies_parsed_at":"2023-02-18T23:15:51.047Z","dependency_job_id":"673d2bd6-7fae-48dc-b5ba-7d96afc40ed6","html_url":"https://github.com/sagold/json-schema-library","commit_stats":{"total_commits":645,"total_committers":14,"mean_commits":46.07142857142857,"dds":"0.35813953488372097","last_synced_commit":"d7550830b381c18e3afc8f6eb9bf93f32d31a60d"},"previous_names":[],"tags_count":102,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sagold%2Fjson-schema-library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sagold%2Fjson-schema-library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sagold%2Fjson-schema-library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sagold%2Fjson-schema-library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sagold","download_url":"https://codeload.github.com/sagold/json-schema-library/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270640,"owners_count":22042858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["customizable","json-schema","library","npm-package","pointer","traversal","validation"],"created_at":"2024-08-17T08:04:08.936Z","updated_at":"2025-05-15T04:02:59.397Z","avatar_url":"https://github.com/sagold.png","language":"TypeScript","funding_links":[],"categories":["library"],"sub_categories":[],"readme":"[![Npm package version](https://badgen.net/npm/v/json-schema-library)](https://github.com/sagold/json-schema-library/actions/workflows/ci.yaml) [![CI](https://github.com/sagold/json-schema-library/actions/workflows/ci.yaml/badge.svg)](https://github.com/sagold/json-schema-library/actions/workflows/ci.yaml) ![Types](https://badgen.net/npm/types/json-schema-library)\n\n\u003ch1 align=\"center\"\u003e\n    \u003cimg src=\"./docs/json-schema-library-10.png\" width=\"192\" alt=\"json-schema-library\"\u003e\n    \u003cbr/\u003e\n    json-schema-library\n\u003c/h1\u003e\n\n\u003e **json-schema-library** provides tools and utilities for working with JSON Schema - enabling creation, validation, and schema exploration. Unlike most validators and editors, which hide the inner workings, this library is designed for developers building custom tools around JSON Schema. It runs in both Node and browser environments, prioritizing flexibility and extensibility over minimal memory footprint or raw performance.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n    \u003ca href=\"#overview\"\u003e\u003cb\u003eOverview\u003c/b\u003e\u003c/a\u003e · \u003ca href=\"#schemanode-methods\"\u003e\u003cb\u003eMethods\u003c/b\u003e\u003c/a\u003e · \u003ca href=\"#draft-customization\"\u003e\u003cb\u003eCustomization\u003c/b\u003e\u003c/a\u003e · \u003ca href=\"#keyword-extensions\"\u003e\u003cb\u003eExtensions\u003c/b\u003e\u003c/a\u003e · \u003ca href=\"#breaking-changes\"\u003eBreaking Changes\u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n**Quick start**\n\n`npm install json-schema-library`\n\n_json-schema-library_ includes a compileSchema function that converts a JSON Schema into a SchemaNode, which gives you easy-to-use methods for working with the schema.\n\n```ts\nimport { compileSchema, SchemaNode } from \"json-schema-library\";\nimport myJsonSchema from \"./myJsonSchema.json\";\nimport myData from \"./myData.json\";\n\nconst schema: SchemaNode = compileSchema(myJsonSchema);\n// validate data and collect errors if invalid\nconst { valid, errors } = schema.validate(myData);\n// create data which validates to the compiled JSON Schema\nconst defaultData = schema.getData();\n// access a subschema at a specific JSON Pointer location\nconst { node, error } = schema.getNode(\"#/image/title\");\nnode \u0026\u0026 console.log(node.schema);\n```\n\nPer default, `compileSchema` uses the draft-version referenced in `$schema` for _cross-draft_ support. In case $schema is omitted or invalid the latest schema (draft-2020-12) will be used. Customizing draft selection is documented in [draft customization](#draft-customization).\n\n```ts\nconst schemaNode = compileSchema({ $schema: \"draft-07\" });\nconsole.log(schemaNode.getDraftVersion()); // draft-07\n```\n\n## Overview\n\n### compileSchema\n\nUse `compileSchema` once to turn a JSON Schema into a tree of SchemaNodes. After that, you'll work with individual nodes in the tree. You can also pass an options object to `compileSchema` to customize how the nodes are created.\n\n```ts\ntype CompileOptions = {\n    // set of drafts to use\n    drafts: Draft[];\n    // a context to share\n    remote: SchemaNode;\n    // if format-validations should create errors. Defaults to true\n    formatAssertion: boolean | \"meta-schema\";\n    // default options for all calls to node.getData()\n    getDataDefaultOptions?: {\n        // Add all properties (required and optional) to the generated data\n        addOptionalProps?: boolean;\n        // Remove data that does not match input schema. Defaults to false\n        removeInvalidData?: boolean;\n        // Set to false to take default values as they are and not extend them. Defaults to true\n        extendDefaults?: boolean;\n        // Limits how often a $ref should be followed before aborting. Prevents infinite data-structure. Defaults to 1\n        recursionLimit?: number;\n    };\n};\n```\n\nWith this\n\n```ts\nimport { compileSchema, draft04, draft06, draft07, draft2019, draft2020 } from \"json-schema-library\";\n\n// only draft07 is used for all JSON schema\ncompileSchema(mySchema, { drafts: [draft07] });\n\n// the created node will share a context with `anotherSchemaNode` enabling cross schema ref-resolution\n// Note that anotherSchemaNode still uses its own drafts it was compiled with\ncompileSchema(mySchema, { remote: anotherSchemaNode });\n\n// format validation is disabled\ncompileSchema(mySchema, { formatAssertion: false });\n\n// for all calls to getData, `addOptionalProps` is `true` per default\ncompileSchema(mySchema, { getDataDefaultOptions: { addOptionalProps: true } });\n```\n\nDetails on _drafts_ are documented in [draft customization](#draft-customization).\nDetails on `getDataDefaultOptions` are documented in [getData](#getData).\n\n### SchemaNode\n\n`compileSchema` builds a tree where each sub-schema becomes its own SchemaNode. Every node in the tree offers the same set of methods.\nFor example:\n\n```ts\nconst root = compileSchema(mySchema);\nconst rootData = root.getData();\nconst { node: titleNode } = root.getNode(\"#/image/title\");\nconst titleData = titleNode?.getData();\n```\n\n\u003cdetails\u003e\u003csummary\u003eEach node has an identity\u003c/summary\u003e\n\n```ts\nconst titleNode = compileSchema(mySchema).getNode(\"#/image/title\");\nconsole.log(titleNode.evaluationPath); // #/properties/image/properties/title\nconsole.log(titleNode.schemaLocation); // #/properties/image/properties/title\n```\n\n- `evaluationPath` refers to the path in schema and is extended by `$ref`, e.g. if image is defined on `$defs`: `#/properties/image/$ref/properties/title`\n- `schemaLocation` refers to the absolute path within the schema and will not change, e.g. `#/$defs/properties/title`\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eEach node has a reference to its parent node\u003c/summary\u003e\n\nThe parent-node can be a sub-schema or intermediary node:\n\n```ts\nconst root = compileSchema(mySchema);\nconst { node: childNode } = root.getNode(\"#/image\");\nassert(root === childNode.parent);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eAll nodes share a context\u003c/summary\u003e\n\n\u003e It is not advised to work on context directly, but it might be useful in some situations\n\nA context is shared across all nodes of a schema\n\n```ts\nconst root = compileSchema(mySchema);\nconst { node: childNode } = root.getNode(\"#/image\");\nassert(root.context === childNode.context);\n```\n\nAnd some context properties are shared across all schema added as remotes. The property `rootNode` refers to the root-schema for the current node\n\n```ts\nconst root = compileSchema(mySchema);\nconst { node: childNode } = root.getNode(\"#/image\");\nassert(root === childNode.context.rootNode);\n```\n\nNote that rootNodes will change when working across remote schema (using $ref).\n\n\u003c/details\u003e\n\n### Draft Support\n\n_json-schema-library_ fully supports all core features of draft versions draft-04, draft-06, draft-07, draft-2019-09 and draft-2020-12. Additionally, most format-validations are supported per default besides the listed format below. You can always override or extend format validation as is documented in [draft customization](#draft-customization).\n\n\u003cdetails\u003e\u003csummary\u003eOverview draft support\u003c/summary\u003e\n\nDraft support is defined by running a validator against the official [json-schema-test-suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite).\n\n- Test results for _json-schema-library_ can be inspected in [github actions](https://github.com/sagold/json-schema-library/actions/workflows/ci.yaml)\n- A comparison to other validators is listed on [json-schema-benchmark](https://github.com/sagold/json-schema-benchmark)\n\nPlease note that these benchmarks refer to validation only. _json-schema-library_ offers tooling outside of validation and strives to be as spec-compliant as possible.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eOverview format validation support\u003c/summary\u003e\n\n- **`❌ unsupported formats`** iri, iri-reference, idn-hostname\n- **`✅ supported formats`**: date, date-time, date, duration, ecmascript-regex, email, hostname, idn-email, ipv4, ipv6, json-pointer, regex, relative-json-pointer, time, unknown, uri-reference, uri-template, uri, uuid\n\n\u003c/details\u003e\n\n## SchemaNode methods\n\n[addRemoteSchema](#addremoteschema) ·\n[compileSchema](#compileSchema-1) ·\n[createSchema](#createSchema) ·\n[getChildSelection](#getchildselection) ·\n[getData](#getdata) ·\n[getNode](#getnode) ·\n[getNodeChild](#getnodechild) ·\n[getNodeRef](#getnoderef) ·\n[getNodeRoot](#getnoderoot) ·\n[reduceNode](#reducenode) ·\n[toDataNodes](#todatanodes) ·\n[toSchemaNodes](#toschemanodes) ·\n[validate](#validate)\n\n\u003c/details\u003e\n\n### addRemoteSchema\n\n`addRemoteSchema` lets you add additional schemas that can be referenced by an URL using `$ref`. Use this to combine multiple schemas without changing the actual schema.\n\nEach schema is referenced by their unique `$id` (since draft-06, previously `id`). Usually an `$id` is specified as an url, for example `https://mydomain.com/schema/schema-name` or with a file extension like `https://mydomain.com/schema/schema-name.json`.\n\nOn a compiled schema\n\n```ts\nconst schemaNode = compileSchema({\n    $id: \"https://sagold.com/local\",\n    type: \"object\",\n    required: [\"character\"],\n    properties: {\n        character: {\n            $ref: \"https://sagold.com/remote\"\n        }\n    }\n});\n```\n\nuse the exposed method `addRemoteSchema` to add a remote schema for $ref-resolution:\n\n```ts\nschemaNode.addRemoteSchema(\"https://sagold.com/remote\", {\n    $id: \"https://sagold.com/remote\",\n    title: \"A character\",\n    type: \"string\",\n    minLength: 1,\n    maxLength: 1\n});\n```\n\n**Note** the given _url_ and `$id` on the root schema should match. If `$id` is omitted it will be added from the passed url.\n\nTo access the remote schema, add a $ref within your local schema and the remote schema will be resolved automatically:\n\n```ts\nschemaNode.validate({ character: \"AB\" }); // maxLength error\nschemaNode.getData({}); // { character: \"A\" } - default value resolved\n// returns remote schema (from compiled local schema):\nconst { node, error } = schemaNode.getNodeRef(\"https://sagold.com/remote\");\n```\n\n**Note** JSON Schema $ref-keyword can become tricky when combined with $ids in sub-schemas. For more details, see [json-schema.org: Structuring a complex schema](https://json-schema.org/understanding-json-schema/structuring.html#base-uri).\n\n\u003cdetails\u003e\u003csummary\u003eAdding remote schema to compileSchema\u003c/summary\u003e\n\nIt is possible to pass remoteSchema on compileSchema by passing a SchemaNode (with all its remote schemas) in `remote`:\n\n```ts\nconst remote = compileSchema({\n    $id: \"https://sagold.com/remote\",\n    $defs: {\n        character: {\n            title: \"A character\",\n            type: \"string\",\n            minLength: 1,\n            maxLength: 1\n        }\n    }\n});\n\nconst schemaNode = compileSchema({ $ref: \"https://sagold.com/remote#/defs/character\" }, { remote });\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eAccess local subschemas in remote schemas\u003c/summary\u003e\n\nYou can add a local uri reference to the remote schema by using the `#` separator. The following example resolves hte local path `/$defs/character` in the remote schema `https://sagold.com/remote` throught the combined url:\n`https://sagold.com/remote#/$defs/character`\n\n```ts\nconst schemaNode = compileSchema({\n    $id: \"https://sagold.com/local\",\n    $ref: \"https://sagold.com/remote#/$defs/character\"\n});\n\nschemaNode.addRemoteSchema(\"https://sagold.com/remote\", {\n    $defs: {\n        character: {\n            title: \"A character\",\n            type: \"string\",\n            minLength: 1,\n            maxLength: 1\n        }\n    }\n});\n\nschemaNode.validate(\"AB\"); // maxLength error\nschemaNode.getData(\"A\"); // \"A\" - default value resolved\n// returns remote schema (from compiled local schema):\nconst { node, error } = schemaNode.getNodeRef(\"https://sagold.com/remote#/$defs/character\");\n```\n\n**Note** JSON Pointer are not restricted to `$defs` (definitions), but can reference any subschema. For example:\n\n```ts\nconst schemaNode = compileSchema({\n    $id: \"https://sagold.com/local\",\n    $ref: \"https://sagold.com/remote#/properties/character\"\n});\n\nschemaNode.addRemoteSchema(\"https://sagold.com/remote\", {\n    type: \"object\",\n    properties: {\n        character: {\n            title: \"A character\",\n            type: \"string\",\n            minLength: 1,\n            maxLength: 1\n        }\n    }\n});\n\nschemaNode.validate(\"AB\"); // maxLength error\nschemaNode.getData(\"A\"); // \"A\" - default value resolved\n// returns remote schema (from compiled local schema):\nschemaNode.getNodeRef(\"https://sagold.com/remote#/properties/character\");\n```\n\n\u003c/details\u003e\n\n### compileSchema\n\n`node.compileSchema` creates a new schema node in the same context as node. With this, the created node will be able to resolve local `$ref` and remote `$ref` correctly. Note, the created schema will not be part of (linked) from any nodes in the schema-tree.\n\n```ts\nconst someNode = node.compileSchema({ prefixItems: [{ type: \"string\" }, { $ref: \"#/$defs/string\" }] });\n```\n\n### createSchema\n\n`createSchema` returns a simple JSON Schema for the input data.\n\n```ts\nconst schemaNode = compileSchema(mySchema);\nconst schema: JsonSchema = schemaNode.createSchema({ title: \"initial value\" });\nconsole.log(schema); // { type: \"string\" }\n```\n\n### getChildSelection\n\n`getChildSelection` returns a list of available sub-schemas for the given property. In many cases, a single schema will be returned. For _oneOf_-schemas, a list of possible options is returned. This helper always returns a list of schemas.\n\n```ts\nconst schemaNode = compileSchema(mySchema);\nconst schemas: SchemaNode[] = schemaNode.getChildSelection(\"content\");\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema } from \"json-schema-library\";\n\nconst jsonSchema = compileSchema({\n    type: \"object\",\n    properties: {\n        content: {\n            oneOf: [{ type: \"string\" }, { type: \"number\" }]\n        }\n    }\n});\n\nconst childNodes: JsonSchema[] = jsonSchema.getChildSelection(\"content\");\n\nexpect(childNodes.map((n) =\u003e n.schema)).to.deep.equal([{ type: \"string\" }, { type: \"number\" }]);\n```\n\n\u003c/details\u003e\n\n### getData\n\n`getData` creates input data from a JSON Schema that is valid to the schema. Where possible, the JSON Schema `default` property will be used to initially setup input data. Otherwise, the first values encountered (enum values, initial values, etc.) are used to build up the json-data.\n\n```ts\nconst myData = compileSchema(myJsonSchema).getData();\n```\n\nAdditionally, you can pass input data. `getData` will then complement any missing values from the schema, while keeping the initial values.\n\n```ts\nconst myData = compileSchema(myJsonSchema).getData({ name: \"input-data\" });\n```\n\n**Note** If you are using references in your schema, `getData` will only resolve the first _$ref_ in each path, ensuring no infinite data structures are created. In case the limit of **1** _$ref_ resolution is too low, you can modify the value globally one by adjusting the json-schema-library settings:\n\n```ts\nconst myData = compileSchema(myJsonSchema).getData(inputData, { recursionLimit: 2 });\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema } from \"json-schema-library\";\n\nconst myJsonSchema: JsonSchema = {\n    type: \"object\",\n    required: [\"name\", \"option\", \"list\"],\n    properties: {\n        name: { type: \"string\" },\n        option: {\n            type: \"string\",\n            enum: [\"first-option\", \"second-option\"]\n        },\n        list: {\n            type: \"array\",\n            items: {\n                type: \"string\",\n                default: \"new item\"\n            },\n            minItems: 1\n        }\n    }\n};\n\nconst schemaNode = new compileSchema(myJsonSchema);\nconst myData = schemaNode.getData();\n\nexpect(myData).to.deep.equal({\n    name: \"\",\n    option: \"first-option\",\n    list: [\"new item\"]\n});\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample with input data\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema } from \"json-schema-library\";\n\nconst myJsonSchema: JsonSchema = {\n    type: \"object\",\n    required: [\"name\", \"option\", \"list\"],\n    properties: {\n        name: { type: \"string\" },\n        option: {\n            type: \"string\",\n            enum: [\"first-option\", \"second-option\"]\n        },\n        list: {\n            type: \"array\",\n            items: {\n                type: \"string\",\n                default: \"new item\"\n            },\n            minItems: 1\n        }\n    }\n};\n\nconst jsonSchema = compileSchema(myJsonSchema);\nconst myData = jsonSchema.getData({ name: \"input-data\", list: [] });\n\nexpect(myData).to.deep.equal({\n    name: \"input-data\",\n    option: \"first-option\",\n    list: [\"new item\"]\n});\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eOption: extendDefaults (default: false)\u003c/summary\u003e\n\nPer default, `getData` does try to create data that is valid to the json-schema. Example: array-schemas with `minItems: 1` will add one item to fullfil the validation criteria. You can use the option and pass `{ extendDefaults: false }` to override this behaviour with a default value:\n\n```ts\nimport { compileSchema } from \"json-schema-library\";\n\nconst myJsonSchema = {\n    type: \"array\",\n    default: [], // if omitted will add an array item\n    items: {\n        type: \"string\",\n        enum: [\"one\", \"two\"]\n    },\n    minItems: 1 // usually adds an enty, but default states: []\n};\n\nconst myData = compileSchema(myJsonSchema).getData(undefined, { extendDefaults: false });\n\nexpect(myData).to.deep.equal([]);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eOption: addOptionalProps (default: false)\u003c/summary\u003e\n\n`getData` will only add required properties per default:\n\n```ts\nconst data = compileSchema({\n    required: [\"title\"],\n    properties: {\n        title: { type: \"string\" },\n        subTitle: { type: \"string\", default: \"sub-title\" }\n    }\n}).getData(undefined);\nconsole.log(data); // { title: \"\" }\n```\n\nWith `addOptionalProps:true`, `getData` will also add all optional properties\n\n```ts\nconst data = compileSchema({\n    required: [\"title\"],\n    properties: {\n        title: { type: \"string\" },\n        subTitle: { type: \"string\", default: \"sub-title\" }\n    }\n}).getData(undefined, { addOptionalProps: true });\nconsole.log(data); // { title: \"\", subTitle: \"sub-title\" }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eOption: removeInvalidData (default: false)\u003c/summary\u003e\n\nWith `removeInvalidData:true`, `getData` will remove data that is invalid to the given schema;\n\n```ts\nconst data = compileSchema({\n    properties: { valid: { type: \"string\" } },\n    additionalProperties: false\n}).getData({ valid: \"stays\", invalid: \"removed\" }, { removeInvalidData: true });\nconsole.log(data); // { valid: \"stays\" }\n```\n\n`removeInvalidData:true` will _not_ remove data that is valid, but unspecified:\n\n```ts\nconst data = compileSchema({\n    properties: { valid: { type: \"string\" } },\n    additionalProperties: true\n}).getData({ valid: \"stays\", invalid: \"removed\" }, { removeInvalidData: true });\nconsole.log(data); // { valid: \"stays\", invalid: \"removed\" }\n```\n\n\u003c/details\u003e\n\n### getNode\n\n`getNode` returns the JSON Schema from data location specified by a JSON Pointer. In many cases the JSON Schema can be retrieved without passing any data, but in situations where the schema is dynamic (for example in _oneOf_, _dependencies_, etc.), input-data is required or `getNode` will return a _JsonError_ as is done when the JSON Pointer path is invalid.\n\n```ts\nconst { node, error } = compileSchema(mySchema).getNode(\"/list/1/name\", myData);\nif (node) console.log(node.schema);\n```\n\n**Note** `getNode` will return a `node=undefined` for paths that lead to valid properties, but miss a schema definition. For example:\n\n```ts\nconst { node, error } = compileSchema({ type: \"object\" }).getNode(\"/name\");\nconsole.log(node, error); // undefined, undefined\n```\n\nIn case this is unwanted behaviour, use the `withSchemaWarning` option to return a json-error with code `schema-warning` instead:\n\n```ts\nconst schemaNode = compileSchema({ type: \"object\" });\nconst { node, error } = schemaNode.getNode(\"/name\", undefined, { withSchemaWarning: true });\nconsole.log(node?.schema, error); // undefined, { type: \"error\", code: \"schema-warning\" }\n```\n\nOr set `getNode` to return a simple JSON Schema for the found data setting `createSchema: true`:\n\n```ts\nconst schemaNode = compileSchema({ type: \"object\" });\nconst { node, error } = schemaNode.getNode(\"/name\", { name: 123 }, { createSchema: true });\nconsole.log(node?.schema, error); // { type: \"number\" }, undefined\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema } from \"json-schema-library\";\n\nconst mySchema = {\n    type: \"object\",\n    properties: {\n        list: {\n            type: \"array\",\n            items: {\n                oneOf: [\n                    {\n                        type: \"object\",\n                        required: [\"name\"],\n                        properties: {\n                            name: {\n                                type: \"string\",\n                                title: \"name of item\"\n                            }\n                        }\n                    },\n                    {\n                        type: \"object\",\n                        required: [\"description\"],\n                        properties: {\n                            description: {\n                                type: \"string\",\n                                title: \"description of item\"\n                            }\n                        }\n                    }\n                ]\n            }\n        }\n    }\n};\n\nconst { node } = compileSchema(mySchema).getNode(\"/list/1\", {\n    list: [{ description: \"...\" }, { name: \"my-item\" }]\n});\n\nexpect(node.schema).to.deep.equal({\n    type: \"object\",\n    required: [\"name\"],\n    properties: {\n        name: {\n            type: \"string\",\n            title: \"name of item\"\n        }\n    }\n});\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eEvaluating errors\u003c/summary\u003e\n\nAll returned json-errors have a data property with the following properties\n\n- `pointer` JSON Pointer to the location where the error occured. In case of omitted data, this is the last JSON Schema location that could be resolved\n- `schema` the JSON Schema of the last resolved location and the source of the error\n- `value` the data value at this location that could not be resolved\n\n```ts\nconst { error } = schemaNode.getNode(\"/list/1\");\nif (error) {\n    console.log(Object.keys(error.data)); // [pointer, schema, value]\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eAbout JsonPointer\u003c/summary\u003e\n\n**[JSON Pointer](https://tools.ietf.org/html/rfc6901)** defines a string syntax for identifying a specific value within a Json document and is [supported by Json-Schema](https://json-schema.org/understanding-json-schema/structuring.html). Given a Json document, it behaves similar to a [lodash path](https://lodash.com/docs/4.17.5#get) (`a[0].b.c`), which follows JS-syntax, but instead uses `/` separators (e.g., `a/0/b/c`). In the end, you describe a path into the Json data to a specific point.\n\n\u003c/details\u003e\n\n### getNodeChild\n\n`getNodeChild` retrieves the SchemaNode of a child property or index. Using `get` it is possible to incrementally go through the data, retrieving the schema for each next item.\n\n```ts\nconst mySchema = { type: \"object\", properties: { title: { type: \"string\" } } };\nconst root = compileSchema(mySchema);\nconst { node } = root.getNodeChild(\"title\", { title: \"value\" });\nif (node == null) return;\nconsole.log(node.schema);\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema } from \"json-schema-library\";\n\nconst root = compileSchema(mySchema);\nconst localSchema: JsonSchema = {\n    oneOf: [\n        {\n            type: \"object\",\n            properties: { title: { type: \"string\" } }\n        },\n        {\n            type: \"object\",\n            properties: { title: { type: \"number\" } }\n        }\n    ]\n};\n\nconst schema = root.getNodeChild(\"title\", { title: 4 }).node?.schema;\n\nexpect(schema).to.deep.eq({ type: \"number\" });\n```\n\n\u003c/details\u003e\n\n### getNodeRef\n\n`getNodeRef` retrieves the SchemaNode of a `$ref` string.\n\n```ts\nconst root = compileSchema(mySchema);\nroot.addRemoteSchema(\"https://remote.com/schema\", remoteSchema);\n\nroot.getNodeRef(\"#/$defs/title\"); // title-schema of mySchema\nroot.getNodeRef(\"https://remote.com/schema\"); // remoteSchema\n```\n\n### getNodeRoot\n\n`getNodeRoot` returns the rootNode containing the initial JSON Schema\n\n```ts\nconst root = compileSchema(mySchema);\nconst { node } = root.getNode(\"/image/title\");\n\nif (node) {\n    assert(node.getNodeRoot() === root); // success\n}\n```\n\n\u003c/details\u003e\n\n### reduceNode\n\n`reduceNode` compiles dynamic JSON schema keywords of a SchemaNode according to the given data.\nThis utility helps walking down the schema-tree with a set of data and it helps getting a mostly\ncomplete json-schema for a specific data-value.\n\n```ts\nconst { node: reducedNode } = compileSchema({\n    properties: {\n        trigger: { type: \"boolean\"}\n    }\n    dependentSchemas: {\n        trigger: {\n            required: [\"title\"],\n            properties: {\n                title: { type: \"string\" }\n            }\n        }\n    }\n}).reduceNode({ trigger: true });\n\nexpect(reducedNode.schema).to.deep.eq({\n    required: [\"title\"],\n    properties: {\n        trigger: { type: \"boolean\"},\n        title: { type: \"string\" }\n    }\n});\n```\n\n\u003e ⚠️ Please be aware that certain schema-definitions are lost when resolving or merging sub-schemas.\n\u003e This mainly refers to validation-properties, but also some ambigiuous schema might get overriden.\n\n### toDataNodes\n\n`toDataNodes` collects all data-items (_object_, _array_ and _value_) and their SchemaNode and return them as a list of **DataNodes**:\n\n```ts\ntype DataNode = { pointer: string; value: unknown; node: SchemaNode };\nconst schemaNode = compileSchema(mySchema);\nconst nodes: DataNode[] = schemaNode.toDataNodes(myData);\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema, JsonPointer } from \"json-schema-library\";\n\nconst mySchema: JsonSchema = {\n    type: \"array\",\n    items: [{ type: \"number\" }, { type: \"string\" }]\n};\n\nconst schemaNode = compileSchema(mySchema);\nschemaNode.toDataNodes([5, \"nine\"]).map((dataNode) =\u003e ({\n    schema: dataNode.node.schema,\n    value: dataNode.value,\n    pointer: dataNode.pointer\n}));\n\nexpect(calls).to.deep.equal([\n    { schema: mySchema, value: [5, \"nine\"], pointer: \"#\" },\n    { schema: { type: \"number\" }, value: 5, pointer: \"#/0\" },\n    { schema: { type: \"string\" }, value: \"nine\", pointer: \"#/1\" }\n]);\n```\n\n\u003c/details\u003e\n\n### toSchemaNodes\n\n`toSchemaNodes` collects all sub-schema definitions, like in `properties[\"property\"]`, `anyOf[1]`, `contains`, `$defs[\"name\"]`, etc. and returns them as a list of **SchemaNodes**:\n\n```ts\nconst nodes: SchemaNode[] = compileSchema(mySchema).toSchemaNodes();\n```\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nimport { compileSchema, JsonSchema, SchemaNode } from \"json-schema-library\";\n\nconst mySchema: JsonSchema = {\n    type: \"array\",\n    items: {\n        oneOf: [{ type: \"number\" }, { $ref: \"#/$defs/value\" }]\n    },\n    $defs: {\n        value: { type: \"string\" },\n        object: { type: \"object\" }\n    }\n};\n\nconst nodes = compileSchema(mySchema)\n    .toSchemaNodes(myCallback)\n    .map((node) =\u003e node.schema);\n\nexpect(calls).to.deep.equal([\n    mySchema,\n    { oneOf: [{ type: \"number\" }, { $ref: \"#/$defs/value\" }] },\n    { type: \"number\" },\n    { $ref: \"#/$defs/value\" },\n    { type: \"string\" },\n    { type: \"object\" }\n]);\n```\n\n\u003c/details\u003e\n\n### validate\n\n`validate` is a complete _JSON Schema validator_ for your input data. Calling _validate_ will return a list of validation errors for the passed data.\n\n```ts\nconst { valid, errors } = compileSchema(myJsonSchema).validate(myData);\n// { valid: boolean, errors: JsonError[] }\n```\n\n\u003cdetails\u003e\u003csummary\u003eAbout type JsonError\u003c/summary\u003e\n\nIn _json-schema-library_ all errors are in the format of a `JsonError`:\n\n```ts\ntype JsonError = {\n    type: \"error\";\n    code: string;\n    message: string;\n    data?: { [p: string]: any };\n};\n```\n\nIn almost all cases, a JSON Pointer is given on _error.data.pointer_, which points to the source within data where the error occured. For more details on how to work with errors, refer to section [custom errors](#extending-a-draft).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nconst myJsonSchema: JsonSchema = { type: \"object\", additionalProperties: false };\n\nconst { errors } = compileSchema(myJsonSchema).validate({ name: \"my-data\" });\n\nexpect(errors).to.deep.equal([\n    {\n        type: \"error\",\n        code: \"no-additional-properties-error\",\n        message: \"Additional property `name` in `#` is not allowed\",\n        data: { property: \"name\", properties: [], pointer: \"#\" }\n    }\n]);\n```\n\n\u003c/details\u003e\n\nYou can also use async validators to validate data with json-schema. For this, another property asyncErrors is exposed on validate:\n\n```ts\nconst { errorsAsync } = compileSchema(myJsonSchema).validate(myData);\n\nif (errorsAsync.length \u003e 0) {\n    const additionalErrors = (await Promise.all(errorsAsync)).filter((err) =\u003e err != null);\n}\n```\n\nPer default _json-schema-library_ does not contain async validators, so `errorsAsync` is always empty. If you add async validators, a list of `Promise\u003cJsonError|undefined\u003e` is return and you need to resolve and filter non-errors (undefined) yourself.\n\n\u003e **Note** `isValid` only refers to errors. `errorsAsync` has to be evaluated separately\n\n\u003cdetails\u003e\u003csummary\u003eExample Async Validation\u003c/summary\u003e\n\n```ts\nimport { JsonSchemaValidator, draft2020 } from \"json-schema-library\";\n// return Promise\u003cJsonError\u003e\nconst customValidator: JsonSchemaValidator = async ({ node, pointer, data }) =\u003e {\n    return node.createError(\"type-error\", {\n        schema: {},\n        pointer,\n        value: data\n    });\n};\n\nconst draftList = [\n    extendDraft(jsonEditorDraft, {\n        keywords: {\n            custom: customValidator\n        }\n    })\n];\n\nconst { isValid, errorsAsync } = compileSchema({ custom: true }).validate(\"data\");\nconsole.log(isValid, errors.length); // true, 0\n\nconst errors = await Promise.all(errorsAsync);\nconsole.log(errors); /// [{ code: \"type-error\", value: \"data\", pointer: \"#\", ... }]\n```\n\n\u003c/details\u003e\n\n## Draft Customization\n\n[**Extending a Draft**](#extending-a-draft) · [**Keyword**](#keyword)\n\n_json-schema-library_ uses the concept of **drafts** to support different versions of the JSON Schema specification — such as Draft 04, Draft 07, or 2020-12 — and to allow customization of schema behavior.\n\nEach **draft** describes how a schema should be parsed, validated, and interpreted. Drafts can also be extended or modified to change or enhance behavior, such as:\n\n- Replacing or adding new keywords (`oneOf`, `if/then`, custom ones, etc.)\n- Defining or overriding format validators (`format: \"email\"`, etc.)\n- Customizing or localizing error messages\n- Tweaking how schema nodes behave during parsing or resolution\n\nOut of the box, the library exports all compliant JSON Schema drafts:\n\n```ts\nimport { draft04, draft06, draft07, draft2019, draft2020 } from \"json-schema-library\";\n```\n\nWhen you compile a schema, the library will automatically select the correct draft based on the `$schema` field — or fall back to the last draft in the list:\n\n```ts\ncompileSchema(schema, { drafts: [draft04, draft07, draft2020] });\n```\n\nA `Draft` is an object that defines the core behavior and extensions for a schema. It includes:\n\n```ts\ntype Draft = {\n    $schemaRegEx: string;\n    version: DraftVersion;\n    keywords: Keyword[];\n    errors: ErrorConfig;\n    formats: typeof formats;\n    methods: {};\n};\n```\n\nHere’s a breakdown of what each piece does:\n\n**`$schemaRegEx`**\n\nA regex string that identifies whether a draft should be used for a given schema, based on the `$schema` property. For example:\n\n```ts\ndraft.$schemaRegEx === \"draft[-/]2020-12\";\n// matches \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n// matches \"$schema\": \"draft-2020-12\"\n```\n\nWhen compiling, drafts are matched from left to right — the first one that matches is used. If no match is found, the **last draft** in the list is used as a fallback. If you're only using one draft, the `$schemaRegEx` check is skipped.\n\n**`version`**\n\nDescribes the draft version (e.g., `\"2020-12\"`). This is mostly used for debugging and logging.\n\n**`keywords`**\n\nA list of keyword handlers for that draft, such as `properties`, `allOf`, `oneOf`, `$ref`, and more. Each keyword defines how the library should parse and validate that keyword. You can override, extend, or remove any keyword.\nLearn more in [Keyword](#keyword).\n\n**`errors`**\n\nAn object mapping error types to either template strings or error functions. These can be used to customize error messages globally or define more intelligent error generation logic.\n\n**`formats`**\n\nAn object mapping format names (like `\"email\"`, `\"uuid\"`, `\"date-time\"`) to custom validation functions. You can override or add formats depending on your needs.\n\n**`methods`**\n\nDraft-specific implementations for certain core behaviors in `SchemaNode`, such as how child schemas are selected or how schemas are converted to data nodes. These can be overridden in custom drafts if needed.\n\n\u003cdetails\u003e\u003csummary\u003eAvailable methods\u003c/summary\u003e\n\n```ts\ncreateSchema: typeof createSchema;\ngetChildSelection: typeof getChildSelection;\ngetData: typeof getData;\ntoDataNodes: typeof toDataNodes;\n```\n\n\u003c/details\u003e\n\n### Extending a Draft\n\nYou may want to extend a draft when the default JSON Schema behavior does not fit your needs. Whether you want to add new keywords, modify error messages, or define custom formats for your validation, `extendDraft` helps you adjust the draft version to meet your specific requirements.\n\nExamples:\n\n```ts\nimport { extendDraft, draft2020, oneOfFuzzyKeyword, createCustomError, render, ErrorData } from \"json-schema-library\";\n\nconst myDraft = extendDraft(draft2020, {\n    // Match all $schema\n    $schemaRegEx: \"\",\n\n    // Register a custom \"oneOf\" keyword, replacing the existing one\n    keywords: [oneOfFuzzyKeyword],\n\n    formats: {\n        // Add a new \"format\": \"test\", which returns an error when the value is \"test\"\n        test: ({ data, node, pointer }) =\u003e {\n            if (data === \"test\") {\n                return node.createError(\"test-error\", {\n                    schema: node.schema,\n                    pointer: pointer,\n                    value: data,\n                    customValue: \"test\"\n                });\n            }\n        }\n    },\n\n    errors: {\n        // Add a new custom error \"test-error\"\n        \"test-error\": \"Test error for value {{value}} - {{customValue}}\",\n\n        // Overwrite the existing MaxLengthError message\n        \"max-length-error\": \"Too many characters\",\n\n        // Add a dynamic MinLengthError with custom logic\n        \"min-length-error\": (data: ErrorData) =\u003e {\n            if (data.minLength === 1) {\n                return {\n                    type: \"error\",\n                    code: \"min-length-one-error\",\n                    message: \"Input is required\",\n                    data\n                };\n            }\n            return {\n                type: \"error\",\n                code: \"min-length-error\",\n                message: render(\"Value in `{{pointer}}` is `{{length}}`, but should be `{{minimum}}` at minimum\", data),\n                data\n            };\n        }\n    }\n});\n```\n\n### Overwrite a format validator\n\nThe built-in format validators may not always align with your specific requirements. For instance, you might need to validate the output of an `\u003cinput type=\"time\" /\u003e`, which produces values in formats like `HH:MM` or `HH:MM:SS`. In such cases, you can customize or overwrite the format validators to suit your needs using `extendDraft`\n\n\u003cdetails\u003e\n\u003csummary\u003eExample of overwriting a format validator\u003c/summary\u003e\n\n```ts\nimport { extendDraft, draft2020 } from \"json-schema-library\";\n\n/**\n * A Regexp that extends http://tools.ietf.org/html/rfc3339#section-5.6 spec.\n * The specification requires seconds and timezones to be a valid date format.\n *\n * matchTimeSecondsAndTimeOptional matches:\n * - HH:MM:SSz\n * - HH:MM:SS(+/-)HH:MM\n * - HH:MM:SS\n * - HH:MMz\n * - HH:MM(+/-)HH:MM\n * - HH:MM\n */\nconst matchTimeSecondsAndTimeOptional =\n    /^(?\u003ctime\u003e(?:([0-1]\\d|2[0-3]):[0-5]\\d(:(?\u003csecond\u003e[0-5]\\d|60))?))(?:\\.\\d+)?(?\u003coffset\u003e(?:z|[+-]([0-1]\\d|2[0-3])(?::?[0-5]\\d)?)?)$/i;\n\nconst customTimeFormatDraft = extendDraft(draft2020, {\n    formats: {\n        // This example extends the default time formatter which validates against RFC3339\n        time: ({ node, pointer, data }) =\u003e {\n            const { schema } = node;\n            if (typeof data !== \"string\" || data === \"\") {\n                return undefined;\n            }\n\n            // Use the Custom Regex to validate the date and time.\n            const matches = data.match(matchTimeSecondsAndTimeOptional);\n            if (!matches) {\n                return node.createError(\"format-date-time-error\", { value: data, pointer, schema });\n            }\n\n            // leap second\n            if (matches.groups.second === \"60\") {\n                // Omitted the code here for brevity.\n            }\n\n            return undefined;\n        }\n    }\n});\n\nconst { errors, valid } = compileSchema(\n    {\n        type: \"string\",\n        format: \"time\",\n        $schema: \"https://json-schema.org/draft/2020-12/schema\"\n    },\n    { drafts: [customTimeFormatDraft] }\n).validate(\"15:31:12\");\n\nconsole.assert(valid, errors.at(0)?.message);\n```\n\n\u003c/details\u003e\n\n### Keyword\n\n**Keywords** hold the main logic for JSON Schema functionality. Each `Keyword` corresponds to a JSON Schema keyword like `properties`, `prefixItems`, `oneOf`, etc and offers implementations to `parse`, `validate`, `resolve` and `reduce`. Note that support for each implementation is optional, dependending on the feature requirements. The main properties of a `Keyword`:\n\n- a `Keyword` is only processed if the specified `keyword` is available as property on the JSON Schema\n- an optional `order` property may be added as order of keyword execution is sometimes important (`additionalItems` last, `$ref` evaluation first)\n- the list of keywords is unique by property-value `keyword`\n- for a given function `addX`, a function `X` must be present\n\n```ts\ntype Keyword = {\n    id: string;\n    keyword: string;\n    order?: number;\n    parse?: (node: SchemaNode) =\u003e void;\n    addResolve?: (node: SchemaNode) =\u003e boolean;\n    resolve?: JsonSchemaResolver;\n    addValidate?: (node: SchemaNode) =\u003e boolean;\n    validate?: JsonSchemaValidator;\n    addReduce?: (node: SchemaNode) =\u003e boolean;\n    reduce?: JsonSchemaReducer;\n};\n```\n\nFor examples on keyword implementations refer to [./src/keywords](./src/keywords).\n\n**parse**\n\n`parse` will be executed on compile time, usually to add a compiled sub-schema on the parent-node.\n\n\u003cdetails\u003e\u003csummary\u003eExample of keyword using parse\u003c/summary\u003e\n\n```ts\nexport const notKeyword: Keyword = {\n    id: \"not\",\n    keyword: \"not\",\n    parse: parseNot\n};\n\nexport function parseNot(node: SchemaNode) {\n    const { schema, evaluationPath, schemaLocation } = node;\n    if (schema.not != null) {\n        node.not = node.compileSchema(schema.not, `${evaluationPath}/not`, `${schemaLocation}/not`);\n    }\n}\n```\n\n\u003c/details\u003e\n\n**resolve**\n\nA resolver returns a child-schema for a property-key, item-index or undefined if the key does not apply.\n\n\u003cdetails\u003e\u003csummary\u003eExample of keyword using resolve\u003c/summary\u003e\n\n```ts\nexport const propertiesKeyword: Keyword = {\n    id: \"property\",\n    keyword: \"properties\",\n    parse: parseProperties,\n    addResolve: (node: SchemaNode) =\u003e node.properties != null,\n    resolve: propertyResolver\n};\n\nfunction propertyResolver({ node, key }: JsonSchemaResolverParams) {\n    return node.properties?.[key];\n}\n```\n\n\u003c/details\u003e\n\n**reduce**\n\nA reducer replaces the JSON Schema keyword to a simple, static JSON Schema based on the current data\n\n\u003cdetails\u003e\u003csummary\u003eExample of keyword using reduce\u003c/summary\u003e\n\n```ts\nexport const typeKeyword: Keyword = {\n    id: \"type\",\n    keyword: \"type\",\n    addReduce: (node) =\u003e Array.isArray(node.schema.type),\n    reduce: reduceType,\n    addValidate: ({ schema }) =\u003e schema.type != null,\n    validate: validateType\n};\n\nfunction reduceType({ node, pointer, data }: JsonSchemaReducerParams): undefined | SchemaNode {\n    const dataType = getJsonSchemaType(data, node.schema.type);\n    if (dataType !== \"undefined\" \u0026\u0026 Array.isArray(node.schema.type) \u0026\u0026 node.schema.type.includes(dataType)) {\n        return node.compileSchema({ ...node.schema, pointer, type: dataType }, node.evaluationPath);\n    }\n    return undefined;\n}\n```\n\n\u003c/details\u003e\n\nCurrently **keywords** are not exposed per default. You can still access any keyword implementation by retrieving them from a draft:\n\n```ts\nimport { draft07 } from \"json-schema-library\";\nconst dependentSchemasKeyword = draft2020.keywords.find((f) =\u003e f.keyword === \"dependentSchemas\");\n```\n\n## Keyword extensions\n\n### oneOfProperty\n\nFor `oneOf` resolution, JSON Schema states that data is valid if it validates against exactly one of those sub-schemas. In some scenarios this is unwanted behaviour, as the actual `oneOf` schema is known and only validation errors of this exact sub-schema should be returned.\n\nFor an explicit `oneOf` resolution, the JSON Schema may be extended by a property `oneOfProperty`. This will always associate an entry with a matching value (instead of schema validation) and return only this schema or validation errors, depending on the current task. For example:\n\n```ts\nconst schema = {\n    oneOfProperty: \"id\",\n    oneOf: [\n        {\n            type: \"object\",\n            properties: { id: { const: \"1\" }, title: { type: \"number\" } }\n        },\n        {\n            type: \"object\",\n            properties: { id: { const: \"2\" }, title: { type: \"number\" } }\n        },\n        {\n            type: \"object\",\n            properties: { id: { const: \"3\" }, title: { type: \"number\" } }\n        }\n    ]\n};\n\nconst resolvedNode = compileSchema(schema).reduce({ id: \"2\", title: \"not a number\" });\n\n// will always return (even if invalid)\nexpect(resolvedNode?.schema).to.deep.eq({\n    type: \"object\",\n    properties: { id: { const: \"2\" }, title: { type: \"number\" } }\n});\n```\n\n### oneOfFuzzyKeyword\n\nIf you're working with complex schemas that use the `oneOf` keyword to validate multiple options, `oneOfFuzzyKeyword` offers an alternative approach. It scores the schemas to return the best match, even if none of the schemas fully validate the input data. This makes error messages more readable and helps identify the most appropriate schema when multiple options exist.\n\n`oneOfFuzzyKeyword` helps when no schema fully validates the data but you want to prioritize schemas based on how well they fit the input. This makes it easier to interpret validation results for complex conditions.\n\n`oneOfFuzzyKeyword` is exposed by _json-schema-library_ and can be used to extend any draft.\n\n```ts\nimport { extendDraft, oneOfFuzzyKeyword, draft2020 } from \"json-schema-library\";\n\nconst myDraft = extendDraft(draft2020, {\n    keywords: [oneOfFuzzyKeyword]\n});\n```\n\n### errorMessages\n\nYou can set custom errors messages locally by using the errors-keyword:\n\n```ts\nconst { errors } = compileSchema({\n    type: \"array\",\n    minItems: 2,\n    errorMessages: {\n        \"min-items-error\": \"Custom error {{minItems}}\"\n    }\n}).validate([1]);\n\nassert.deepEqual(errors[0].message, \"Custom error 2\");\n```\n\n## Breaking Changes\n\n### v10.0.0\n\n\u003e This update involves some significant changes in how you work with the library, so please carefully review the migration guide and adjust your implementation accordingly.\n\nIn version v10.0.0, we've made significant changes to the library’s API, particularly in how we handle drafts and schemas. These changes are required to support features like `dynamicAnchor`, `unevaluatedItems`, and `oneOfIndex` and to integrate with the headless-json-editor. The previous approach of directly working with JSON schema objects lacked the flexibility needed for more advanced features and extensibility.\n\nThe new implementation revolves around compiling schemas into a **SchemaNode** tree. This change offers a more fitting, simpler, and extensible approach to working with JSON schemas.\n\n#### Key Changes:\n\n- **Compile Schema**: The `compileSchema` function now replaces the previous Draft-Class approach.\n- **SchemaNode Representation**: All schemas are now represented as `SchemaNode`, which holds the schema and provides an easier way to work with them.\n\n#### Breaking Changes:\n\n**`compileSchema`** is now a standalone function and replaces the `Draft` class. All return values for JSON Schema are now `SchemaNode` objects that contain a `schema` property.\n\n```ts\n// PREVIOUSLY\nconst draft = new Draft(schema);\n\n// NOW\nconst node = compileSchema(schema);\n```\n\n**Changed Methods**:\n\n- `draft.createSchemaOf(schema)` → `node.createSchema(schema)`\n- `draft.each(data, callback)` → `const nodes = node.toDataNodes(data)`\n- `draft.eachSchema(callback)` → `const nodes = node.toSchemaNodes()`\n- `draft.getChildSchemaSelection(property)` → `node.getChildSelection(property)`\n- `draft.getNode(options)` → `node.getNode(pointer, data, options)`\n- `draft.getTemplate(inputData)` → `node.getData(inputData)`\n- `draft.isValid(data)` → `node.validate(data).valid`\n- `draft.step(property, data)` → `node.getNodeChild(property, data)`\n\n**Renamed Properties**: `templateDefaultOptions` → `getDataDefaultOptions`\n\n**Draft Customization**: Customizing drafts has changed completely. The previous methods of extending drafts are no longer valid, and draft handling is now centered around `SchemaNode`.\n\n**Removed Error Property `name`**: Error property `name` has been removed from `JsonError` in favor of `code`.\n\n**Removed Configuration Option**: The `templateDefaultOptions` property has been removed from the global settings object. You should now configure it using the `compileSchema` options:\n\n```ts\ncompileSchema(schema, {\n    getDataDefaultOptions: {\n        addOptionalProps: false,\n        removeInvalidData: false,\n        extendDefaults: true\n    }\n});\n```\n\n**Changed remote $id support** in `addRemoteSchema`. An `$id` has to be a valid url (previously any value was accepted)\n\n### v9.0.0\n\n**breaking changes**:\n\n- _getSchema_ signature changed in favour of an options object. Instead of `draft.getNode(pointer, data)` arguments have to be passed as an object `draft.getNode({ pointer, data })`. This removes setting unwanted optional arguments and keeps the api more stable in the future (e.g. `withSchemaWarning` option)\n- _JsonError_ now must expose `pointer`, `schema` and `value` consistently on data property\n\n**updates**\n\n- _getSchema_ consistently returns errors and can return errors for empty schema using `withSchemaWarning` option\n\n### v8.0.0\n\nWith version `v8.0.0`, _getData_ was improved to better support optional properties and utilize existing core logic, making it more reliable. Breaking changes:\n\n- Renamed `JSONError` to `JsonError` and `JSONSchema` to `JsonSchema`\n- `getData` only adds required properties. Behaviour can be changed by [getData default options](#getData-default-options)\n- Internal schema property `oneOfSchema` has been replaced by `schema.getOneOfOrigin()`\n- Changed `unique-items-error` to point to error for duplicated item and changed data-properties\n- Removed `SchemaService` as it was no longer used nor tested\n\n\u003cdetails\u003e\u003csummary\u003eExposed new helper functions\u003c/summary\u003e\n\n- `mergeSchema` - Merges to two json schema\n- `reduceNode` - Reduce schema by merging dynamic constructs into a static json schema omitting those properties\n- `isDynamicSchema` - Returns true if the passed schema contains dynamic properties (_if_, _dependencies_, _allOf_, etc)\n- `resolveDynamicSchema` - Resolves all dynamic schema definitions for the given input data and returns the resulting JSON Schema without any dynamic schema definitions.\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsagold%2Fjson-schema-library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsagold%2Fjson-schema-library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsagold%2Fjson-schema-library/lists"}