├── .eslintrc.js ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug-or-error-report.md │ ├── change.md │ ├── compatibility.md │ ├── installation.md │ └── typescript.md ├── PULL_REQUEST_TEMPLATE.md ├── config.yml ├── dependabot.yml ├── img │ ├── gap.svg │ └── reserved.svg └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── .npmrc ├── .prettierignore ├── .runkit_example.js ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── benchmark ├── jtd.js └── package.json ├── bower.json ├── docs ├── .vuepress │ ├── components │ │ ├── Button.vue │ │ ├── Column.vue │ │ ├── Columns.vue │ │ ├── Contributors.vue │ │ ├── Feature.vue │ │ ├── Feature │ │ │ └── arrow.svg │ │ ├── Features.vue │ │ ├── FooterColumn.vue │ │ ├── FooterColumns.vue │ │ ├── GitHub.vue │ │ ├── HeroSection.vue │ │ ├── HeroSection │ │ │ └── hero-image.svg │ │ ├── HomePage.vue │ │ ├── HomeSection.vue │ │ ├── NewsHome.vue │ │ ├── NewsIndex.vue │ │ ├── NewsPost.vue │ │ ├── NewsPostMeta.vue │ │ ├── Projects.vue │ │ ├── Sponsors.vue │ │ ├── Subscribe.vue │ │ ├── Testimonial.vue │ │ ├── Testimonial │ │ │ ├── testimonials-blue.svg │ │ │ └── testimonials-green.svg │ │ └── Testimonials.vue │ ├── config.js │ ├── public │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── IstokWeb-Regular.ttf │ │ │ └── Raleway-VariableFont_wght.ttf │ │ └── img │ │ │ ├── ajv.png │ │ │ ├── ajv.svg │ │ │ ├── apple-touch-icon.png │ │ │ ├── gap.svg │ │ │ ├── microsoft.png │ │ │ ├── mozilla.svg │ │ │ ├── openjs.png │ │ │ ├── reserved.svg │ │ │ ├── retool.jpg │ │ │ ├── retool.svg │ │ │ ├── simplex.svg │ │ │ └── tidelift.svg │ ├── styles │ │ ├── index.styl │ │ └── palette.styl │ └── theme │ │ ├── LICENSE │ │ ├── components │ │ ├── AlgoliaSearchBox.vue │ │ ├── DropdownLink.vue │ │ ├── DropdownTransition.vue │ │ ├── Home.vue │ │ ├── NavLink.vue │ │ ├── NavLinks.vue │ │ ├── Navbar.vue │ │ ├── Page.vue │ │ ├── PageEdit.vue │ │ ├── PageNav.vue │ │ ├── Sidebar.vue │ │ ├── SidebarButton.vue │ │ ├── SidebarGroup.vue │ │ ├── SidebarLink.vue │ │ └── SidebarLinks.vue │ │ ├── global-components │ │ ├── Badge.vue │ │ ├── CodeBlock.vue │ │ └── CodeGroup.vue │ │ ├── index.js │ │ ├── layouts │ │ ├── 404.vue │ │ └── Layout.vue │ │ ├── noopModule.js │ │ ├── styles │ │ ├── arrow.styl │ │ ├── code.styl │ │ ├── config.styl │ │ ├── custom-blocks.styl │ │ ├── index.styl │ │ ├── mobile.styl │ │ ├── toc.styl │ │ └── wrapper.styl │ │ └── util │ │ └── index.js ├── README.md ├── api.md ├── codegen.md ├── coercion.md ├── components.md ├── faq.md ├── guide │ ├── async-validation.md │ ├── combining-schemas.md │ ├── environments.md │ ├── formats.md │ ├── getting-started.md │ ├── managing-schemas.md │ ├── modifying-data.md │ ├── schema-language.md │ ├── typescript.md │ ├── user-keywords.md │ └── why-ajv.md ├── json-schema.md ├── json-type-definition.md ├── keywords.md ├── news │ ├── 2020-08-14-mozilla-grant-openjs-foundation.md │ ├── 2020-12-15-ajv-version-7-released.md │ ├── 2021-03-07-ajv-supports-json-type-definition.md │ ├── 2021-03-27-ajv-version-8-released.md │ ├── 2021-04-24-ajv-online-event.md │ ├── 2021-05-24-ajv-online-event-video.md │ ├── 2021-07-22-ajv-microsoft-foss-fund-award.md │ └── README.md ├── options.md ├── packages │ └── README.md ├── projects │ ├── 0x.png │ ├── autorest.png │ ├── aws-amplify.png │ ├── backstage.svg │ ├── dependency-cruiser.png │ ├── eslint.png │ ├── express-gateway.svg │ ├── fast.svg │ ├── fastify.png │ ├── flipper.png │ ├── mdn.svg │ ├── middy.png │ ├── netlify-cms.png │ ├── ng-alain.svg │ ├── nodered.png │ ├── nx.svg │ ├── quicktype.svg │ ├── rjsf.png │ ├── rushstack.svg │ ├── rxdb.svg │ ├── stoplight.png │ ├── taskcluster.png │ ├── teambit.png │ ├── theia.svg │ ├── tsdoc.svg │ ├── tsed.png │ ├── vercel.svg │ ├── vue-form-generator.png │ ├── webhint.jpg │ ├── webpack.svg │ └── zigbee2mqtt.png ├── security.md ├── standalone.md ├── strict-mode.md ├── testimonials.md └── v6-to-v8-migration.md ├── karma.conf.js ├── lib ├── 2019.ts ├── 2020.ts ├── ajv.ts ├── compile │ ├── codegen │ │ ├── code.ts │ │ ├── index.ts │ │ └── scope.ts │ ├── errors.ts │ ├── index.ts │ ├── jtd │ │ ├── parse.ts │ │ ├── serialize.ts │ │ └── types.ts │ ├── names.ts │ ├── ref_error.ts │ ├── resolve.ts │ ├── rules.ts │ ├── util.ts │ └── validate │ │ ├── applicability.ts │ │ ├── boolSchema.ts │ │ ├── dataType.ts │ │ ├── defaults.ts │ │ ├── index.ts │ │ ├── keyword.ts │ │ └── subschema.ts ├── core.ts ├── jtd.ts ├── refs │ ├── data.json │ ├── json-schema-2019-09 │ │ ├── index.ts │ │ ├── meta │ │ │ ├── applicator.json │ │ │ ├── content.json │ │ │ ├── core.json │ │ │ ├── format.json │ │ │ ├── meta-data.json │ │ │ └── validation.json │ │ └── schema.json │ ├── json-schema-2020-12 │ │ ├── index.ts │ │ ├── meta │ │ │ ├── applicator.json │ │ │ ├── content.json │ │ │ ├── core.json │ │ │ ├── format-annotation.json │ │ │ ├── meta-data.json │ │ │ ├── unevaluated.json │ │ │ └── validation.json │ │ └── schema.json │ ├── json-schema-draft-06.json │ ├── json-schema-draft-07.json │ ├── json-schema-secure.json │ └── jtd-schema.ts ├── runtime │ ├── equal.ts │ ├── parseJson.ts │ ├── quote.ts │ ├── re2.ts │ ├── timestamp.ts │ ├── ucs2length.ts │ ├── uri.ts │ └── validation_error.ts ├── standalone │ ├── index.ts │ └── instance.ts ├── types │ ├── index.ts │ ├── json-schema.ts │ └── jtd-schema.ts └── vocabularies │ ├── applicator │ ├── additionalItems.ts │ ├── additionalProperties.ts │ ├── allOf.ts │ ├── anyOf.ts │ ├── contains.ts │ ├── dependencies.ts │ ├── dependentSchemas.ts │ ├── if.ts │ ├── index.ts │ ├── items.ts │ ├── items2020.ts │ ├── not.ts │ ├── oneOf.ts │ ├── patternProperties.ts │ ├── prefixItems.ts │ ├── properties.ts │ ├── propertyNames.ts │ └── thenElse.ts │ ├── code.ts │ ├── core │ ├── id.ts │ ├── index.ts │ └── ref.ts │ ├── discriminator │ ├── index.ts │ └── types.ts │ ├── draft2020.ts │ ├── draft7.ts │ ├── dynamic │ ├── dynamicAnchor.ts │ ├── dynamicRef.ts │ ├── index.ts │ ├── recursiveAnchor.ts │ └── recursiveRef.ts │ ├── errors.ts │ ├── format │ ├── format.ts │ └── index.ts │ ├── jtd │ ├── discriminator.ts │ ├── elements.ts │ ├── enum.ts │ ├── error.ts │ ├── index.ts │ ├── metadata.ts │ ├── nullable.ts │ ├── optionalProperties.ts │ ├── properties.ts │ ├── ref.ts │ ├── type.ts │ ├── union.ts │ └── values.ts │ ├── metadata.ts │ ├── next.ts │ ├── unevaluated │ ├── index.ts │ ├── unevaluatedItems.ts │ └── unevaluatedProperties.ts │ └── validation │ ├── const.ts │ ├── dependentRequired.ts │ ├── enum.ts │ ├── index.ts │ ├── limitContains.ts │ ├── limitItems.ts │ ├── limitLength.ts │ ├── limitNumber.ts │ ├── limitProperties.ts │ ├── multipleOf.ts │ ├── pattern.ts │ ├── required.ts │ └── uniqueItems.ts ├── package.json ├── rollup.config.js ├── scripts ├── .eslintrc.yml ├── bundle.js ├── get-ajv-packages ├── get-contributors.js ├── jsontests.js ├── prepare-site ├── prepare-tests ├── publish-bundles └── publish-site ├── spec ├── .eslintrc.yml ├── _json │ └── README.md ├── after_test.ts ├── ajv.spec.ts ├── ajv.ts ├── ajv2019.ts ├── ajv2020.ts ├── ajv_all_instances.ts ├── ajv_async_instances.ts ├── ajv_instances.ts ├── ajv_jtd.ts ├── ajv_options.ts ├── ajv_standalone.ts ├── async.spec.ts ├── async │ ├── boolean.json │ ├── compound.json │ ├── format.json │ ├── items.json │ ├── keyword.json │ ├── no_async.json │ └── properties.json ├── async_schemas.spec.ts ├── async_validate.spec.ts ├── boolean.spec.ts ├── chai.ts ├── chai_type.ts ├── codegen.spec.ts ├── coercion.spec.ts ├── discriminator.spec.ts ├── dynamic-ref.spec.ts ├── errors.spec.ts ├── extras.spec.ts ├── extras │ ├── $data │ │ ├── absolute_ref.json │ │ ├── const.json │ │ ├── enum.json │ │ ├── exclusiveMaximum.json │ │ ├── exclusiveMinimum.json │ │ ├── format.json │ │ ├── maxItems.json │ │ ├── maxLength.json │ │ ├── maxProperties.json │ │ ├── maximum.json │ │ ├── minItems.json │ │ ├── minLength.json │ │ ├── minProperties.json │ │ ├── minimum.json │ │ ├── multipleOf.json │ │ ├── pattern.json │ │ ├── required.json │ │ └── uniqueItems.json │ ├── const.json │ ├── contains.json │ ├── exclusiveMaximum.json │ └── exclusiveMinimum.json ├── issues │ ├── 1001_addKeyword_and_schema_without_id.spec.ts │ ├── 1344_non_root_recursive_ref_standalone.spec.ts │ ├── 1414_base_uri_change.spec.ts │ ├── 1501_jtd_many_properties.spec.ts │ ├── 1515_evaluated_properties_nested_anyof.spec.ts │ ├── 1539_add_keyword_name_to_validation_error.spec.ts │ ├── 1625_evaluated_truthy_pattern_properties.spec.ts │ ├── 1683_re2_engine.spec.ts │ ├── 1819_mincontains.spec.ts │ ├── 181_allErrors_custom_keyword_skipped.spec.ts │ ├── 182_nan_validation.spec.ts │ ├── 1935_integer_narrowing_subschema.spec.ts │ ├── 1949_jtd_empty_values.spec.ts │ ├── 1971_jtd_discriminator.spec.ts │ ├── 2001_jtd_only_optional_properties.spec.ts │ ├── 204_options_schemas_data_together.spec.ts │ ├── 210_mutual_recur_frags.spec.ts │ ├── 240_mutual_recur_frags_common_ref.spec.ts │ ├── 259_validate_meta_against_itself.spec.ts │ ├── 273_error_schemaPath_refd_schema.spec.ts │ ├── 342_uniqueItems_non-json_objects.spec.ts │ ├── 485_type_validation_priority.spec.ts │ ├── 50_refs_with_definitions.spec.ts │ ├── 521_wrong_warning_id_property.spec.ts │ ├── 743_removeAdditional_to_remove_proto.spec.ts │ ├── 768_passContext_recursive_ref.spec.ts │ ├── 815_id_updates_ref_base.spec.ts │ ├── 8_shared_refs.spec.ts │ ├── 955_removeAdditional_custom_keywords.spec.ts │ └── re2.ts ├── javacript.spec.js ├── json-schema.spec.ts ├── json_parse_tests.json ├── jtd-schema.spec.ts ├── jtd-timestamps.spec.ts ├── keyword.spec.ts ├── options │ ├── comment.spec.ts │ ├── int32range.spec.ts │ ├── meta_validateSchema.spec.ts │ ├── nullable.spec.ts │ ├── options_add_schemas.spec.ts │ ├── options_code.spec.ts │ ├── options_refs.spec.ts │ ├── options_reporting.spec.ts │ ├── options_validation.spec.ts │ ├── ownProperties.spec.ts │ ├── removeAdditional.spec.ts │ ├── schemaId.spec.ts │ ├── strict.spec.ts │ ├── strictDefaults.spec.ts │ ├── strictKeywords.spec.ts │ ├── strictNumbers.spec.ts │ ├── unicodeRegExp.spec.ts │ ├── unknownFormats.spec.ts │ └── useDefaults.spec.ts ├── remotes │ ├── bar.json │ ├── buu.json │ ├── first.json │ ├── foo.json │ ├── hyper-schema.json │ ├── name.json │ ├── node.json │ ├── scope_change.json │ ├── second.json │ └── tree.json ├── resolve.spec.ts ├── schema-tests.spec.ts ├── security.spec.ts ├── security │ ├── array.json │ ├── object.json │ └── string.json ├── standalone.spec.ts ├── tests │ ├── issues │ │ ├── 12_restoring_root_after_resolve.json │ │ ├── 13_root_ref_in_ref_in_remote_ref.json │ │ ├── 14_ref_in_remote_ref_with_id.json │ │ ├── 1668_not_with_other_keywords.json │ │ ├── 170_ref_and_id_in_sibling.json │ │ ├── 17_escaping_pattern_property.json │ │ ├── 19_required_many_properties.json │ │ ├── 1_ids_in_refs.json │ │ ├── 20_failing_to_parse_schema.json │ │ ├── 226_json_with_control_chars.json │ │ ├── 27_1_recursive_raml_schema.json │ │ ├── 27_recursive_reference.json │ │ ├── 28_escaping_pattern_error.json │ │ ├── 2_root_ref_in_ref.json │ │ ├── 311_quotes_in_refs.json │ │ ├── 33_json_schema_latest.json │ │ ├── 413_dependencies_with_quote.json │ │ ├── 490_integer_validation.json │ │ ├── 502_contains_empty_array_with_ref_in_another_property.json │ │ ├── 5_adding_dependency_after.json │ │ ├── 5_recursive_references.json │ │ ├── 62_resolution_scope_change.json │ │ ├── 63_id_property_not_in_schema.json │ │ ├── 70_1_recursive_hash_ref_in_remote_ref.json │ │ ├── 70_swagger_schema.json │ │ ├── 861_empty_propertynames.json │ │ ├── 87_$_property.json │ │ └── 94_dependencies_fail.json │ ├── rules │ │ ├── allOf.json │ │ ├── anyOf.json │ │ ├── comment.json │ │ ├── dependencies.json │ │ ├── format.json │ │ ├── if.json │ │ ├── items.json │ │ ├── oneOf.json │ │ ├── required.json │ │ ├── type.json │ │ └── uniqueItems.json │ └── schemas │ │ ├── advanced.json │ │ ├── basic.json │ │ ├── complex.json │ │ ├── complex2.json │ │ ├── complex3.json │ │ ├── cosmicrealms.json │ │ └── medium.json ├── tsconfig.json └── types │ ├── async-validate.spec.ts │ ├── error-parameters.spec.ts │ ├── json-schema.spec.ts │ └── jtd-schema.spec.ts └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const jsConfig = require("@ajv-validator/config/.eslintrc_js") 2 | const tsConfig = require("@ajv-validator/config/.eslintrc") 3 | 4 | module.exports = { 5 | env: { 6 | es6: true, 7 | node: true, 8 | }, 9 | overrides: [ 10 | jsConfig, 11 | { 12 | ...tsConfig, 13 | files: ["*.ts"], 14 | rules: { 15 | ...tsConfig.rules, 16 | complexity: ["error", 17], 17 | "@typescript-eslint/no-empty-function": "off", 18 | "@typescript-eslint/no-explicit-any": "off", 19 | "@typescript-eslint/no-floating-promises": "off", 20 | "@typescript-eslint/no-implied-eval": "off", 21 | "@typescript-eslint/no-invalid-this": "off", 22 | "@typescript-eslint/no-parameter-properties": "off", 23 | "@typescript-eslint/no-unnecessary-condition": "warn", 24 | "@typescript-eslint/no-unsafe-assignment": "off", 25 | "@typescript-eslint/no-unsafe-member-access": "off", 26 | "@typescript-eslint/restrict-template-expressions": "off", 27 | }, 28 | }, 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @epoberezkin 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: epoberezkin 2 | tidelift: "npm/ajv" 3 | open_collective: "ajv" 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/change.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature or change proposal 3 | about: For proposals of new features, options or some other improvements 4 | title: "" 5 | labels: "enhancement" 6 | assignees: "" 7 | --- 8 | 9 | 16 | 17 | **What version of Ajv you are you using?** 18 | 19 | **What problem do you want to solve?** 20 | 21 | **What do you think is the correct solution to problem?** 22 | 23 | **Will you be able to implement it?** 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/compatibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Browser and compatibility issue 3 | about: For issues that only happen in a specific environment 4 | title: "" 5 | labels: "compatibility" 6 | assignees: "" 7 | --- 8 | 9 | 16 | 17 | **The version of Ajv you are using** 18 | 19 | **The environment you have the problem with** 20 | 21 | **Your code (please make it as small as possible to reproduce the issue)** 22 | 23 | **If your issue is in the browser, please list the other packages loaded in the page in the order they are loaded. Please check if the issue gets resolved (or results change) if you move Ajv bundle closer to the top** 24 | 25 | **Results in node.js v8+** 26 | 27 | **Results and error messages in your platform** 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Installation and dependency issue 3 | about: For issues that happen during installation 4 | title: "" 5 | labels: "installation" 6 | assignees: "" 7 | --- 8 | 9 | 21 | 22 | **The version of Ajv you are using** 23 | 24 | **Operating system and node.js version** 25 | 26 | **Package manager and its version** 27 | 28 | **Link to (or contents of) package.json** 29 | 30 | **Error messages** 31 | 32 | **The output of `npm ls`** 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Missing or incorrect type definition 3 | about: Please use for issues related to typescript types 4 | title: "" 5 | labels: "typescript" 6 | assignees: "" 7 | --- 8 | 9 | 15 | 16 | **What version of Ajv are you using? Does the issue happen if you use the latest version?** 17 | 18 | **Your typescript code** 19 | 20 | 23 | 24 | ```typescript 25 | 26 | ``` 27 | 28 | **Typescript compiler error messages** 29 | 30 | ``` 31 | 32 | ``` 33 | 34 | **Describe the change that should be made to address the issue?** 35 | 36 | **Are you going to resolve the issue?** 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | **What issue does this pull request resolve?** 13 | 14 | **What changes did you make?** 15 | 16 | **Is there anything that requires more attention while reviewing?** 17 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Please supply comments to be used for GitHub labels 2 | githubLabels: 3 | bug: > 4 | Bug confirmed - to be fixed. PR is welcome! 5 | 6 | # duplicate: > 7 | # enhancement: > 8 | # good first issue: > 9 | # help wanted: > 10 | # invalid: > 11 | # question: > 12 | # wont fix: > 13 | 14 | bug report: > 15 | Thank you for the report! If you didn't post a code sample to RunKit yet, 16 | please clone this notebook https://runkit.com/esp/ajv-issue, 17 | post the code sample that demonstrates the bug and post the link here. 18 | It will speed up the investigation and fixing! 19 | 20 | json schema: > 21 | This question is about the usage of JSON Schema specification - it is not specific to Ajv. 22 | Please use JSON Schema reference materials or [submit the question to Stack Overflow](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv). 23 | 24 | - [JSON Schema specification](http://json-schema.org/) 25 | 26 | - [Tutorial by Space Telescope Science Institute](http://json-schema.org/understanding-json-schema/) 27 | 28 | - [validation keywords](https://github.com/ajv-validator/ajv#validation-keywords) (in Ajv docs) 29 | 30 | - [combining schemas](https://github.com/ajv-validator/ajv#ref) (in Ajv docs) 31 | 32 | - [Tutorial by @epoberezkin](https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343) 33 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: "@types/node" 10 | versions: 11 | - 15.0.0 12 | - dependency-name: eslint-config-prettier 13 | versions: 14 | - 8.0.0 15 | - 8.1.0 16 | - 8.2.0 17 | - dependency-name: karma 18 | versions: 19 | - 6.0.3 20 | - 6.0.4 21 | - 6.1.0 22 | - 6.1.1 23 | - 6.1.2 24 | - 6.2.0 25 | - 6.3.0 26 | - 6.3.1 27 | -------------------------------------------------------------------------------- /.github/img/gap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: ["*"] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x, 18.x, 20.x, 21.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: git submodule update --init 25 | - name: update website 26 | if: ${{ github.event_name == 'push' && matrix.node-version == '16.x' }} 27 | run: ./scripts/publish-site 28 | env: 29 | GH_TOKEN_PUBLIC: ${{ secrets.GH_TOKEN_PUBLIC }} 30 | GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }} 31 | GIT_USER_NAME: ${{ secrets.GIT_USER_NAME }} 32 | - run: npm run build 33 | - run: npm run test-ci 34 | - name: coveralls 35 | uses: coverallsapp/github-action@v2 36 | with: 37 | github-token: ${{ secrets.GITHUB_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish-npm: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 18 15 | registry-url: https://registry.npmjs.org/ 16 | - run: npm install 17 | - run: git submodule update --init 18 | - run: npm run test-ci 19 | - name: Publish beta version to npm 20 | if: ${{ github.event.release.prerelease }} 21 | run: npm publish --tag beta 22 | env: 23 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | - name: Publish to npm 25 | if: ${{ !github.event.release.prerelease }} 26 | run: npm publish 27 | env: 28 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | - name: Commit bundles to ajv-dist 30 | run: ./scripts/publish-bundles 31 | env: 32 | GH_TOKEN_PUBLIC: ${{ secrets.GH_TOKEN_PUBLIC }} 33 | GIT_USER_EMAIL: ${{ secrets.GIT_USER_EMAIL }} 34 | GIT_USER_NAME: ${{ secrets.GIT_USER_NAME }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | .nyc_output 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | .DS_Store 31 | 32 | # Browserified tests 33 | .browser 34 | 35 | # compiled typescript 36 | dist/ 37 | 38 | # browser bundles 39 | bundle/ 40 | 41 | package-lock.json 42 | 43 | spec/_json/*.js 44 | 45 | # docs 46 | docs/code_of_conduct.md 47 | docs/contributing.md 48 | docs/license.md 49 | docs/.vuepress/components/Contributors/ 50 | docs/packages/* 51 | !docs/packages/README.md 52 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "spec/JSON-Schema-Test-Suite"] 2 | path = spec/JSON-Schema-Test-Suite 3 | url = https://github.com/json-schema/JSON-Schema-Test-Suite.git 4 | [submodule "spec/json-typedef-spec"] 5 | path = spec/json-typedef-spec 6 | url = https://github.com/jsontypedef/json-typedef-spec.git 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | spec/JSON-Schema-Test-Suite 2 | spec/json-typedef-spec 3 | .browser 4 | coverage 5 | dist 6 | bundle 7 | .nyc_output 8 | spec/_json 9 | docs/.vuepress/components/_contributors.js 10 | -------------------------------------------------------------------------------- /.runkit_example.js: -------------------------------------------------------------------------------- 1 | const Ajv = require("ajv") 2 | const ajv = new Ajv({allErrors: true}) 3 | 4 | const schema = { 5 | type: "object", 6 | properties: { 7 | foo: {type: "string"}, 8 | bar: {type: "number", maximum: 3}, 9 | }, 10 | required: ["foo", "bar"], 11 | additionalProperties: false, 12 | } 13 | 14 | const validate = ajv.compile(schema) 15 | 16 | test({foo: "abc", bar: 2}) 17 | test({foo: 2, bar: 4}) 18 | 19 | function test(data) { 20 | const valid = validate(data) 21 | if (valid) console.log("Valid!") 22 | else console.log("Invalid: " + ajv.errorsText(validate.errors)) 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 Evgeny Poberezkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "benchmark": "^2.1.4" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ajv", 3 | "description": "Another JSON Schema Validator", 4 | "main": "bundle/ajv.min.js", 5 | "authors": ["Evgeny Poberezkin"], 6 | "license": "MIT", 7 | "keywords": ["JSON", "schema", "validator"], 8 | "homepage": "https://github.com/ajv-validator/ajv", 9 | "moduleType": ["amd", "globals", "node"], 10 | "ignore": ["node_modules", "bower_components", "spec"] 11 | } 12 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Button.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Column.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 29 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Columns.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Contributors.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | {{ contributor }} 10 | 11 | 12 | 13 | 14 | 24 | 25 | 41 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Feature.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | 74 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Feature/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Features.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /docs/.vuepress/components/FooterColumn.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 60 | -------------------------------------------------------------------------------- /docs/.vuepress/components/FooterColumns.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /docs/.vuepress/components/GitHub.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | Star 12 | 13 | 14 | 15 | 16 | 17 | 26 | 27 | 34 | -------------------------------------------------------------------------------- /docs/.vuepress/components/HomePage.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /docs/.vuepress/components/HomeSection.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /docs/.vuepress/components/NewsHome.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ post.frontmatter.title }} 7 | 8 | 9 | 10 | 11 | 12 | Read more 13 | 14 | 15 | 16 | 17 | 18 | All news 19 | 20 | 21 | 22 | 23 | 35 | 36 | 60 | -------------------------------------------------------------------------------- /docs/.vuepress/components/NewsIndex.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ post.frontmatter.title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Read more 14 | 15 | 16 | 17 | 18 | 19 | 30 | -------------------------------------------------------------------------------- /docs/.vuepress/components/NewsPost.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ $page.frontmatter.title }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 24 | 25 | 32 | -------------------------------------------------------------------------------- /docs/.vuepress/components/NewsPostMeta.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ resolvedDate }} 5 | 6 | 7 | 8 | 9 | 28 | 29 | 36 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Projects.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 45 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Sponsors.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 69 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Subscribe.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | Subscribe to Ajv news 9 | 10 | 11 | 12 | 13 | Submit 14 | 15 | 16 | 17 | 64 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Testimonial.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 57 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Testimonials.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/IstokWeb-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/fonts/IstokWeb-Regular.ttf -------------------------------------------------------------------------------- /docs/.vuepress/public/fonts/Raleway-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/fonts/Raleway-VariableFont_wght.ttf -------------------------------------------------------------------------------- /docs/.vuepress/public/img/ajv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/img/ajv.png -------------------------------------------------------------------------------- /docs/.vuepress/public/img/ajv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/.vuepress/public/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/img/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/.vuepress/public/img/gap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/.vuepress/public/img/microsoft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/img/microsoft.png -------------------------------------------------------------------------------- /docs/.vuepress/public/img/mozilla.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/public/img/openjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/img/openjs.png -------------------------------------------------------------------------------- /docs/.vuepress/public/img/retool.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/.vuepress/public/img/retool.jpg -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | $ajvBlueColor = #409cff 2 | $ajvGreenColor = #23c8d2 // #1fdca3 3 | $ajvRedColor = #f5775b 4 | 5 | $tipColor = $ajvGreenColor 6 | $warningColor = #f1f440 7 | $dangerColor = $ajvRedColor 8 | $attentionBoxColor = #f7f7f3 9 | 10 | $accentColor = #07aab4 // darken($ajvGreenColor, 15%) 11 | $accentCode = #7ec699 12 | $textColor = #292828 13 | $borderColor = #eaecef 14 | $codeBgColor = #282c34 15 | $arrowBgColor = #ccc 16 | $badgeTipColor = $ajvGreenColor 17 | $badgeWarningColor = #e9c400 18 | $badgeErrorColor = $ajvRedColor 19 | 20 | $MQMobileNarrow = 480px 21 | $MQMobileSmall = 414px 22 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-present, Yuxi (Evan) You 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/components/DropdownTransition.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/components/Page.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 | 32 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 27 | 28 | 65 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/components/SidebarButton.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | 18 | 19 | 20 | 21 | 22 | 41 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/global-components/Badge.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 45 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/global-components/CodeBlock.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 30 | 31 | 42 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/layouts/404.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 5 | 6 | {{ getMsg() }} 7 | 8 | 9 | Take me home. 10 | 11 | 12 | 13 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/noopModule.js: -------------------------------------------------------------------------------- 1 | export default {} 2 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/arrow.styl: -------------------------------------------------------------------------------- 1 | @require './config' 2 | 3 | .arrow 4 | display inline-block 5 | width 0 6 | height 0 7 | &.up 8 | border-left 4px solid transparent 9 | border-right 4px solid transparent 10 | border-bottom 6px solid $arrowBgColor 11 | &.down 12 | border-left 4px solid transparent 13 | border-right 4px solid transparent 14 | border-top 6px solid $arrowBgColor 15 | &.right 16 | border-top 4px solid transparent 17 | border-bottom 4px solid transparent 18 | border-left 6px solid $arrowBgColor 19 | &.left 20 | border-top 4px solid transparent 21 | border-bottom 4px solid transparent 22 | border-right 6px solid $arrowBgColor 23 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/config.styl: -------------------------------------------------------------------------------- 1 | $contentClass = '.theme-default-content' 2 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/custom-blocks.styl: -------------------------------------------------------------------------------- 1 | .custom-block 2 | .custom-block-title 3 | font-weight 600 4 | margin-bottom -0.4rem 5 | &.tip, &.warning, &.danger 6 | padding .1rem 1.5rem 7 | border-left-width .5rem 8 | border-left-style solid 9 | margin 1rem 0 10 | &.tip 11 | background-color #f3f5f7 12 | border-color #42b983 13 | &.warning 14 | background-color rgba(255,229,100,.3) 15 | border-color darken(#ffe564, 35%) 16 | color darken(#ffe564, 70%) 17 | .custom-block-title 18 | color darken(#ffe564, 50%) 19 | a 20 | color $textColor 21 | &.danger 22 | background-color #ffe6e6 23 | border-color darken(red, 20%) 24 | color darken(red, 70%) 25 | .custom-block-title 26 | color darken(red, 40%) 27 | a 28 | color $textColor 29 | &.details 30 | display block 31 | position relative 32 | border-radius 2px 33 | margin 1.6em 0 34 | padding 1.6em 35 | background-color #eee 36 | h4 37 | margin-top 0 38 | figure, p 39 | &:last-child 40 | margin-bottom 0 41 | padding-bottom 0 42 | summary 43 | outline none 44 | cursor pointer 45 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/mobile.styl: -------------------------------------------------------------------------------- 1 | @require './config' 2 | 3 | $mobileSidebarWidth = $sidebarWidth * 0.82 4 | 5 | // narrow desktop / iPad 6 | @media (max-width: $MQNarrow) 7 | .sidebar 8 | font-size 15px 9 | width $mobileSidebarWidth 10 | .page 11 | padding-left $mobileSidebarWidth 12 | 13 | // wide mobile 14 | @media (max-width: $MQMobile) 15 | .sidebar 16 | top 0 17 | padding-top $navbarHeight 18 | transform translateX(-100%) 19 | transition transform .2s ease 20 | .page 21 | padding-left 0 22 | .theme-container 23 | &.sidebar-open 24 | .sidebar 25 | transform translateX(0) 26 | &.no-navbar 27 | .sidebar 28 | padding-top: 0 29 | 30 | // narrow mobile 31 | @media (max-width: $MQMobileNarrow) 32 | h1 33 | font-size 1.9rem 34 | {$contentClass} 35 | div[class*="language-"] 36 | margin 0.85rem -1.5rem 37 | border-radius 0 38 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/toc.styl: -------------------------------------------------------------------------------- 1 | .table-of-contents 2 | .badge 3 | vertical-align middle 4 | -------------------------------------------------------------------------------- /docs/.vuepress/theme/styles/wrapper.styl: -------------------------------------------------------------------------------- 1 | $wrapper 2 | max-width $contentWidth 3 | margin 0 auto 4 | padding 2rem 2.5rem 5 | @media (max-width: $MQNarrow) 6 | padding 2rem 7 | @media (max-width: $MQMobileNarrow) 8 | padding 1.5rem 9 | 10 | -------------------------------------------------------------------------------- /docs/news/2020-08-14-mozilla-grant-openjs-foundation.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: Mozilla MOSS grant and OpenJS Foundation 4 | date: 2020-08-14 5 | --- 6 | 7 | [](https://www.mozilla.org/en-US/moss/)[](https://openjsf.org/blog/2020/08/14/ajv-joins-openjs-foundation-as-an-incubation-project/) 8 | 9 | Ajv has been awarded a grant from Mozilla’s [Open Source Support (MOSS) program](https://www.mozilla.org/en-US/moss/) in the “Foundational Technology” track! It will sponsor the development of Ajv support of [JSON Schema version 2019-09](https://tools.ietf.org/html/draft-handrews-json-schema-02) and of [JSON Type Definition (RFC8927)](https://datatracker.ietf.org/doc/rfc8927/). 10 | 11 | Ajv also joined [OpenJS Foundation](https://openjsf.org/) – having this support will help ensure the longevity and stability of Ajv for all its users. 12 | 13 | 14 | 15 | This [blog post](https://www.poberezkin.com/posts/2020-08-14-ajv-json-validator-mozilla-open-source-grant-openjs-foundation.html) has more details. 16 | 17 | I am looking for the long term maintainers of Ajv – working with [ReadySet](https://www.thereadyset.co/), also sponsored by Mozilla, to establish clear guidelines for the role of a "maintainer" and the contribution standards, and to encourage a wider, more inclusive, contribution from the community. 18 | -------------------------------------------------------------------------------- /docs/news/2021-03-07-ajv-supports-json-type-definition.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: Ajv supports JSON Type Definition 4 | date: 2021-03-07 5 | --- 6 | 7 | JSON Type Definition (JTD) is a new specification for defining JSON structures that is very simple to use, comparing with JSON Schema, less error prone, and it is published as [RFC8927](https://datatracker.ietf.org/doc/rfc8927/). 8 | 9 | See Choosing schema language for a detailed comparison between JSON Schema and JSON Type definition and informal specification. 10 | 11 | 12 | 13 | In addition to validation, Ajv also supports: 14 | - generation of [serializers](/api.html#jtd-serialize) and [parsers](/api.html#jtd-parse) from JTD schemas/ This is more efficient than native JSON serialization/parsing - you can combine JSON string parsing and validation in one function call. 15 | - utility type [JTDSchemaType](/guide/typescript.html#utility-types-for-schemas) to convert your data type to the type of JTD schema and [JTDDataType](/guide/typescript.html#utility-type-for-jtd-data-type) to convert the type of schema to the type of data. 16 | -------------------------------------------------------------------------------- /docs/news/2021-03-27-ajv-version-8-released.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: Ajv version 8 is released! 4 | date: 2021-03-27 5 | --- 6 | 7 | Ajv version 8 has these new features: 8 | 9 | - support of JSON Schema draft-2020-12: prefixItems keyword and changed semantics of items keyword, dynamic recursive references. 10 | - OpenAPI discriminator keyword. 11 | - improved JSON Type Definition support: 12 | 13 | - errors consistent with JTD specification. 14 | - error objects with additional properties to simplify error handling 15 | - internationalized error messages with [ajv-i18n](/packages/ajv-i18n) 16 | - TypeScript: support type unions in [JSONSchemaType](/guide/typescript.html#type-safe-unions) 17 | 18 | See [release notes](https://github.com/ajv-validator/ajv/releases/tag/v8.0.0) for the details. 19 | 20 | To install the new version: 21 | 22 | ```bash 23 | npm install ajv 24 | ``` 25 | 26 | See [Getting started](/guide/getting-started.md) for code examples. 27 | -------------------------------------------------------------------------------- /docs/news/2021-04-24-ajv-online-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: "Ajv online event - May 20, 10am PT / 6pm UK" 4 | date: 2021-04-24 5 | more: false 6 | --- 7 | 8 | We will talk about: 9 | - new features of Ajv version 8. 10 | - the improvements sponsored by Mozilla's MOSS grant. 11 | - how Ajv is used in JavaScript applications. 12 | 13 | Speakers: 14 | - [Evgeny Poberezkin](https://github.com/epoberezkin), the creator of Ajv. 15 | - [Mehan Jayasuriya](https://github.com/mehan), Program Officer at Mozilla Foundation, leading the [MOSS](https://www.mozilla.org/en-US/moss/) and other programs investing in the open source and community ecosystems. 16 | - [Matteo Collina](https://github.com/mcollina), Technical Director at NearForm and Node.js Technical Steering Committee member, creator of Fastify web framework. 17 | - [Kin Lane](https://github.com/kinlane), Chief Evangelist at Postman. Studying the tech, business & politics of APIs since 2010. Presidential Innovation Fellow during the Obama administration. 18 | - [Ulysse Carion](https://github.com/ucarion), the creator of JSON Type Definition specification. 19 | 20 | [Gajus Kuizinas](https://github.com/gajus) will host the event. 21 | 22 | Please [register here](https://us02web.zoom.us/webinar/register/4216192074976/WN_erJ_t4ICTHOnGC1SOybNnw). 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/news/2021-05-24-ajv-online-event-video.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: Ajv online event video uploaded 4 | date: 2021-05-24 5 | more: false 6 | --- 7 | 8 | Huge thanks to everybody who joined, and to the speakers! The video of the event is [available on YouTube](https://www.youtube.com/watch?v=KxSKqXEBB7A). 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/news/2021-07-22-ajv-microsoft-foss-fund-award.md: -------------------------------------------------------------------------------- 1 | --- 2 | news: true 3 | title: Microsoft FOSS award 4 | date: 2021-07-22 5 | more: false 6 | --- 7 | 8 | Ajv was awarded a sponsorship from [Microsoft FOSS fund](https://github.com/microsoft/foss-fund/blob/main/README.md#2021) - huge thanks to Microsoft and the engineers who voted to support Ajv development. 9 | 10 | This award will contribute to a long term maintenance of Ajv. 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/news/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | newsIndex: true 3 | editLink: false 4 | --- 5 | 6 | # Ajv News 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/projects/0x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/0x.png -------------------------------------------------------------------------------- /docs/projects/autorest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/autorest.png -------------------------------------------------------------------------------- /docs/projects/aws-amplify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/aws-amplify.png -------------------------------------------------------------------------------- /docs/projects/dependency-cruiser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/dependency-cruiser.png -------------------------------------------------------------------------------- /docs/projects/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/eslint.png -------------------------------------------------------------------------------- /docs/projects/fast.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/projects/fastify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/fastify.png -------------------------------------------------------------------------------- /docs/projects/flipper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/flipper.png -------------------------------------------------------------------------------- /docs/projects/middy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/middy.png -------------------------------------------------------------------------------- /docs/projects/netlify-cms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/netlify-cms.png -------------------------------------------------------------------------------- /docs/projects/nodered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/nodered.png -------------------------------------------------------------------------------- /docs/projects/rjsf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/rjsf.png -------------------------------------------------------------------------------- /docs/projects/stoplight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/stoplight.png -------------------------------------------------------------------------------- /docs/projects/taskcluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/taskcluster.png -------------------------------------------------------------------------------- /docs/projects/teambit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/teambit.png -------------------------------------------------------------------------------- /docs/projects/tsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/tsed.png -------------------------------------------------------------------------------- /docs/projects/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/projects/vue-form-generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/vue-form-generator.png -------------------------------------------------------------------------------- /docs/projects/webhint.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/webhint.jpg -------------------------------------------------------------------------------- /docs/projects/zigbee2mqtt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajv-validator/ajv/82735a15826a30cc51e97a1bbfb59b3d388e4b98/docs/projects/zigbee2mqtt.png -------------------------------------------------------------------------------- /lib/compile/jtd/types.ts: -------------------------------------------------------------------------------- 1 | import type {SchemaObject} from "../../types" 2 | 3 | export type SchemaObjectMap = {[Ref in string]?: SchemaObject} 4 | 5 | export const jtdForms = [ 6 | "elements", 7 | "values", 8 | "discriminator", 9 | "properties", 10 | "optionalProperties", 11 | "enum", 12 | "type", 13 | "ref", 14 | ] as const 15 | 16 | export type JTDForm = (typeof jtdForms)[number] 17 | -------------------------------------------------------------------------------- /lib/compile/names.ts: -------------------------------------------------------------------------------- 1 | import {Name} from "./codegen" 2 | 3 | const names = { 4 | // validation function arguments 5 | data: new Name("data"), // data passed to validation function 6 | // args passed from referencing schema 7 | valCxt: new Name("valCxt"), // validation/data context - should not be used directly, it is destructured to the names below 8 | instancePath: new Name("instancePath"), 9 | parentData: new Name("parentData"), 10 | parentDataProperty: new Name("parentDataProperty"), 11 | rootData: new Name("rootData"), // root data - same as the data passed to the first/top validation function 12 | dynamicAnchors: new Name("dynamicAnchors"), // used to support recursiveRef and dynamicRef 13 | // function scoped variables 14 | vErrors: new Name("vErrors"), // null or array of validation errors 15 | errors: new Name("errors"), // counter of validation errors 16 | this: new Name("this"), 17 | // "globals" 18 | self: new Name("self"), 19 | scope: new Name("scope"), 20 | // JTD serialize/parse name for JSON string and position 21 | json: new Name("json"), 22 | jsonPos: new Name("jsonPos"), 23 | jsonLen: new Name("jsonLen"), 24 | jsonPart: new Name("jsonPart"), 25 | } 26 | 27 | export default names 28 | -------------------------------------------------------------------------------- /lib/compile/ref_error.ts: -------------------------------------------------------------------------------- 1 | import {resolveUrl, normalizeId, getFullPath} from "./resolve" 2 | import type {UriResolver} from "../types" 3 | 4 | export default class MissingRefError extends Error { 5 | readonly missingRef: string 6 | readonly missingSchema: string 7 | 8 | constructor(resolver: UriResolver, baseId: string, ref: string, msg?: string) { 9 | super(msg || `can't resolve reference ${ref} from id ${baseId}`) 10 | this.missingRef = resolveUrl(resolver, baseId, ref) 11 | this.missingSchema = normalizeId(getFullPath(resolver, this.missingRef)) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/compile/rules.ts: -------------------------------------------------------------------------------- 1 | import type {AddedKeywordDefinition} from "../types" 2 | 3 | const _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"] as const 4 | 5 | export type JSONType = (typeof _jsonTypes)[number] 6 | 7 | const jsonTypes: Set = new Set(_jsonTypes) 8 | 9 | export function isJSONType(x: unknown): x is JSONType { 10 | return typeof x == "string" && jsonTypes.has(x) 11 | } 12 | 13 | type ValidationTypes = { 14 | [K in JSONType]: boolean | RuleGroup | undefined 15 | } 16 | 17 | export interface ValidationRules { 18 | rules: RuleGroup[] 19 | post: RuleGroup 20 | all: {[Key in string]?: boolean | Rule} // rules that have to be validated 21 | keywords: {[Key in string]?: boolean} // all known keywords (superset of "all") 22 | types: ValidationTypes 23 | } 24 | 25 | export interface RuleGroup { 26 | type?: JSONType 27 | rules: Rule[] 28 | } 29 | 30 | // This interface wraps KeywordDefinition because definition can have multiple keywords 31 | export interface Rule { 32 | keyword: string 33 | definition: AddedKeywordDefinition 34 | } 35 | 36 | export function getRules(): ValidationRules { 37 | const groups: Record<"number" | "string" | "array" | "object", RuleGroup> = { 38 | number: {type: "number", rules: []}, 39 | string: {type: "string", rules: []}, 40 | array: {type: "array", rules: []}, 41 | object: {type: "object", rules: []}, 42 | } 43 | return { 44 | types: {...groups, integer: true, boolean: true, null: true}, 45 | rules: [{rules: []}, groups.number, groups.string, groups.array, groups.object], 46 | post: {rules: []}, 47 | all: {}, 48 | keywords: {}, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/compile/validate/applicability.ts: -------------------------------------------------------------------------------- 1 | import type {AnySchemaObject} from "../../types" 2 | import type {SchemaObjCxt} from ".." 3 | import type {JSONType, RuleGroup, Rule} from "../rules" 4 | 5 | export function schemaHasRulesForType( 6 | {schema, self}: SchemaObjCxt, 7 | type: JSONType 8 | ): boolean | undefined { 9 | const group = self.RULES.types[type] 10 | return group && group !== true && shouldUseGroup(schema, group) 11 | } 12 | 13 | export function shouldUseGroup(schema: AnySchemaObject, group: RuleGroup): boolean { 14 | return group.rules.some((rule) => shouldUseRule(schema, rule)) 15 | } 16 | 17 | export function shouldUseRule(schema: AnySchemaObject, rule: Rule): boolean | undefined { 18 | return ( 19 | schema[rule.keyword] !== undefined || 20 | rule.definition.implements?.some((kwd) => schema[kwd] !== undefined) 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /lib/compile/validate/boolSchema.ts: -------------------------------------------------------------------------------- 1 | import type {KeywordErrorDefinition, KeywordErrorCxt} from "../../types" 2 | import type {SchemaCxt} from ".." 3 | import {reportError} from "../errors" 4 | import {_, Name} from "../codegen" 5 | import N from "../names" 6 | 7 | const boolError: KeywordErrorDefinition = { 8 | message: "boolean schema is false", 9 | } 10 | 11 | export function topBoolOrEmptySchema(it: SchemaCxt): void { 12 | const {gen, schema, validateName} = it 13 | if (schema === false) { 14 | falseSchemaError(it, false) 15 | } else if (typeof schema == "object" && schema.$async === true) { 16 | gen.return(N.data) 17 | } else { 18 | gen.assign(_`${validateName}.errors`, null) 19 | gen.return(true) 20 | } 21 | } 22 | 23 | export function boolOrEmptySchema(it: SchemaCxt, valid: Name): void { 24 | const {gen, schema} = it 25 | if (schema === false) { 26 | gen.var(valid, false) // TODO var 27 | falseSchemaError(it) 28 | } else { 29 | gen.var(valid, true) // TODO var 30 | } 31 | } 32 | 33 | function falseSchemaError(it: SchemaCxt, overrideAllErrors?: boolean): void { 34 | const {gen, data} = it 35 | // TODO maybe some other interface should be used for non-keyword validation errors... 36 | const cxt: KeywordErrorCxt = { 37 | gen, 38 | keyword: "false schema", 39 | data, 40 | schema: false, 41 | schemaCode: false, 42 | schemaValue: false, 43 | params: {}, 44 | it, 45 | } 46 | reportError(cxt, boolError, undefined, overrideAllErrors) 47 | } 48 | -------------------------------------------------------------------------------- /lib/compile/validate/defaults.ts: -------------------------------------------------------------------------------- 1 | import type {SchemaObjCxt} from ".." 2 | import {_, getProperty, stringify} from "../codegen" 3 | import {checkStrictMode} from "../util" 4 | 5 | export function assignDefaults(it: SchemaObjCxt, ty?: string): void { 6 | const {properties, items} = it.schema 7 | if (ty === "object" && properties) { 8 | for (const key in properties) { 9 | assignDefault(it, key, properties[key].default) 10 | } 11 | } else if (ty === "array" && Array.isArray(items)) { 12 | items.forEach((sch, i: number) => assignDefault(it, i, sch.default)) 13 | } 14 | } 15 | 16 | function assignDefault(it: SchemaObjCxt, prop: string | number, defaultValue: unknown): void { 17 | const {gen, compositeRule, data, opts} = it 18 | if (defaultValue === undefined) return 19 | const childData = _`${data}${getProperty(prop)}` 20 | if (compositeRule) { 21 | checkStrictMode(it, `default is ignored for: ${childData}`) 22 | return 23 | } 24 | 25 | let condition = _`${childData} === undefined` 26 | if (opts.useDefaults === "empty") { 27 | condition = _`${condition} || ${childData} === null || ${childData} === ""` 28 | } 29 | // `${childData} === undefined` + 30 | // (opts.useDefaults === "empty" ? ` || ${childData} === null || ${childData} === ""` : "") 31 | gen.if(condition, _`${childData} = ${stringify(defaultValue)}`) 32 | } 33 | -------------------------------------------------------------------------------- /lib/refs/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", 3 | "description": "Meta-schema for $data reference (JSON AnySchema extension proposal)", 4 | "type": "object", 5 | "required": ["$data"], 6 | "properties": { 7 | "$data": { 8 | "type": "string", 9 | "anyOf": [{"format": "relative-json-pointer"}, {"format": "json-pointer"}] 10 | } 11 | }, 12 | "additionalProperties": false 13 | } 14 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2019-09/index.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from "../../core" 2 | import type {AnySchemaObject} from "../../types" 3 | import * as metaSchema from "./schema.json" 4 | import * as applicator from "./meta/applicator.json" 5 | import * as content from "./meta/content.json" 6 | import * as core from "./meta/core.json" 7 | import * as format from "./meta/format.json" 8 | import * as metadata from "./meta/meta-data.json" 9 | import * as validation from "./meta/validation.json" 10 | 11 | const META_SUPPORT_DATA = ["/properties"] 12 | 13 | export default function addMetaSchema2019(this: Ajv, $data?: boolean): Ajv { 14 | ;[ 15 | metaSchema, 16 | applicator, 17 | content, 18 | core, 19 | with$data(this, format), 20 | metadata, 21 | with$data(this, validation), 22 | ].forEach((sch) => this.addMetaSchema(sch, undefined, false)) 23 | return this 24 | 25 | function with$data(ajv: Ajv, sch: AnySchemaObject): AnySchemaObject { 26 | return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2019-09/meta/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2019-09/schema", 3 | "$id": "https://json-schema.org/draft/2019-09/meta/content", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2019-09/vocab/content": true 6 | }, 7 | "$recursiveAnchor": true, 8 | 9 | "title": "Content vocabulary meta-schema", 10 | 11 | "type": ["object", "boolean"], 12 | "properties": { 13 | "contentMediaType": {"type": "string"}, 14 | "contentEncoding": {"type": "string"}, 15 | "contentSchema": {"$recursiveRef": "#"} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2019-09/meta/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2019-09/schema", 3 | "$id": "https://json-schema.org/draft/2019-09/meta/core", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2019-09/vocab/core": true 6 | }, 7 | "$recursiveAnchor": true, 8 | 9 | "title": "Core vocabulary meta-schema", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "$id": { 13 | "type": "string", 14 | "format": "uri-reference", 15 | "$comment": "Non-empty fragments not allowed.", 16 | "pattern": "^[^#]*#?$" 17 | }, 18 | "$schema": { 19 | "type": "string", 20 | "format": "uri" 21 | }, 22 | "$anchor": { 23 | "type": "string", 24 | "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" 25 | }, 26 | "$ref": { 27 | "type": "string", 28 | "format": "uri-reference" 29 | }, 30 | "$recursiveRef": { 31 | "type": "string", 32 | "format": "uri-reference" 33 | }, 34 | "$recursiveAnchor": { 35 | "type": "boolean", 36 | "default": false 37 | }, 38 | "$vocabulary": { 39 | "type": "object", 40 | "propertyNames": { 41 | "type": "string", 42 | "format": "uri" 43 | }, 44 | "additionalProperties": { 45 | "type": "boolean" 46 | } 47 | }, 48 | "$comment": { 49 | "type": "string" 50 | }, 51 | "$defs": { 52 | "type": "object", 53 | "additionalProperties": {"$recursiveRef": "#"}, 54 | "default": {} 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2019-09/meta/format.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2019-09/schema", 3 | "$id": "https://json-schema.org/draft/2019-09/meta/format", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2019-09/vocab/format": true 6 | }, 7 | "$recursiveAnchor": true, 8 | 9 | "title": "Format vocabulary meta-schema", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "format": {"type": "string"} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2019-09/meta/meta-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2019-09/schema", 3 | "$id": "https://json-schema.org/draft/2019-09/meta/meta-data", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2019-09/vocab/meta-data": true 6 | }, 7 | "$recursiveAnchor": true, 8 | 9 | "title": "Meta-data vocabulary meta-schema", 10 | 11 | "type": ["object", "boolean"], 12 | "properties": { 13 | "title": { 14 | "type": "string" 15 | }, 16 | "description": { 17 | "type": "string" 18 | }, 19 | "default": true, 20 | "deprecated": { 21 | "type": "boolean", 22 | "default": false 23 | }, 24 | "readOnly": { 25 | "type": "boolean", 26 | "default": false 27 | }, 28 | "writeOnly": { 29 | "type": "boolean", 30 | "default": false 31 | }, 32 | "examples": { 33 | "type": "array", 34 | "items": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/index.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from "../../core" 2 | import type {AnySchemaObject} from "../../types" 3 | import * as metaSchema from "./schema.json" 4 | import * as applicator from "./meta/applicator.json" 5 | import * as unevaluated from "./meta/unevaluated.json" 6 | import * as content from "./meta/content.json" 7 | import * as core from "./meta/core.json" 8 | import * as format from "./meta/format-annotation.json" 9 | import * as metadata from "./meta/meta-data.json" 10 | import * as validation from "./meta/validation.json" 11 | 12 | const META_SUPPORT_DATA = ["/properties"] 13 | 14 | export default function addMetaSchema2020(this: Ajv, $data?: boolean): Ajv { 15 | ;[ 16 | metaSchema, 17 | applicator, 18 | unevaluated, 19 | content, 20 | core, 21 | with$data(this, format), 22 | metadata, 23 | with$data(this, validation), 24 | ].forEach((sch) => this.addMetaSchema(sch, undefined, false)) 25 | return this 26 | 27 | function with$data(ajv: Ajv, sch: AnySchemaObject): AnySchemaObject { 28 | return $data ? ajv.$dataMetaSchema(sch, META_SUPPORT_DATA) : sch 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/applicator.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/applicator", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/applicator": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Applicator vocabulary meta-schema", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "prefixItems": {"$ref": "#/$defs/schemaArray"}, 13 | "items": {"$dynamicRef": "#meta"}, 14 | "contains": {"$dynamicRef": "#meta"}, 15 | "additionalProperties": {"$dynamicRef": "#meta"}, 16 | "properties": { 17 | "type": "object", 18 | "additionalProperties": {"$dynamicRef": "#meta"}, 19 | "default": {} 20 | }, 21 | "patternProperties": { 22 | "type": "object", 23 | "additionalProperties": {"$dynamicRef": "#meta"}, 24 | "propertyNames": {"format": "regex"}, 25 | "default": {} 26 | }, 27 | "dependentSchemas": { 28 | "type": "object", 29 | "additionalProperties": {"$dynamicRef": "#meta"}, 30 | "default": {} 31 | }, 32 | "propertyNames": {"$dynamicRef": "#meta"}, 33 | "if": {"$dynamicRef": "#meta"}, 34 | "then": {"$dynamicRef": "#meta"}, 35 | "else": {"$dynamicRef": "#meta"}, 36 | "allOf": {"$ref": "#/$defs/schemaArray"}, 37 | "anyOf": {"$ref": "#/$defs/schemaArray"}, 38 | "oneOf": {"$ref": "#/$defs/schemaArray"}, 39 | "not": {"$dynamicRef": "#meta"} 40 | }, 41 | "$defs": { 42 | "schemaArray": { 43 | "type": "array", 44 | "minItems": 1, 45 | "items": {"$dynamicRef": "#meta"} 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/content", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/content": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Content vocabulary meta-schema", 10 | 11 | "type": ["object", "boolean"], 12 | "properties": { 13 | "contentEncoding": {"type": "string"}, 14 | "contentMediaType": {"type": "string"}, 15 | "contentSchema": {"$dynamicRef": "#meta"} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/core", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/core": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Core vocabulary meta-schema", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "$id": { 13 | "$ref": "#/$defs/uriReferenceString", 14 | "$comment": "Non-empty fragments not allowed.", 15 | "pattern": "^[^#]*#?$" 16 | }, 17 | "$schema": {"$ref": "#/$defs/uriString"}, 18 | "$ref": {"$ref": "#/$defs/uriReferenceString"}, 19 | "$anchor": {"$ref": "#/$defs/anchorString"}, 20 | "$dynamicRef": {"$ref": "#/$defs/uriReferenceString"}, 21 | "$dynamicAnchor": {"$ref": "#/$defs/anchorString"}, 22 | "$vocabulary": { 23 | "type": "object", 24 | "propertyNames": {"$ref": "#/$defs/uriString"}, 25 | "additionalProperties": { 26 | "type": "boolean" 27 | } 28 | }, 29 | "$comment": { 30 | "type": "string" 31 | }, 32 | "$defs": { 33 | "type": "object", 34 | "additionalProperties": {"$dynamicRef": "#meta"} 35 | } 36 | }, 37 | "$defs": { 38 | "anchorString": { 39 | "type": "string", 40 | "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" 41 | }, 42 | "uriString": { 43 | "type": "string", 44 | "format": "uri" 45 | }, 46 | "uriReferenceString": { 47 | "type": "string", 48 | "format": "uri-reference" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/format-annotation.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/format-annotation": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Format vocabulary meta-schema for annotation results", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "format": {"type": "string"} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/meta-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/meta-data": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Meta-data vocabulary meta-schema", 10 | 11 | "type": ["object", "boolean"], 12 | "properties": { 13 | "title": { 14 | "type": "string" 15 | }, 16 | "description": { 17 | "type": "string" 18 | }, 19 | "default": true, 20 | "deprecated": { 21 | "type": "boolean", 22 | "default": false 23 | }, 24 | "readOnly": { 25 | "type": "boolean", 26 | "default": false 27 | }, 28 | "writeOnly": { 29 | "type": "boolean", 30 | "default": false 31 | }, 32 | "examples": { 33 | "type": "array", 34 | "items": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/refs/json-schema-2020-12/meta/unevaluated.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json-schema.org/draft/2020-12/schema", 3 | "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", 4 | "$vocabulary": { 5 | "https://json-schema.org/draft/2020-12/vocab/unevaluated": true 6 | }, 7 | "$dynamicAnchor": "meta", 8 | 9 | "title": "Unevaluated applicator vocabulary meta-schema", 10 | "type": ["object", "boolean"], 11 | "properties": { 12 | "unevaluatedItems": {"$dynamicRef": "#meta"}, 13 | "unevaluatedProperties": {"$dynamicRef": "#meta"} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/runtime/equal.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/ajv-validator/ajv/issues/889 2 | import * as equal from "fast-deep-equal" 3 | 4 | type Equal = typeof equal & {code: string} 5 | ;(equal as Equal).code = 'require("ajv/dist/runtime/equal").default' 6 | 7 | export default equal as Equal 8 | -------------------------------------------------------------------------------- /lib/runtime/quote.ts: -------------------------------------------------------------------------------- 1 | const rxEscapable = 2 | // eslint-disable-next-line no-control-regex, no-misleading-character-class 3 | /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g 4 | 5 | const escaped: {[K in string]?: string} = { 6 | "\b": "\\b", 7 | "\t": "\\t", 8 | "\n": "\\n", 9 | "\f": "\\f", 10 | "\r": "\\r", 11 | '"': '\\"', 12 | "\\": "\\\\", 13 | } 14 | 15 | export default function quote(s: string): string { 16 | rxEscapable.lastIndex = 0 17 | return ( 18 | '"' + 19 | (rxEscapable.test(s) 20 | ? s.replace(rxEscapable, (a) => { 21 | const c = escaped[a] 22 | return typeof c === "string" 23 | ? c 24 | : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) 25 | }) 26 | : s) + 27 | '"' 28 | ) 29 | } 30 | 31 | quote.code = 'require("ajv/dist/runtime/quote").default' 32 | -------------------------------------------------------------------------------- /lib/runtime/re2.ts: -------------------------------------------------------------------------------- 1 | import * as re2 from "re2" 2 | 3 | type Re2 = typeof re2 & {code: string} 4 | ;(re2 as Re2).code = 'require("ajv/dist/runtime/re2").default' 5 | 6 | export default re2 as Re2 7 | -------------------------------------------------------------------------------- /lib/runtime/timestamp.ts: -------------------------------------------------------------------------------- 1 | const DT_SEPARATOR = /t|\s/i 2 | const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ 3 | const TIME = /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:z|([+-]\d\d)(?::?(\d\d))?)$/i 4 | const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 5 | 6 | export default function validTimestamp(str: string, allowDate: boolean): boolean { 7 | // http://tools.ietf.org/html/rfc3339#section-5.6 8 | const dt: string[] = str.split(DT_SEPARATOR) 9 | return ( 10 | (dt.length === 2 && validDate(dt[0]) && validTime(dt[1])) || 11 | (allowDate && dt.length === 1 && validDate(dt[0])) 12 | ) 13 | } 14 | 15 | function validDate(str: string): boolean { 16 | const matches: string[] | null = DATE.exec(str) 17 | if (!matches) return false 18 | const y: number = +matches[1] 19 | const m: number = +matches[2] 20 | const d: number = +matches[3] 21 | return ( 22 | m >= 1 && 23 | m <= 12 && 24 | d >= 1 && 25 | (d <= DAYS[m] || 26 | // leap year: https://tools.ietf.org/html/rfc3339#appendix-C 27 | (m === 2 && d === 29 && (y % 100 === 0 ? y % 400 === 0 : y % 4 === 0))) 28 | ) 29 | } 30 | 31 | function validTime(str: string): boolean { 32 | const matches: string[] | null = TIME.exec(str) 33 | if (!matches) return false 34 | const hr: number = +matches[1] 35 | const min: number = +matches[2] 36 | const sec: number = +matches[3] 37 | const tzH: number = +(matches[4] || 0) 38 | const tzM: number = +(matches[5] || 0) 39 | return ( 40 | (hr <= 23 && min <= 59 && sec <= 59) || 41 | // leap second 42 | (hr - tzH === 23 && min - tzM === 59 && sec === 60) 43 | ) 44 | } 45 | 46 | validTimestamp.code = 'require("ajv/dist/runtime/timestamp").default' 47 | -------------------------------------------------------------------------------- /lib/runtime/ucs2length.ts: -------------------------------------------------------------------------------- 1 | // https://mathiasbynens.be/notes/javascript-encoding 2 | // https://github.com/bestiejs/punycode.js - punycode.ucs2.decode 3 | export default function ucs2length(str: string): number { 4 | const len = str.length 5 | let length = 0 6 | let pos = 0 7 | let value: number 8 | while (pos < len) { 9 | length++ 10 | value = str.charCodeAt(pos++) 11 | if (value >= 0xd800 && value <= 0xdbff && pos < len) { 12 | // high surrogate, and there is a next character 13 | value = str.charCodeAt(pos) 14 | if ((value & 0xfc00) === 0xdc00) pos++ // low surrogate 15 | } 16 | } 17 | return length 18 | } 19 | 20 | ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default' 21 | -------------------------------------------------------------------------------- /lib/runtime/uri.ts: -------------------------------------------------------------------------------- 1 | import * as uri from "fast-uri" 2 | 3 | type URI = typeof uri & {code: string} 4 | ;(uri as URI).code = 'require("ajv/dist/runtime/uri").default' 5 | 6 | export default uri as URI 7 | -------------------------------------------------------------------------------- /lib/runtime/validation_error.ts: -------------------------------------------------------------------------------- 1 | import type {ErrorObject} from "../types" 2 | 3 | export default class ValidationError extends Error { 4 | readonly errors: Partial[] 5 | readonly ajv: true 6 | readonly validation: true 7 | 8 | constructor(errors: Partial[]) { 9 | super("validation failed") 10 | this.errors = errors 11 | this.ajv = this.validation = true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/standalone/instance.ts: -------------------------------------------------------------------------------- 1 | import Ajv, {AnySchema, AnyValidateFunction, ErrorObject} from "../core" 2 | import standaloneCode from "." 3 | import * as requireFromString from "require-from-string" 4 | 5 | export default class AjvPack { 6 | errors?: ErrorObject[] | null // errors from the last validation 7 | constructor(readonly ajv: Ajv) {} 8 | 9 | validate(schemaKeyRef: AnySchema | string, data: unknown): boolean | Promise { 10 | return Ajv.prototype.validate.call(this, schemaKeyRef, data) 11 | } 12 | 13 | compile(schema: AnySchema, meta?: boolean): AnyValidateFunction { 14 | return this.getStandalone(this.ajv.compile(schema, meta)) 15 | } 16 | 17 | getSchema(keyRef: string): AnyValidateFunction | undefined { 18 | const v = this.ajv.getSchema(keyRef) 19 | if (!v) return undefined 20 | return this.getStandalone(v) 21 | } 22 | 23 | private getStandalone(v: AnyValidateFunction): AnyValidateFunction { 24 | return requireFromString(standaloneCode(this.ajv, v)) as AnyValidateFunction 25 | } 26 | 27 | addSchema(...args: Parameters): AjvPack { 28 | this.ajv.addSchema.call(this.ajv, ...args) 29 | return this 30 | } 31 | 32 | addKeyword(...args: Parameters): AjvPack { 33 | this.ajv.addKeyword.call(this.ajv, ...args) 34 | return this 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/allOf.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, AnySchema} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {alwaysValidSchema} from "../../compile/util" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: "allOf", 7 | schemaType: "array", 8 | code(cxt: KeywordCxt) { 9 | const {gen, schema, it} = cxt 10 | /* istanbul ignore if */ 11 | if (!Array.isArray(schema)) throw new Error("ajv implementation error") 12 | const valid = gen.name("valid") 13 | schema.forEach((sch: AnySchema, i: number) => { 14 | if (alwaysValidSchema(it, sch)) return 15 | const schCxt = cxt.subschema({keyword: "allOf", schemaProp: i}, valid) 16 | cxt.ok(valid) 17 | cxt.mergeEvaluated(schCxt) 18 | }) 19 | }, 20 | } 21 | 22 | export default def 23 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/anyOf.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types" 2 | import {validateUnion} from "../code" 3 | 4 | export type AnyOfError = ErrorNoParams<"anyOf", AnySchema[]> 5 | 6 | const def: CodeKeywordDefinition = { 7 | keyword: "anyOf", 8 | schemaType: "array", 9 | trackErrors: true, 10 | code: validateUnion, 11 | error: {message: "must match a schema in anyOf"}, 12 | } 13 | 14 | export default def 15 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/dependentSchemas.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import {validateSchemaDeps} from "./dependencies" 3 | 4 | const def: CodeKeywordDefinition = { 5 | keyword: "dependentSchemas", 6 | type: "object", 7 | schemaType: "object", 8 | code: (cxt) => validateSchemaDeps(cxt), 9 | } 10 | 11 | export default def 12 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/items2020.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CodeKeywordDefinition, 3 | KeywordErrorDefinition, 4 | ErrorObject, 5 | AnySchema, 6 | } from "../../types" 7 | import type {KeywordCxt} from "../../compile/validate" 8 | import {_, str} from "../../compile/codegen" 9 | import {alwaysValidSchema} from "../../compile/util" 10 | import {validateArray} from "../code" 11 | import {validateAdditionalItems} from "./additionalItems" 12 | 13 | export type ItemsError = ErrorObject<"items", {limit: number}, AnySchema> 14 | 15 | const error: KeywordErrorDefinition = { 16 | message: ({params: {len}}) => str`must NOT have more than ${len} items`, 17 | params: ({params: {len}}) => _`{limit: ${len}}`, 18 | } 19 | 20 | const def: CodeKeywordDefinition = { 21 | keyword: "items", 22 | type: "array", 23 | schemaType: ["object", "boolean"], 24 | before: "uniqueItems", 25 | error, 26 | code(cxt: KeywordCxt) { 27 | const {schema, parentSchema, it} = cxt 28 | const {prefixItems} = parentSchema 29 | it.items = true 30 | if (alwaysValidSchema(it, schema)) return 31 | if (prefixItems) validateAdditionalItems(cxt, prefixItems) 32 | else cxt.ok(validateArray(cxt)) 33 | }, 34 | } 35 | 36 | export default def 37 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/not.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorNoParams, AnySchema} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {alwaysValidSchema} from "../../compile/util" 4 | 5 | export type NotKeywordError = ErrorNoParams<"not", AnySchema> 6 | 7 | const def: CodeKeywordDefinition = { 8 | keyword: "not", 9 | schemaType: ["object", "boolean"], 10 | trackErrors: true, 11 | code(cxt: KeywordCxt) { 12 | const {gen, schema, it} = cxt 13 | if (alwaysValidSchema(it, schema)) { 14 | cxt.fail() 15 | return 16 | } 17 | 18 | const valid = gen.name("valid") 19 | cxt.subschema( 20 | { 21 | keyword: "not", 22 | compositeRule: true, 23 | createErrors: false, 24 | allErrors: false, 25 | }, 26 | valid 27 | ) 28 | 29 | cxt.failResult( 30 | valid, 31 | () => cxt.reset(), 32 | () => cxt.error() 33 | ) 34 | }, 35 | error: {message: "must NOT be valid"}, 36 | } 37 | 38 | export default def 39 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/prefixItems.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import {validateTuple} from "./items" 3 | 4 | const def: CodeKeywordDefinition = { 5 | keyword: "prefixItems", 6 | type: "array", 7 | schemaType: ["array"], 8 | before: "uniqueItems", 9 | code: (cxt) => validateTuple(cxt, "items"), 10 | } 11 | 12 | export default def 13 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/propertyNames.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CodeKeywordDefinition, 3 | ErrorObject, 4 | KeywordErrorDefinition, 5 | AnySchema, 6 | } from "../../types" 7 | import type {KeywordCxt} from "../../compile/validate" 8 | import {_, not} from "../../compile/codegen" 9 | import {alwaysValidSchema} from "../../compile/util" 10 | 11 | export type PropertyNamesError = ErrorObject<"propertyNames", {propertyName: string}, AnySchema> 12 | 13 | const error: KeywordErrorDefinition = { 14 | message: "property name must be valid", 15 | params: ({params}) => _`{propertyName: ${params.propertyName}}`, 16 | } 17 | 18 | const def: CodeKeywordDefinition = { 19 | keyword: "propertyNames", 20 | type: "object", 21 | schemaType: ["object", "boolean"], 22 | error, 23 | code(cxt: KeywordCxt) { 24 | const {gen, schema, data, it} = cxt 25 | if (alwaysValidSchema(it, schema)) return 26 | const valid = gen.name("valid") 27 | 28 | gen.forIn("key", data, (key) => { 29 | cxt.setParams({propertyName: key}) 30 | cxt.subschema( 31 | { 32 | keyword: "propertyNames", 33 | data: key, 34 | dataTypes: ["string"], 35 | propertyName: key, 36 | compositeRule: true, 37 | }, 38 | valid 39 | ) 40 | gen.if(not(valid), () => { 41 | cxt.error(true) 42 | if (!it.allErrors) gen.break() 43 | }) 44 | }) 45 | 46 | cxt.ok(valid) 47 | }, 48 | } 49 | 50 | export default def 51 | -------------------------------------------------------------------------------- /lib/vocabularies/applicator/thenElse.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {checkStrictMode} from "../../compile/util" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: ["then", "else"], 7 | schemaType: ["object", "boolean"], 8 | code({keyword, parentSchema, it}: KeywordCxt) { 9 | if (parentSchema.if === undefined) checkStrictMode(it, `"${keyword}" without "if" is ignored`) 10 | }, 11 | } 12 | 13 | export default def 14 | -------------------------------------------------------------------------------- /lib/vocabularies/core/id.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | 3 | const def: CodeKeywordDefinition = { 4 | keyword: "id", 5 | code() { 6 | throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID') 7 | }, 8 | } 9 | 10 | export default def 11 | -------------------------------------------------------------------------------- /lib/vocabularies/core/index.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../../types" 2 | import idKeyword from "./id" 3 | import refKeyword from "./ref" 4 | 5 | const core: Vocabulary = [ 6 | "$schema", 7 | "$id", 8 | "$defs", 9 | "$vocabulary", 10 | {keyword: "$comment"}, 11 | "definitions", 12 | idKeyword, 13 | refKeyword, 14 | ] 15 | 16 | export default core 17 | -------------------------------------------------------------------------------- /lib/vocabularies/discriminator/types.ts: -------------------------------------------------------------------------------- 1 | import type {ErrorObject} from "../../types" 2 | 3 | export enum DiscrError { 4 | Tag = "tag", 5 | Mapping = "mapping", 6 | } 7 | 8 | export type DiscrErrorObj = ErrorObject< 9 | "discriminator", 10 | {error: E; tag: string; tagValue: unknown}, 11 | string 12 | > 13 | -------------------------------------------------------------------------------- /lib/vocabularies/draft2020.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../types" 2 | import coreVocabulary from "./core" 3 | import validationVocabulary from "./validation" 4 | import getApplicatorVocabulary from "./applicator" 5 | import dynamicVocabulary from "./dynamic" 6 | import nextVocabulary from "./next" 7 | import unevaluatedVocabulary from "./unevaluated" 8 | import formatVocabulary from "./format" 9 | import {metadataVocabulary, contentVocabulary} from "./metadata" 10 | 11 | const draft2020Vocabularies: Vocabulary[] = [ 12 | dynamicVocabulary, 13 | coreVocabulary, 14 | validationVocabulary, 15 | getApplicatorVocabulary(true), 16 | formatVocabulary, 17 | metadataVocabulary, 18 | contentVocabulary, 19 | nextVocabulary, 20 | unevaluatedVocabulary, 21 | ] 22 | 23 | export default draft2020Vocabularies 24 | -------------------------------------------------------------------------------- /lib/vocabularies/draft7.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../types" 2 | import coreVocabulary from "./core" 3 | import validationVocabulary from "./validation" 4 | import getApplicatorVocabulary from "./applicator" 5 | import formatVocabulary from "./format" 6 | import {metadataVocabulary, contentVocabulary} from "./metadata" 7 | 8 | const draft7Vocabularies: Vocabulary[] = [ 9 | coreVocabulary, 10 | validationVocabulary, 11 | getApplicatorVocabulary(), 12 | formatVocabulary, 13 | metadataVocabulary, 14 | contentVocabulary, 15 | ] 16 | 17 | export default draft7Vocabularies 18 | -------------------------------------------------------------------------------- /lib/vocabularies/dynamic/dynamicAnchor.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, getProperty, Code} from "../../compile/codegen" 4 | import N from "../../compile/names" 5 | import {SchemaEnv, compileSchema} from "../../compile" 6 | import {getValidate} from "../core/ref" 7 | 8 | const def: CodeKeywordDefinition = { 9 | keyword: "$dynamicAnchor", 10 | schemaType: "string", 11 | code: (cxt) => dynamicAnchor(cxt, cxt.schema), 12 | } 13 | 14 | export function dynamicAnchor(cxt: KeywordCxt, anchor: string): void { 15 | const {gen, it} = cxt 16 | it.schemaEnv.root.dynamicAnchors[anchor] = true 17 | const v = _`${N.dynamicAnchors}${getProperty(anchor)}` 18 | const validate = it.errSchemaPath === "#" ? it.validateName : _getValidate(cxt) 19 | gen.if(_`!${v}`, () => gen.assign(v, validate)) 20 | } 21 | 22 | function _getValidate(cxt: KeywordCxt): Code { 23 | const {schemaEnv, schema, self} = cxt.it 24 | const {root, baseId, localRefs, meta} = schemaEnv.root 25 | const {schemaId} = self.opts 26 | const sch = new SchemaEnv({schema, schemaId, root, baseId, localRefs, meta}) 27 | compileSchema.call(self, sch) 28 | return getValidate(cxt, sch) 29 | } 30 | 31 | export default def 32 | -------------------------------------------------------------------------------- /lib/vocabularies/dynamic/index.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../../types" 2 | import dynamicAnchor from "./dynamicAnchor" 3 | import dynamicRef from "./dynamicRef" 4 | import recursiveAnchor from "./recursiveAnchor" 5 | import recursiveRef from "./recursiveRef" 6 | 7 | const dynamic: Vocabulary = [dynamicAnchor, dynamicRef, recursiveAnchor, recursiveRef] 8 | 9 | export default dynamic 10 | -------------------------------------------------------------------------------- /lib/vocabularies/dynamic/recursiveAnchor.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import {dynamicAnchor} from "./dynamicAnchor" 3 | import {checkStrictMode} from "../../compile/util" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: "$recursiveAnchor", 7 | schemaType: "boolean", 8 | code(cxt) { 9 | if (cxt.schema) dynamicAnchor(cxt, "") 10 | else checkStrictMode(cxt.it, "$recursiveAnchor: false is ignored") 11 | }, 12 | } 13 | 14 | export default def 15 | -------------------------------------------------------------------------------- /lib/vocabularies/dynamic/recursiveRef.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import {dynamicRef} from "./dynamicRef" 3 | 4 | const def: CodeKeywordDefinition = { 5 | keyword: "$recursiveRef", 6 | schemaType: "string", 7 | code: (cxt) => dynamicRef(cxt, cxt.schema), 8 | } 9 | 10 | export default def 11 | -------------------------------------------------------------------------------- /lib/vocabularies/errors.ts: -------------------------------------------------------------------------------- 1 | import type {TypeError} from "../compile/validate/dataType" 2 | import type {ApplicatorKeywordError} from "./applicator" 3 | import type {ValidationKeywordError} from "./validation" 4 | import type {FormatError} from "./format/format" 5 | import type {UnevaluatedPropertiesError} from "./unevaluated/unevaluatedProperties" 6 | import type {UnevaluatedItemsError} from "./unevaluated/unevaluatedItems" 7 | import type {DependentRequiredError} from "./validation/dependentRequired" 8 | import type {DiscriminatorError} from "./discriminator" 9 | 10 | export type DefinedError = 11 | | TypeError 12 | | ApplicatorKeywordError 13 | | ValidationKeywordError 14 | | FormatError 15 | | UnevaluatedPropertiesError 16 | | UnevaluatedItemsError 17 | | DependentRequiredError 18 | | DiscriminatorError 19 | -------------------------------------------------------------------------------- /lib/vocabularies/format/index.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../../types" 2 | import formatKeyword from "./format" 3 | 4 | const format: Vocabulary = [formatKeyword] 5 | 6 | export default format 7 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/elements.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, SchemaObject} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {alwaysValidSchema} from "../../compile/util" 4 | import {validateArray} from "../code" 5 | import {_, not} from "../../compile/codegen" 6 | import {checkMetadata} from "./metadata" 7 | import {checkNullable} from "./nullable" 8 | import {typeError, _JTDTypeError} from "./error" 9 | 10 | export type JTDElementsError = _JTDTypeError<"elements", "array", SchemaObject> 11 | 12 | const def: CodeKeywordDefinition = { 13 | keyword: "elements", 14 | schemaType: "object", 15 | error: typeError("array"), 16 | code(cxt: KeywordCxt) { 17 | checkMetadata(cxt) 18 | const {gen, data, schema, it} = cxt 19 | if (alwaysValidSchema(it, schema)) return 20 | const [valid] = checkNullable(cxt) 21 | gen.if(not(valid), () => 22 | gen.if( 23 | _`Array.isArray(${data})`, 24 | () => gen.assign(valid, validateArray(cxt)), 25 | () => cxt.error() 26 | ) 27 | ) 28 | cxt.ok(valid) 29 | }, 30 | } 31 | 32 | export default def 33 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/error.ts: -------------------------------------------------------------------------------- 1 | import type {KeywordErrorDefinition, KeywordErrorCxt, ErrorObject} from "../../types" 2 | import {_, Code} from "../../compile/codegen" 3 | 4 | export type _JTDTypeError = ErrorObject< 5 | K, 6 | {type: T; nullable: boolean}, 7 | S 8 | > 9 | 10 | export function typeError(t: string): KeywordErrorDefinition { 11 | return { 12 | message: (cxt) => typeErrorMessage(cxt, t), 13 | params: (cxt) => typeErrorParams(cxt, t), 14 | } 15 | } 16 | 17 | export function typeErrorMessage({parentSchema}: KeywordErrorCxt, t: string): string { 18 | return parentSchema?.nullable ? `must be ${t} or null` : `must be ${t}` 19 | } 20 | 21 | export function typeErrorParams({parentSchema}: KeywordErrorCxt, t: string): Code { 22 | return _`{type: ${t}, nullable: ${!!parentSchema?.nullable}}` 23 | } 24 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/index.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../../types" 2 | import refKeyword from "./ref" 3 | import typeKeyword, {JTDTypeError} from "./type" 4 | import enumKeyword, {JTDEnumError} from "./enum" 5 | import elements, {JTDElementsError} from "./elements" 6 | import properties, {JTDPropertiesError} from "./properties" 7 | import optionalProperties from "./optionalProperties" 8 | import discriminator, {JTDDiscriminatorError} from "./discriminator" 9 | import values, {JTDValuesError} from "./values" 10 | import union from "./union" 11 | import metadata from "./metadata" 12 | 13 | const jtdVocabulary: Vocabulary = [ 14 | "definitions", 15 | refKeyword, 16 | typeKeyword, 17 | enumKeyword, 18 | elements, 19 | properties, 20 | optionalProperties, 21 | discriminator, 22 | values, 23 | union, 24 | metadata, 25 | {keyword: "additionalProperties", schemaType: "boolean"}, 26 | {keyword: "nullable", schemaType: "boolean"}, 27 | ] 28 | 29 | export default jtdVocabulary 30 | 31 | export type JTDErrorObject = 32 | | JTDTypeError 33 | | JTDEnumError 34 | | JTDElementsError 35 | | JTDPropertiesError 36 | | JTDDiscriminatorError 37 | | JTDValuesError 38 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/metadata.ts: -------------------------------------------------------------------------------- 1 | import {KeywordCxt} from "../../ajv" 2 | import type {CodeKeywordDefinition} from "../../types" 3 | import {alwaysValidSchema} from "../../compile/util" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: "metadata", 7 | schemaType: "object", 8 | code(cxt: KeywordCxt) { 9 | checkMetadata(cxt) 10 | const {gen, schema, it} = cxt 11 | if (alwaysValidSchema(it, schema)) return 12 | const valid = gen.name("valid") 13 | cxt.subschema({keyword: "metadata", jtdMetadata: true}, valid) 14 | cxt.ok(valid) 15 | }, 16 | } 17 | 18 | export function checkMetadata({it, keyword}: KeywordCxt, metadata?: boolean): void { 19 | if (it.jtdMetadata !== metadata) { 20 | throw new Error(`JTD: "${keyword}" cannot be used in this schema location`) 21 | } 22 | } 23 | 24 | export default def 25 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/nullable.ts: -------------------------------------------------------------------------------- 1 | import type {KeywordCxt} from "../../compile/validate" 2 | import {_, not, nil, Code, Name} from "../../compile/codegen" 3 | 4 | export function checkNullable( 5 | {gen, data, parentSchema}: KeywordCxt, 6 | cond: Code = nil 7 | ): [Name, Code] { 8 | const valid = gen.name("valid") 9 | if (parentSchema.nullable) { 10 | gen.let(valid, _`${data} === null`) 11 | cond = not(valid) 12 | } else { 13 | gen.let(valid, false) 14 | } 15 | return [valid, cond] 16 | } 17 | 18 | export function checkNullableObject(cxt: KeywordCxt, cond: Code): [Name, Code] { 19 | const [valid, cond_] = checkNullable(cxt, cond) 20 | return [valid, _`${cond_} && typeof ${cxt.data} == "object" && !Array.isArray(${cxt.data})`] 21 | } 22 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/optionalProperties.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {validateProperties, error} from "./properties" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: "optionalProperties", 7 | schemaType: "object", 8 | error, 9 | code(cxt: KeywordCxt) { 10 | if (cxt.parentSchema.properties) return 11 | validateProperties(cxt) 12 | }, 13 | } 14 | 15 | export default def 16 | -------------------------------------------------------------------------------- /lib/vocabularies/jtd/union.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import {validateUnion} from "../code" 3 | 4 | const def: CodeKeywordDefinition = { 5 | keyword: "union", 6 | schemaType: "array", 7 | trackErrors: true, 8 | code: validateUnion, 9 | error: {message: "must match a schema in union"}, 10 | } 11 | 12 | export default def 13 | -------------------------------------------------------------------------------- /lib/vocabularies/metadata.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../types" 2 | 3 | export const metadataVocabulary: Vocabulary = [ 4 | "title", 5 | "description", 6 | "default", 7 | "deprecated", 8 | "readOnly", 9 | "writeOnly", 10 | "examples", 11 | ] 12 | 13 | export const contentVocabulary: Vocabulary = [ 14 | "contentMediaType", 15 | "contentEncoding", 16 | "contentSchema", 17 | ] 18 | -------------------------------------------------------------------------------- /lib/vocabularies/next.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../types" 2 | import dependentRequired from "./validation/dependentRequired" 3 | import dependentSchemas from "./applicator/dependentSchemas" 4 | import limitContains from "./validation/limitContains" 5 | 6 | const next: Vocabulary = [dependentRequired, dependentSchemas, limitContains] 7 | 8 | export default next 9 | -------------------------------------------------------------------------------- /lib/vocabularies/unevaluated/index.ts: -------------------------------------------------------------------------------- 1 | import type {Vocabulary} from "../../types" 2 | import unevaluatedProperties from "./unevaluatedProperties" 3 | import unevaluatedItems from "./unevaluatedItems" 4 | 5 | const unevaluated: Vocabulary = [unevaluatedProperties, unevaluatedItems] 6 | 7 | export default unevaluated 8 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/const.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_} from "../../compile/codegen" 4 | import {useFunc} from "../../compile/util" 5 | import equal from "../../runtime/equal" 6 | 7 | export type ConstError = ErrorObject<"const", {allowedValue: any}> 8 | 9 | const error: KeywordErrorDefinition = { 10 | message: "must be equal to constant", 11 | params: ({schemaCode}) => _`{allowedValue: ${schemaCode}}`, 12 | } 13 | 14 | const def: CodeKeywordDefinition = { 15 | keyword: "const", 16 | $data: true, 17 | error, 18 | code(cxt: KeywordCxt) { 19 | const {gen, data, $data, schemaCode, schema} = cxt 20 | if ($data || (schema && typeof schema == "object")) { 21 | cxt.fail$data(_`!${useFunc(gen, equal)}(${data}, ${schemaCode})`) 22 | } else { 23 | cxt.fail(_`${schema} !== ${data}`) 24 | } 25 | }, 26 | } 27 | 28 | export default def 29 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/dependentRequired.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorObject} from "../../types" 2 | import { 3 | validatePropertyDeps, 4 | error, 5 | DependenciesErrorParams, 6 | PropertyDependencies, 7 | } from "../applicator/dependencies" 8 | 9 | export type DependentRequiredError = ErrorObject< 10 | "dependentRequired", 11 | DependenciesErrorParams, 12 | PropertyDependencies 13 | > 14 | 15 | const def: CodeKeywordDefinition = { 16 | keyword: "dependentRequired", 17 | type: "object", 18 | schemaType: "object", 19 | error, 20 | code: (cxt) => validatePropertyDeps(cxt), 21 | } 22 | 23 | export default def 24 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/index.ts: -------------------------------------------------------------------------------- 1 | import type {ErrorObject, Vocabulary} from "../../types" 2 | import limitNumber, {LimitNumberError} from "./limitNumber" 3 | import multipleOf, {MultipleOfError} from "./multipleOf" 4 | import limitLength from "./limitLength" 5 | import pattern, {PatternError} from "./pattern" 6 | import limitProperties from "./limitProperties" 7 | import required, {RequiredError} from "./required" 8 | import limitItems from "./limitItems" 9 | import uniqueItems, {UniqueItemsError} from "./uniqueItems" 10 | import constKeyword, {ConstError} from "./const" 11 | import enumKeyword, {EnumError} from "./enum" 12 | 13 | const validation: Vocabulary = [ 14 | // number 15 | limitNumber, 16 | multipleOf, 17 | // string 18 | limitLength, 19 | pattern, 20 | // object 21 | limitProperties, 22 | required, 23 | // array 24 | limitItems, 25 | uniqueItems, 26 | // any 27 | {keyword: "type", schemaType: ["string", "array"]}, 28 | {keyword: "nullable", schemaType: "boolean"}, 29 | constKeyword, 30 | enumKeyword, 31 | ] 32 | 33 | export default validation 34 | 35 | type LimitError = ErrorObject< 36 | "maxItems" | "minItems" | "minProperties" | "maxProperties" | "minLength" | "maxLength", 37 | {limit: number}, 38 | number | {$data: string} 39 | > 40 | 41 | export type ValidationKeywordError = 42 | | LimitError 43 | | LimitNumberError 44 | | MultipleOfError 45 | | PatternError 46 | | RequiredError 47 | | UniqueItemsError 48 | | ConstError 49 | | EnumError 50 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/limitContains.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {checkStrictMode} from "../../compile/util" 4 | 5 | const def: CodeKeywordDefinition = { 6 | keyword: ["maxContains", "minContains"], 7 | type: "array", 8 | schemaType: "number", 9 | code({keyword, parentSchema, it}: KeywordCxt) { 10 | if (parentSchema.contains === undefined) { 11 | checkStrictMode(it, `"${keyword}" without "contains" is ignored`) 12 | } 13 | }, 14 | } 15 | 16 | export default def 17 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/limitItems.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, str, operators} from "../../compile/codegen" 4 | 5 | const error: KeywordErrorDefinition = { 6 | message({keyword, schemaCode}) { 7 | const comp = keyword === "maxItems" ? "more" : "fewer" 8 | return str`must NOT have ${comp} than ${schemaCode} items` 9 | }, 10 | params: ({schemaCode}) => _`{limit: ${schemaCode}}`, 11 | } 12 | 13 | const def: CodeKeywordDefinition = { 14 | keyword: ["maxItems", "minItems"], 15 | type: "array", 16 | schemaType: "number", 17 | $data: true, 18 | error, 19 | code(cxt: KeywordCxt) { 20 | const {keyword, data, schemaCode} = cxt 21 | const op = keyword === "maxItems" ? operators.GT : operators.LT 22 | cxt.fail$data(_`${data}.length ${op} ${schemaCode}`) 23 | }, 24 | } 25 | 26 | export default def 27 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/limitLength.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, str, operators} from "../../compile/codegen" 4 | import {useFunc} from "../../compile/util" 5 | import ucs2length from "../../runtime/ucs2length" 6 | 7 | const error: KeywordErrorDefinition = { 8 | message({keyword, schemaCode}) { 9 | const comp = keyword === "maxLength" ? "more" : "fewer" 10 | return str`must NOT have ${comp} than ${schemaCode} characters` 11 | }, 12 | params: ({schemaCode}) => _`{limit: ${schemaCode}}`, 13 | } 14 | 15 | const def: CodeKeywordDefinition = { 16 | keyword: ["maxLength", "minLength"], 17 | type: "string", 18 | schemaType: "number", 19 | $data: true, 20 | error, 21 | code(cxt: KeywordCxt) { 22 | const {keyword, data, schemaCode, it} = cxt 23 | const op = keyword === "maxLength" ? operators.GT : operators.LT 24 | const len = 25 | it.opts.unicode === false ? _`${data}.length` : _`${useFunc(cxt.gen, ucs2length)}(${data})` 26 | cxt.fail$data(_`${len} ${op} ${schemaCode}`) 27 | }, 28 | } 29 | 30 | export default def 31 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/limitNumber.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, str, operators, Code} from "../../compile/codegen" 4 | 5 | const ops = operators 6 | 7 | type Kwd = "maximum" | "minimum" | "exclusiveMaximum" | "exclusiveMinimum" 8 | 9 | type Comparison = "<=" | ">=" | "<" | ">" 10 | 11 | const KWDs: {[K in Kwd]: {okStr: Comparison; ok: Code; fail: Code}} = { 12 | maximum: {okStr: "<=", ok: ops.LTE, fail: ops.GT}, 13 | minimum: {okStr: ">=", ok: ops.GTE, fail: ops.LT}, 14 | exclusiveMaximum: {okStr: "<", ok: ops.LT, fail: ops.GTE}, 15 | exclusiveMinimum: {okStr: ">", ok: ops.GT, fail: ops.LTE}, 16 | } 17 | 18 | export type LimitNumberError = ErrorObject< 19 | Kwd, 20 | {limit: number; comparison: Comparison}, 21 | number | {$data: string} 22 | > 23 | 24 | const error: KeywordErrorDefinition = { 25 | message: ({keyword, schemaCode}) => str`must be ${KWDs[keyword as Kwd].okStr} ${schemaCode}`, 26 | params: ({keyword, schemaCode}) => 27 | _`{comparison: ${KWDs[keyword as Kwd].okStr}, limit: ${schemaCode}}`, 28 | } 29 | 30 | const def: CodeKeywordDefinition = { 31 | keyword: Object.keys(KWDs), 32 | type: "number", 33 | schemaType: "number", 34 | $data: true, 35 | error, 36 | code(cxt: KeywordCxt) { 37 | const {keyword, data, schemaCode} = cxt 38 | cxt.fail$data(_`${data} ${KWDs[keyword as Kwd].fail} ${schemaCode} || isNaN(${data})`) 39 | }, 40 | } 41 | 42 | export default def 43 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/limitProperties.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, str, operators} from "../../compile/codegen" 4 | 5 | const error: KeywordErrorDefinition = { 6 | message({keyword, schemaCode}) { 7 | const comp = keyword === "maxProperties" ? "more" : "fewer" 8 | return str`must NOT have ${comp} than ${schemaCode} properties` 9 | }, 10 | params: ({schemaCode}) => _`{limit: ${schemaCode}}`, 11 | } 12 | 13 | const def: CodeKeywordDefinition = { 14 | keyword: ["maxProperties", "minProperties"], 15 | type: "object", 16 | schemaType: "number", 17 | $data: true, 18 | error, 19 | code(cxt: KeywordCxt) { 20 | const {keyword, data, schemaCode} = cxt 21 | const op = keyword === "maxProperties" ? operators.GT : operators.LT 22 | cxt.fail$data(_`Object.keys(${data}).length ${op} ${schemaCode}`) 23 | }, 24 | } 25 | 26 | export default def 27 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/multipleOf.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {_, str} from "../../compile/codegen" 4 | 5 | export type MultipleOfError = ErrorObject< 6 | "multipleOf", 7 | {multipleOf: number}, 8 | number | {$data: string} 9 | > 10 | 11 | const error: KeywordErrorDefinition = { 12 | message: ({schemaCode}) => str`must be multiple of ${schemaCode}`, 13 | params: ({schemaCode}) => _`{multipleOf: ${schemaCode}}`, 14 | } 15 | 16 | const def: CodeKeywordDefinition = { 17 | keyword: "multipleOf", 18 | type: "number", 19 | schemaType: "number", 20 | $data: true, 21 | error, 22 | code(cxt: KeywordCxt) { 23 | const {gen, data, schemaCode, it} = cxt 24 | // const bdt = bad$DataType(schemaCode, def.schemaType, $data) 25 | const prec = it.opts.multipleOfPrecision 26 | const res = gen.let("res") 27 | const invalid = prec 28 | ? _`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` 29 | : _`${res} !== parseInt(${res})` 30 | cxt.fail$data(_`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`) 31 | }, 32 | } 33 | 34 | export default def 35 | -------------------------------------------------------------------------------- /lib/vocabularies/validation/pattern.ts: -------------------------------------------------------------------------------- 1 | import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types" 2 | import type {KeywordCxt} from "../../compile/validate" 3 | import {usePattern} from "../code" 4 | import {_, str} from "../../compile/codegen" 5 | 6 | export type PatternError = ErrorObject<"pattern", {pattern: string}, string | {$data: string}> 7 | 8 | const error: KeywordErrorDefinition = { 9 | message: ({schemaCode}) => str`must match pattern "${schemaCode}"`, 10 | params: ({schemaCode}) => _`{pattern: ${schemaCode}}`, 11 | } 12 | 13 | const def: CodeKeywordDefinition = { 14 | keyword: "pattern", 15 | type: "string", 16 | schemaType: "string", 17 | $data: true, 18 | error, 19 | code(cxt: KeywordCxt) { 20 | const {data, $data, schema, schemaCode, it} = cxt 21 | // TODO regexp should be wrapped in try/catchs 22 | const u = it.opts.unicodeRegExp ? "u" : "" 23 | const regExp = $data ? _`(new RegExp(${schemaCode}, ${u}))` : usePattern(cxt, schema) 24 | cxt.fail$data(_`!${regExp}.test(${data})`) 25 | }, 26 | } 27 | 28 | export default def 29 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from "@rollup/plugin-commonjs" 2 | import {nodeResolve} from "@rollup/plugin-node-resolve" 3 | import json from "@rollup/plugin-json" 4 | import typescript from "@rollup/plugin-typescript" 5 | import {terser} from "rollup-plugin-terser" 6 | 7 | function createBundleConfig(sourceFile, outFile, globalName) { 8 | return { 9 | input: `./lib/${sourceFile}.ts`, 10 | output: [ 11 | { 12 | file: `./bundle/${outFile}.bundle.js`, 13 | format: "umd", 14 | name: globalName, 15 | }, 16 | { 17 | file: `./bundle/${outFile}.min.js`, 18 | format: "umd", 19 | name: globalName, 20 | sourcemap: true, 21 | plugins: [terser()], 22 | }, 23 | ], 24 | plugins: [commonjs(), nodeResolve(), json(), typescript()], 25 | } 26 | } 27 | 28 | export default [ 29 | createBundleConfig("ajv", "ajv7", "ajv7"), 30 | createBundleConfig("2019", "ajv2019", "ajv2019"), 31 | createBundleConfig("2020", "ajv2020", "ajv2020"), 32 | createBundleConfig("jtd", "ajvJTD", "ajvJTD"), 33 | ] 34 | -------------------------------------------------------------------------------- /scripts/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | parserOptions: 2 | sourceType: script 3 | rules: 4 | no-console: 0 5 | no-empty: [2, allowEmptyCatch: true] 6 | -------------------------------------------------------------------------------- /scripts/bundle.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const fs = require("fs") 4 | const path = require("path") 5 | const browserify = require("browserify") 6 | const {minify} = require("terser") 7 | 8 | const [sourceFile, outFile, globalName] = process.argv.slice(2) 9 | 10 | const json = require(path.join(__dirname, "..", "package.json")) 11 | const bundleDir = path.join(__dirname, "..", "bundle") 12 | if (!fs.existsSync(bundleDir)) fs.mkdirSync(bundleDir) 13 | 14 | browserify({standalone: globalName}) 15 | .require(path.join(__dirname, "../dist", sourceFile), {expose: sourceFile}) 16 | .bundle(saveAndMinify) 17 | 18 | async function saveAndMinify(err, buf) { 19 | if (err) { 20 | console.error("browserify error:", err) 21 | process.exit(1) 22 | } 23 | 24 | const bundlePath = path.join(bundleDir, outFile) 25 | const opts = { 26 | ecma: 2018, 27 | warnings: true, 28 | compress: { 29 | pure_getters: true, 30 | keep_infinity: true, 31 | unsafe_methods: true, 32 | }, 33 | format: { 34 | preamble: `/* ${json.name} ${json.version} (${globalName}): ${json.description} */`, 35 | }, 36 | sourceMap: { 37 | filename: outFile + ".min.js", 38 | url: outFile + ".min.js.map", 39 | }, 40 | } 41 | 42 | const result = await minify(buf.toString(), opts) 43 | 44 | fs.writeFileSync(bundlePath + ".bundle.js", buf) 45 | fs.writeFileSync(bundlePath + ".min.js", result.code) 46 | fs.writeFileSync(bundlePath + ".min.js.map", result.map) 47 | if (result.warnings) result.warnings.forEach((msg) => console.warn("terser.minify warning:", msg)) 48 | } 49 | -------------------------------------------------------------------------------- /scripts/get-ajv-packages: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | declare -a packages=( 4 | "ajv-keywords" 5 | "ajv-formats" 6 | "ajv-cli" 7 | "ajv-errors" 8 | "ajv-i18n" 9 | ) 10 | 11 | for package in "${packages[@]}" 12 | do 13 | echo "downloading $package README..." 14 | echo "---" > docs/packages/$package.md 15 | echo "editLink: https://github.com/ajv-validator/$package/edit/master/README.md" >> docs/packages/$package.md 16 | echo "---" >> docs/packages/$package.md 17 | echo "[$package repository](https://github.com/ajv-validator/$package)" >> docs/packages/$package.md 18 | echo "" >> docs/packages/$package.md 19 | curl -L https://raw.githubusercontent.com/ajv-validator/$package/master/README.md >> ./docs/packages/$package.md 20 | done 21 | -------------------------------------------------------------------------------- /scripts/jsontests.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const testSuitePaths = { 4 | draft6: "spec/JSON-Schema-Test-Suite/tests/draft6/", 5 | draft7: "spec/JSON-Schema-Test-Suite/tests/draft7/", 6 | draft2019: "spec/JSON-Schema-Test-Suite/tests/draft2019-09/", 7 | draft2020: "spec/JSON-Schema-Test-Suite/tests/draft2020-12/", 8 | tests: "spec/tests/", 9 | security: "spec/security/", 10 | extras: "spec/extras/", 11 | async: "spec/async/", 12 | } 13 | 14 | const glob = require("glob") 15 | const fs = require("fs") 16 | 17 | for (const suite in testSuitePaths) { 18 | const p = testSuitePaths[suite] 19 | const files = glob.sync(`${p}{**/,}*.json`) 20 | if (files.length === 0) { 21 | console.error(`Missing folder ${p}\nTry: git submodule update --init\n`) 22 | process.exit(1) 23 | } 24 | const code = files 25 | .map((f) => { 26 | const name = f.replace(p, "").replace(/\.json$/, "") 27 | const testPath = f.replace(/^spec/, "..") 28 | return `\n {name: "${name}", test: require("${testPath}")},` 29 | }) 30 | .reduce((list, f) => list + f) 31 | fs.writeFileSync(`./spec/_json/${suite}.js`, `module.exports = [${code}\n]\n`) 32 | } 33 | -------------------------------------------------------------------------------- /scripts/prepare-site: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | function copyReplace { 6 | sed "s/](.\/docs\//](.\//g" $1 > $2 7 | } 8 | 9 | copyReplace CODE_OF_CONDUCT.md docs/code_of_conduct.md 10 | copyReplace CONTRIBUTING.md docs/contributing.md 11 | copyReplace LICENSE docs/license.md 12 | 13 | echo "preparing contributors..." 14 | node scripts/get-contributors.js 15 | 16 | echo "preparing packages..." 17 | ./scripts/get-ajv-packages 18 | -------------------------------------------------------------------------------- /scripts/prepare-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | mkdir -p .browser 6 | 7 | echo 8 | echo Preparing browser tests: 9 | 10 | find spec -type f -name '*.spec.*s' | \ 11 | xargs -I {} sh -c \ 12 | 'export f="{}"; echo $f; ./node_modules/.bin/browserify $f -p [ tsify -p ./spec/tsconfig.json ] -x ajv -u buffer -o $(echo $f | sed -e "s/spec/.browser/" | sed -e "s/.spec.ts/.spec.js/");' 13 | -------------------------------------------------------------------------------- /scripts/publish-bundles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [[ $GITHUB_REF == refs/tags/v* ]]; then 6 | npm run bundle 7 | 8 | echo "About to publish $GITHUB_REF to ajv-dist..." 9 | 10 | git config --global user.name "$GIT_USER_NAME" 11 | git config --global user.email "$GIT_USER_EMAIL" 12 | 13 | git clone https://"${GH_TOKEN_PUBLIC}"@github.com/ajv-validator/ajv-dist.git ../ajv-dist 14 | 15 | rm -rf ../ajv-dist/dist 16 | mkdir ../ajv-dist/dist 17 | cp ./bundle/*.* ../ajv-dist/dist 18 | cd ../ajv-dist 19 | 20 | VERSION=${GITHUB_REF#refs/tags/v} 21 | 22 | sed -E "s/\"version\": \"([^\"]*)\"/\"version\": \"$VERSION\"/" package.json > new_package.json 23 | mv new_package.json package.json 24 | 25 | if [[ $(git status --porcelain) ]]; then 26 | echo "Changes detected. Updating master branch..." 27 | git add -A 28 | git commit -m "$VERSION: updated by ajv workflow https://github.com/ajv-validator/ajv/actions/runs/$GITHUB_RUN_ID" 29 | git push --quiet origin master > /dev/null 2>&1 30 | fi 31 | 32 | echo "Publishing tag..." 33 | git tag "v$VERSION" 34 | git push --tags > /dev/null 2>&1 35 | 36 | echo "Done" 37 | fi 38 | -------------------------------------------------------------------------------- /scripts/publish-site: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | echo "About to publish $GITHUB_REF to gh-pages..." 6 | rm -rf ../gh-pages 7 | 8 | npm install vuepress@1 9 | npm install @vuepress/shared-utils@1 10 | npm run docs:build 11 | 12 | git config --global user.name "$GIT_USER_NAME" 13 | git config --global user.email "$GIT_USER_EMAIL" 14 | git clone -b gh-pages --single-branch https://"${GH_TOKEN_PUBLIC}"@github.com/ajv-validator/ajv.git ../gh-pages 15 | 16 | rsync -a ./docs/.vuepress/dist/ ../gh-pages 17 | 18 | cd ../gh-pages 19 | 20 | if [[ $(git status --porcelain) ]]; then 21 | echo "Changes detected. Updating gh-pages branch..." 22 | git add -A 23 | git commit -m "updated by ajv workflow https://github.com/ajv-validator/ajv/actions/runs/$GITHUB_RUN_ID" 24 | git push --quiet origin gh-pages > /dev/null 2>&1 25 | fi 26 | 27 | echo "Done" 28 | -------------------------------------------------------------------------------- /spec/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true, 3 | rules: 4 | no-console: off 5 | no-invalid-this: off 6 | globals: 7 | describe: false 8 | it: false 9 | before: false 10 | beforeEach: false 11 | afterEach: false 12 | overrides: 13 | - files: ["*.ts"] 14 | parserOptions: 15 | project: ["./spec/tsconfig.json"] 16 | rules: 17 | "@typescript-eslint/explicit-function-return-type": off 18 | "@typescript-eslint/explicit-member-accessibility": off 19 | "@typescript-eslint/restrict-plus-operands": off 20 | "@typescript-eslint/no-explicit-any": off 21 | "@typescript-eslint/no-unsafe-assignment": off 22 | "@typescript-eslint/no-unsafe-call": off 23 | "@typescript-eslint/no-unsafe-member-access": off 24 | "@typescript-eslint/no-unsafe-return": off 25 | "@typescript-eslint/no-var-requires": off 26 | "@typescript-eslint/ban-ts-comment": off 27 | -------------------------------------------------------------------------------- /spec/_json/README.md: -------------------------------------------------------------------------------- 1 | # Test suites from JSON tests 2 | 3 | These files are generated automatically during the test. 4 | -------------------------------------------------------------------------------- /spec/after_test.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from ".." 2 | import type {AnySchema, ErrorObject} from ".." 3 | import chai from "./chai" 4 | const should = chai.should() 5 | 6 | interface TestResult { 7 | validator: Ajv 8 | schema: AnySchema 9 | data: unknown 10 | valid: boolean 11 | expected: boolean 12 | errors: ErrorObject[] | null 13 | passed: boolean // true if valid == expected 14 | } 15 | 16 | export function afterError(res: TestResult): void { 17 | console.log("ajv options:", res.validator.opts) 18 | } 19 | 20 | export function afterEach(res: TestResult): void { 21 | // console.log(res.errors); 22 | res.valid.should.be.a("boolean") 23 | if (res.valid === true) { 24 | should.equal(res.errors, null) 25 | } else { 26 | const errs = res.errors as ErrorObject[] 27 | errs.should.be.an("array") 28 | for (const err of errs) { 29 | err.should.be.an("object") 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spec/ajv.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from "../dist/core" 2 | const AjvClass: typeof Ajv = typeof window == "object" ? (window as any).ajv7 : require("" + "..") 3 | 4 | export default AjvClass 5 | module.exports = AjvClass 6 | module.exports.default = AjvClass 7 | -------------------------------------------------------------------------------- /spec/ajv2019.ts: -------------------------------------------------------------------------------- 1 | import type Ajv2019 from "../dist/2019" 2 | const AjvClass: typeof Ajv2019 = 3 | typeof window == "object" ? (window as any).ajv2019 : require("" + "../dist/2019") 4 | 5 | export default AjvClass 6 | module.exports = AjvClass 7 | module.exports.default = AjvClass 8 | -------------------------------------------------------------------------------- /spec/ajv2020.ts: -------------------------------------------------------------------------------- 1 | import type Ajv2020 from "../dist/2019" 2 | const AjvClass: typeof Ajv2020 = 3 | typeof window == "object" ? (window as any).ajv2020 : require("" + "../dist/2020") 4 | 5 | export default AjvClass 6 | module.exports = AjvClass 7 | module.exports.default = AjvClass 8 | -------------------------------------------------------------------------------- /spec/ajv_all_instances.ts: -------------------------------------------------------------------------------- 1 | import type AjvCore from "../dist/core" 2 | import type {Options} from ".." 3 | import _Ajv from "./ajv" 4 | import _Ajv2019 from "./ajv2019" 5 | import getAjvInstances from "./ajv_instances" 6 | 7 | export default function getAjvAllInstances(options: Options, extraOpts: Options = {}): AjvCore[] { 8 | return [...getAjvs(_Ajv), ...getAjvs(_Ajv2019)] 9 | 10 | function getAjvs(Ajv: typeof AjvCore): AjvCore[] { 11 | return getAjvInstances(Ajv, options, extraOpts) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spec/ajv_async_instances.ts: -------------------------------------------------------------------------------- 1 | import getAjvInstances from "./ajv_instances" 2 | import _Ajv from "./ajv" 3 | import type AjvCore from "../dist/core" 4 | import type {Options} from ".." 5 | 6 | export default function getAjvSyncInstances(extraOpts?: Options): AjvCore[] { 7 | return getAjvInstances( 8 | _Ajv, 9 | { 10 | strict: false, 11 | allErrors: true, 12 | code: {lines: true, optimize: false}, 13 | }, 14 | extraOpts 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /spec/ajv_instances.ts: -------------------------------------------------------------------------------- 1 | import type AjvCore from "../dist/core" 2 | import type {Options} from ".." 3 | 4 | export default function getAjvInstances( 5 | _Ajv: typeof AjvCore, 6 | options: Options, 7 | extraOpts: Options = {} 8 | ): AjvCore[] { 9 | return _getAjvInstances(options, {...extraOpts, logger: false}) 10 | 11 | function _getAjvInstances(opts: Options, useOpts: Options): AjvCore[] { 12 | const optNames = Object.keys(opts) 13 | if (optNames.length) { 14 | opts = Object.assign({}, opts) 15 | const useOpts1 = Object.assign({}, useOpts) 16 | const optName = optNames[0] 17 | useOpts1[optName] = opts[optName] 18 | delete opts[optName] 19 | return [..._getAjvInstances(opts, useOpts), ..._getAjvInstances(opts, useOpts1)] 20 | } 21 | return [new _Ajv(useOpts)] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec/ajv_jtd.ts: -------------------------------------------------------------------------------- 1 | import type AjvJTD from "../dist/jtd" 2 | const AjvClass: typeof AjvJTD = 3 | typeof window == "object" ? (window as any).ajvJTD : require("" + "../dist/jtd") 4 | 5 | export default AjvClass 6 | module.exports = AjvClass 7 | module.exports.default = AjvClass 8 | -------------------------------------------------------------------------------- /spec/ajv_options.ts: -------------------------------------------------------------------------------- 1 | import type {Options} from ".." 2 | 3 | const isBrowser = typeof window == "object" 4 | const fullTest = !isBrowser && process.env.AJV_FULL_TEST 5 | 6 | const codeOptions = {es5: true, lines: true, optimize: false} 7 | 8 | const options: Options = fullTest 9 | ? { 10 | allErrors: true, 11 | verbose: true, 12 | inlineRefs: false, 13 | code: codeOptions, 14 | } 15 | : {allErrors: true, code: codeOptions} 16 | 17 | export default options 18 | -------------------------------------------------------------------------------- /spec/ajv_standalone.ts: -------------------------------------------------------------------------------- 1 | import type AjvCore from "../dist/core" 2 | import type {Options} from ".." 3 | import AjvPack from "../dist/standalone/instance" 4 | 5 | export function withStandalone(instances: AjvCore[]): (AjvCore | AjvPack)[] { 6 | return [...(instances as (AjvCore | AjvPack)[]), ...instances.map(makeStandalone)] 7 | } 8 | 9 | function makeStandalone(ajv: AjvCore): AjvPack { 10 | ajv.opts.code.source = true 11 | return new AjvPack(ajv) 12 | } 13 | 14 | export function getStandalone(_Ajv: typeof AjvCore, opts: Options = {}): AjvPack { 15 | opts.code ||= {} 16 | opts.code.source = true 17 | return new AjvPack(new _Ajv(opts)) 18 | } 19 | -------------------------------------------------------------------------------- /spec/async/items.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "items: async + sync", 4 | "schema": { 5 | "$async": true, 6 | "type": "array", 7 | "items": [ 8 | { 9 | "type": "integer", 10 | "idExists": {"table": "users"} 11 | }, 12 | { 13 | "type": "integer" 14 | }, 15 | { 16 | "type": "integer", 17 | "idExists": {"table": "users"} 18 | } 19 | ], 20 | "minItems": 3, 21 | "additionalItems": false 22 | }, 23 | "tests": [ 24 | { 25 | "description": "valid array", 26 | "data": [1, 2, 5], 27 | "valid": true 28 | }, 29 | { 30 | "description": "another valid array", 31 | "data": [5, 2, 8], 32 | "valid": true 33 | }, 34 | { 35 | "description": "invalid 1st async item", 36 | "data": [9, 2, 8], 37 | "valid": false 38 | }, 39 | { 40 | "description": "invalid 2nd async item", 41 | "data": [1, 2, 9], 42 | "valid": false 43 | }, 44 | { 45 | "description": "invalid sync item", 46 | "data": [1, "abc", 5], 47 | "valid": false 48 | } 49 | ] 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /spec/async/no_async.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "async schema without async elements", 4 | "schema": { 5 | "$async": true, 6 | "type": "string", 7 | "maxLength": 3 8 | }, 9 | "tests": [ 10 | { 11 | "description": "string <= 3 chars is valid", 12 | "data": "abc", 13 | "valid": true 14 | }, 15 | { 16 | "description": "string > 3 chars is invalid", 17 | "data": "abcd", 18 | "valid": false 19 | }, 20 | { 21 | "description": "number is invalid", 22 | "data": 1, 23 | "valid": false 24 | } 25 | ] 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /spec/async/properties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "properties: async + sync", 4 | "schema": { 5 | "$async": true, 6 | "type": "object", 7 | "properties": { 8 | "foo": { 9 | "type": "integer", 10 | "idExists": {"table": "users"} 11 | }, 12 | "bar": { 13 | "type": "integer" 14 | } 15 | } 16 | }, 17 | "tests": [ 18 | { 19 | "description": "valid object", 20 | "data": {"foo": 1, "bar": 2}, 21 | "valid": true 22 | }, 23 | { 24 | "description": "another valid object", 25 | "data": {"foo": 5, "bar": 2}, 26 | "valid": true 27 | }, 28 | { 29 | "description": "invalid sync property", 30 | "data": {"foo": 1, "bar": "abc"}, 31 | "valid": false 32 | }, 33 | { 34 | "description": "invalid async property", 35 | "data": {"foo": 9, "bar": 2}, 36 | "valid": false 37 | } 38 | ] 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /spec/chai.ts: -------------------------------------------------------------------------------- 1 | import type {ChaiStatic} from "./chai_type" 2 | 3 | const chai: ChaiStatic = typeof window == "object" ? (window as any).chai : require("" + "chai") 4 | 5 | export default chai 6 | -------------------------------------------------------------------------------- /spec/chai_type.ts: -------------------------------------------------------------------------------- 1 | import "chai" 2 | export type ChaiStatic = Chai.ChaiStatic 3 | -------------------------------------------------------------------------------- /spec/extras.spec.ts: -------------------------------------------------------------------------------- 1 | import getAjvAllInstances from "./ajv_all_instances" 2 | import {withStandalone} from "./ajv_standalone" 3 | import {_} from "../dist/compile/codegen/code" 4 | import jsonSchemaTest = require("json-schema-test") 5 | import options from "./ajv_options" 6 | import {afterError, afterEach} from "./after_test" 7 | import chai from "./chai" 8 | 9 | const instances = getAjvAllInstances(options, { 10 | $data: true, 11 | formats: {allowedUnknown: true}, 12 | strictTypes: false, 13 | strictTuples: false, 14 | }) 15 | 16 | instances.forEach((ajv) => { 17 | ajv.opts.code.source = true 18 | ajv.opts.code.formats = _`{allowedUnknown: true}` 19 | }) 20 | 21 | jsonSchemaTest(withStandalone(instances), { 22 | description: 23 | "Extra keywords schemas tests of " + instances.length + " ajv instances with different options", 24 | suites: {extras: require("./_json/extras")}, 25 | assert: chai.assert, 26 | afterError, 27 | afterEach, 28 | cwd: __dirname, 29 | hideFolder: "extras/", 30 | timeout: 90000, 31 | }) 32 | -------------------------------------------------------------------------------- /spec/extras/$data/format.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "one property has format set in another property", 4 | "schema": { 5 | "properties": { 6 | "str": { 7 | "format": { 8 | "$data": "1/strFormat" 9 | } 10 | }, 11 | "strFormat": {} 12 | } 13 | }, 14 | "tests": [ 15 | { 16 | "description": "allowed unknown format is valid", 17 | "data": { 18 | "str": "any value", 19 | "strFormat": "allowedUnknown" 20 | }, 21 | "valid": true 22 | }, 23 | { 24 | "description": "unknown format is invalid", 25 | "data": { 26 | "str": "any value", 27 | "strFormat": "completelyUnknown" 28 | }, 29 | "valid": false 30 | }, 31 | { 32 | "description": "valid if the format is undefined", 33 | "data": { 34 | "str": "any value" 35 | }, 36 | "valid": true 37 | }, 38 | { 39 | "description": "fails if value of format is not a string", 40 | "data": { 41 | "str": "1963-06-19T08:30:06.283185Z", 42 | "strFormat": 1963 43 | }, 44 | "valid": false 45 | } 46 | ] 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /spec/extras/$data/maxItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "array length is <= than another property", 4 | "schema": { 5 | "properties": { 6 | "maxArrayLength": {}, 7 | "array": {"maxItems": {"$data": "1/maxArrayLength"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "shorter is valid", 13 | "data": { 14 | "maxArrayLength": 2, 15 | "array": [1] 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "maxArrayLength": 2, 23 | "array": [1, 2] 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too long is invalid", 29 | "data": { 30 | "maxArrayLength": 2, 31 | "array": [1, 2, 3] 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-arrays", 37 | "data": { 38 | "maxArrayLength": 2, 39 | "array": "foobar" 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of maxItems is not a number", 45 | "data": { 46 | "maxArrayLength": "2", 47 | "array": [1, 2] 48 | }, 49 | "valid": false 50 | }, 51 | { 52 | "description": "valid if value of maxItems is undefined", 53 | "data": { 54 | "array": [1, 2] 55 | }, 56 | "valid": true 57 | } 58 | ] 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /spec/extras/$data/maxLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "string length is <= than another property", 4 | "schema": { 5 | "properties": { 6 | "maximumLength": {}, 7 | "string": {"maxLength": {"$data": "1/maximumLength"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "shorter is valid", 13 | "data": { 14 | "maximumLength": 2, 15 | "string": "f" 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "maximumLength": 2, 23 | "string": "fo" 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too long is invalid", 29 | "data": { 30 | "maximumLength": 2, 31 | "string": "foo" 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-strings", 37 | "data": { 38 | "maximumLength": 2, 39 | "string": 100 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of maxLength is not a number", 45 | "data": { 46 | "maximumLength": "2", 47 | "string": "f" 48 | }, 49 | "valid": false 50 | }, 51 | { 52 | "description": "valid if value of maxLength is undefined", 53 | "data": { 54 | "string": "f" 55 | }, 56 | "valid": true 57 | } 58 | ] 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /spec/extras/$data/maxProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "number of object properties is <= than another property", 4 | "schema": { 5 | "properties": { 6 | "maxKeys": {}, 7 | "object": {"maxProperties": {"$data": "1/maxKeys"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "shorter is valid", 13 | "data": { 14 | "maxKeys": 2, 15 | "object": {"foo": 1} 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "maxKeys": 2, 23 | "object": {"foo": 1, "bar": 2} 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too long is invalid", 29 | "data": { 30 | "maxKeys": 2, 31 | "object": {"foo": 1, "bar": 2, "baz": 3} 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-objects", 37 | "data": { 38 | "maxKeys": 2, 39 | "object": "foobar" 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of maxProperties is not a number", 45 | "data": { 46 | "maxKeys": "2", 47 | "object": {"foo": 1, "bar": 2} 48 | }, 49 | "valid": false 50 | }, 51 | { 52 | "description": "valid if value of maxProperties is undefined", 53 | "data": { 54 | "object": {"foo": 1, "bar": 2} 55 | }, 56 | "valid": true 57 | } 58 | ] 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /spec/extras/$data/minItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "array length is >= than another property", 4 | "schema": { 5 | "properties": { 6 | "minArrayLength": {}, 7 | "array": {"minItems": {"$data": "1/minArrayLength"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "longer is valid", 13 | "data": { 14 | "minArrayLength": 2, 15 | "array": [1, 2, 3] 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "minArrayLength": 2, 23 | "array": [1, 2] 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too short is invalid", 29 | "data": { 30 | "minArrayLength": 2, 31 | "array": [1] 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-arrays", 37 | "data": { 38 | "minArrayLength": 2, 39 | "array": "foobar" 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of minItems is not a number", 45 | "data": { 46 | "minArrayLength": "2", 47 | "array": [1, 2] 48 | }, 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /spec/extras/$data/minLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "string length is >= than another property", 4 | "schema": { 5 | "properties": { 6 | "minimumLength": {}, 7 | "string": {"minLength": {"$data": "1/minimumLength"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "longer is valid", 13 | "data": { 14 | "minimumLength": 2, 15 | "string": "foo" 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "minimumLength": 2, 23 | "string": "fo" 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too short is invalid", 29 | "data": { 30 | "minimumLength": 2, 31 | "string": "f" 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-strings", 37 | "data": { 38 | "minimumLength": 2, 39 | "string": 100 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of minLength is not a number", 45 | "data": { 46 | "minimumLength": "2", 47 | "string": "foo" 48 | }, 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /spec/extras/$data/minProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "number of object properties is >= than another property", 4 | "schema": { 5 | "properties": { 6 | "minKeys": {}, 7 | "object": {"minProperties": {"$data": "1/minKeys"}} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "longer is valid", 13 | "data": { 14 | "minKeys": 2, 15 | "object": {"foo": 1, "bar": 2, "baz": 3} 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "exact length is valid", 21 | "data": { 22 | "minKeys": 2, 23 | "object": {"foo": 1, "bar": 2} 24 | }, 25 | "valid": true 26 | }, 27 | { 28 | "description": "too short is invalid", 29 | "data": { 30 | "minKeys": 2, 31 | "object": {"foo": 1} 32 | }, 33 | "valid": false 34 | }, 35 | { 36 | "description": "ignores non-objects", 37 | "data": { 38 | "minKeys": 2, 39 | "object": "foobar" 40 | }, 41 | "valid": true 42 | }, 43 | { 44 | "description": "fails if value of minProperties is not a number", 45 | "data": { 46 | "minKeys": "2", 47 | "object": {"foo": 1, "bar": 2} 48 | }, 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /spec/extras/$data/uniqueItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "uniqueItems in property", 4 | "schema": { 5 | "properties": { 6 | "list": { 7 | "uniqueItems": {"$data": "1/unique"} 8 | }, 9 | "unique": {} 10 | } 11 | }, 12 | "tests": [ 13 | { 14 | "description": "unique array is valid", 15 | "data": { 16 | "list": [1, 2], 17 | "unique": true 18 | }, 19 | "valid": true 20 | }, 21 | { 22 | "description": "non-unique array is invalid", 23 | "data": { 24 | "list": [1, 1], 25 | "unique": true 26 | }, 27 | "valid": false 28 | }, 29 | { 30 | "description": "non-unique array is valid if uniqueItems is false", 31 | "data": { 32 | "list": [1, 1], 33 | "unique": false 34 | }, 35 | "valid": true 36 | }, 37 | { 38 | "description": "non-unique array is valid if uniqueItems is undefined", 39 | "data": { 40 | "list": [1, 1] 41 | }, 42 | "valid": true 43 | }, 44 | { 45 | "description": "fails if uniqueItems is not boolean", 46 | "data": { 47 | "list": [1, 2], 48 | "unique": "true" 49 | }, 50 | "valid": false 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /spec/extras/contains.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "contains keyword requires the item matching schema to be present", 4 | "schema": { 5 | "contains": {"minimum": 5} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "array with item matching schema (5) is valid", 10 | "data": [3, 4, 5], 11 | "valid": true 12 | }, 13 | { 14 | "description": "array with item matching schema (6) is valid", 15 | "data": [3, 4, 6], 16 | "valid": true 17 | }, 18 | { 19 | "description": "array without item matching schema is invalid", 20 | "data": [1, 2, 3, 4], 21 | "valid": false 22 | }, 23 | { 24 | "description": "empty array is invalid", 25 | "data": [], 26 | "valid": false 27 | }, 28 | { 29 | "description": "not array is valid", 30 | "data": {}, 31 | "valid": true 32 | } 33 | ] 34 | }, 35 | { 36 | "description": "contains keyword with const keyword requires a specific item to be present", 37 | "schema": { 38 | "contains": {"const": 5} 39 | }, 40 | "tests": [ 41 | { 42 | "description": "array with item 5 is valid", 43 | "data": [3, 4, 5], 44 | "valid": true 45 | }, 46 | { 47 | "description": "array without item 5 is invalid", 48 | "data": [1, 2, 3, 4], 49 | "valid": false 50 | } 51 | ] 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /spec/issues/1001_addKeyword_and_schema_without_id.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #1001: addKeyword breaks schema without ID", () => { 6 | it("should allow using schemas without ID with addKeyword", () => { 7 | const schema = { 8 | definitions: { 9 | foo: {}, 10 | }, 11 | } 12 | 13 | const ajv: any = new _Ajv() 14 | ajv.addSchema(schema) 15 | ajv.addKeyword("myKeyword") 16 | ajv.getSchema("#/definitions/foo").should.be.a("function") 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /spec/issues/1344_non_root_recursive_ref_standalone.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import standaloneCode from "../../dist/standalone" 3 | import requireFromString = require("require-from-string") 4 | import assert = require("assert") 5 | 6 | const schema = { 7 | $schema: "http://json-schema.org/draft-07/schema#", 8 | $id: "schema", 9 | $ref: "#/$defs/foo", 10 | $defs: { 11 | foo: { 12 | type: "object", 13 | properties: { 14 | bar: {$ref: "#/$defs/bar"}, 15 | }, 16 | }, 17 | bar: { 18 | oneOf: [{$ref: "#/$defs/foo"}], 19 | }, 20 | }, 21 | } 22 | 23 | describe("issue #1344: non-root recursive ref with standalone code", () => { 24 | it("should compile to standalone code", () => { 25 | const ajv = new _Ajv({code: {source: true}}) 26 | ajv.addSchema(schema) 27 | const validate = ajv.getSchema("schema#/$defs/foo") 28 | assert(typeof validate == "function") 29 | assert.strictEqual(validate({}), true) 30 | 31 | const moduleCode = standaloneCode(ajv, validate) 32 | const standaloneValidate = requireFromString(moduleCode) 33 | assert(typeof standaloneValidate == "function") 34 | assert.strictEqual(standaloneValidate({}), true) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /spec/issues/1414_base_uri_change.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import assert = require("assert") 3 | 4 | const schema1 = { 5 | $id: "one", 6 | type: "object", 7 | properties: { 8 | foo: {$ref: "#/definitions/foo"}, 9 | }, 10 | definitions: { 11 | foo: {$ref: "two"}, 12 | }, 13 | } 14 | 15 | const schema2 = { 16 | $id: "two", 17 | type: "object", 18 | properties: { 19 | bar: {$ref: "#/definitions/bar"}, 20 | }, 21 | definitions: { 22 | bar: {type: "string"}, 23 | }, 24 | } 25 | 26 | describe("issue 1414: base URI change", () => { 27 | it("should compile schema", () => { 28 | const ajv = new _Ajv() 29 | ajv.addSchema(schema2) 30 | const validate = ajv.compile(schema1) 31 | assert.strictEqual(typeof validate, "function") 32 | assert.strictEqual(validate({foo: {bar: 1}}), false) 33 | assert.strictEqual(validate({foo: {bar: "1"}}), true) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /spec/issues/1501_jtd_many_properties.spec.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from "../.." 2 | import _Ajv from "../ajv_jtd" 3 | import * as assert from "assert" 4 | 5 | const PROP_COUNT = 10 6 | 7 | describe("schema with many properties", () => { 8 | let ajv: Ajv 9 | const schema = {properties: {}} 10 | const data = {} 11 | const invalidData = {} 12 | 13 | before(() => { 14 | ajv = new _Ajv() 15 | for (let i = 0; i < PROP_COUNT; i++) { 16 | const prop = `prop${i}` 17 | schema.properties[prop] = {type: "uint16"} 18 | data[prop] = i 19 | invalidData[prop] = -i 20 | } 21 | }) 22 | 23 | it("should correctly compile reference to schema", () => { 24 | assert.strictEqual(ajv.validate(schema, data), true) 25 | assert.strictEqual(ajv.validate(schema, invalidData), false) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /spec/issues/1515_evaluated_properties_nested_anyof.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv2019" 2 | import * as assert from "assert" 3 | 4 | describe("tracking evaluated properties with nested anyOf", () => { 5 | it("should initialize evaluated properties", () => { 6 | const ajv = new _Ajv() 7 | 8 | const schema = { 9 | type: "object", 10 | anyOf: [ 11 | { 12 | required: ["foo"], 13 | properties: {foo: {}}, 14 | }, 15 | { 16 | anyOf: [ 17 | { 18 | properties: {bar: {}}, 19 | }, 20 | ], 21 | }, 22 | ], 23 | } 24 | 25 | const validate = ajv.compile(schema) 26 | assert.strictEqual(validate({bar: 1}), true) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /spec/issues/1539_add_keyword_name_to_validation_error.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv2019" 2 | import chai from "../chai" 3 | const should = chai.should() 4 | 5 | describe("keyword usage validation error", () => { 6 | it("should include the keyword name and schema path in the message", () => { 7 | const ajv = new _Ajv({ 8 | keywords: [ 9 | { 10 | keyword: "customKeyword", 11 | metaSchema: { 12 | type: "string", 13 | }, 14 | macro() { 15 | return {} 16 | }, 17 | }, 18 | ], 19 | }) 20 | 21 | const schema = { 22 | type: "object", 23 | properties: { 24 | foo: { 25 | type: "object", 26 | customKeyword: { 27 | bar: true, 28 | }, 29 | }, 30 | }, 31 | } 32 | 33 | should.throw( 34 | () => ajv.compile(schema), 35 | 'keyword "customKeyword" value is invalid at path "#/properties/foo": data must be string' 36 | ) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /spec/issues/1625_evaluated_truthy_pattern_properties.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv2020" 2 | import * as assert from "assert" 3 | 4 | describe("tracking evaluated properties with pattern properties of schema = true", () => { 5 | it("should initialize evaluated properties", () => { 6 | const ajv = new _Ajv() 7 | 8 | const schema = { 9 | type: "object", 10 | patternProperties: { 11 | "^x-": true, 12 | }, 13 | unevaluatedProperties: false, 14 | } 15 | 16 | const validate = ajv.compile(schema) 17 | assert.strictEqual(validate({bar: 1}), false) 18 | assert.strictEqual(validate({"x-bar": false}), true) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /spec/issues/1683_re2_engine.spec.ts: -------------------------------------------------------------------------------- 1 | import getAjvAllInstances from "../ajv_all_instances" 2 | import {withStandalone} from "../ajv_standalone" 3 | import {_} from "../../dist/compile/codegen/code" 4 | import jsonSchemaTest = require("json-schema-test") 5 | import options from "../ajv_options" 6 | import {afterError, afterEach} from "../after_test" 7 | import chai from "../chai" 8 | import re2 from "../../dist/runtime/re2" 9 | import re2tests from "./re2" 10 | 11 | const instances = getAjvAllInstances(options, { 12 | $data: true, 13 | formats: {allowedUnknown: true}, 14 | strictTypes: false, 15 | strictTuples: false, 16 | }) 17 | 18 | instances.forEach((ajv) => { 19 | ajv.opts.code.source = true 20 | ajv.opts.code.formats = _`{allowedUnknown: true}` 21 | ajv.opts.code.regExp = re2 22 | }) 23 | 24 | jsonSchemaTest(withStandalone(instances), { 25 | description: "Test with re2 RegExp engine with " + instances.length + " ajv instances", 26 | suites: {"regular expressions": re2tests}, 27 | assert: chai.assert, 28 | afterError, 29 | afterEach, 30 | cwd: __dirname, 31 | hideFolder: "extras/", 32 | timeout: 90000, 33 | }) 34 | -------------------------------------------------------------------------------- /spec/issues/1819_mincontains.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv2020" 2 | import * as assert from "assert" 3 | 4 | describe("`minContains: 0` without valid items (issue #1819)", () => { 5 | const ajv = new _Ajv() 6 | 7 | const schema = { 8 | type: "array", 9 | minContains: 0, 10 | maxContains: 1, 11 | contains: {type: "number"}, 12 | } 13 | 14 | const validate = ajv.compile(schema) 15 | 16 | it("no items valid", () => assert.strictEqual(validate(["foo"]), true)) 17 | it("1 item valid", () => assert.strictEqual(validate(["foo", 1]), true)) 18 | it("2 items invalid", () => assert.strictEqual(validate(["foo", 1, 2]), false)) 19 | }) 20 | -------------------------------------------------------------------------------- /spec/issues/182_nan_validation.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #182, NaN validation", () => { 6 | const ajv = new _Ajv({strictTypes: false}) 7 | 8 | it("should pass minimum/maximum validation without type", () => { 9 | testNaN(ajv, {minimum: 1}, true) 10 | testNaN(ajv, {maximum: 1}, true) 11 | }) 12 | 13 | it("should NOT pass minimum/maximum validation without type when strict: false", () => { 14 | const _ajv = new _Ajv({strict: false}) 15 | testNaN(_ajv, {minimum: 1}, false) 16 | testNaN(_ajv, {maximum: 1}, false) 17 | }) 18 | 19 | it("should not pass minimum/maximum validation with type", () => { 20 | testNaN(ajv, {type: "number", minimum: 1}, false) 21 | testNaN(ajv, {type: "number", maximum: 1}, false) 22 | }) 23 | 24 | it("should pass type: number validation when strict: false", () => { 25 | const _ajv = new _Ajv({strict: false}) 26 | testNaN(_ajv, {type: "number"}, true) 27 | }) 28 | 29 | it("should not pass type: number validation (changed in v7 - strict by default)", () => { 30 | testNaN(ajv, {type: "number"}, false) 31 | }) 32 | 33 | it("should not pass type: integer validation", () => { 34 | testNaN(ajv, {type: "integer"}, false) 35 | }) 36 | 37 | function testNaN(_ajv, schema, NaNisValid) { 38 | const validate = _ajv.compile(schema) 39 | validate(NaN).should.equal(NaNisValid) 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /spec/issues/1949_jtd_empty_values.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv_jtd" 2 | import * as assert from "assert" 3 | 4 | describe("JTD values with empty schema (issue #1949)", () => { 5 | const ajv = new _Ajv() 6 | 7 | it("should correctly validate empty values form", () => { 8 | const schema = {values: {}} 9 | const validate = ajv.compile(schema) 10 | assert.strictEqual(validate({prop1: 1, prop2: 2}), true) 11 | assert.strictEqual(validate({}), true) 12 | assert.strictEqual(validate(null), false) 13 | assert.strictEqual(validate(1), false) 14 | assert.strictEqual(validate("foo"), false) 15 | assert.strictEqual(validate(undefined), false) 16 | }) 17 | 18 | it("should correctly validate nullable empty values form", () => { 19 | const schema = {values: {}, nullable: true} 20 | const validate = ajv.compile(schema) 21 | assert.strictEqual(validate({prop1: 1, prop2: 2}), true) 22 | assert.strictEqual(validate({}), true) 23 | assert.strictEqual(validate(null), true) 24 | assert.strictEqual(validate(1), false) 25 | assert.strictEqual(validate("foo"), false) 26 | assert.strictEqual(validate(undefined), false) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /spec/issues/2001_jtd_only_optional_properties.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv_jtd" 2 | import * as assert from "assert" 3 | 4 | describe("JTD schema with optional/additional properties only (issue #2001)", () => { 5 | const ajv = new _Ajv() 6 | 7 | it("should correctly serialize optional properties", () => { 8 | const schema = { 9 | optionalProperties: { 10 | prop0: {type: "uint16"}, 11 | prop1: {type: "uint16"}, 12 | prop2: {type: "uint16"}, 13 | }, 14 | additionalProperties: true, 15 | } 16 | const serialize = ajv.compileSerializer(schema) 17 | const test = (data, json) => assert.strictEqual(serialize(data), json) 18 | test({prop0: 0, prop1: 1, prop2: 2}, '{"prop0":0,"prop1":1,"prop2":2}') 19 | test({prop1: 1, prop2: 2}, '{"prop1":1,"prop2":2}') 20 | test({prop0: 0, prop1: 1, prop2: 2, foo: "bar"}, '{"prop0":0,"prop1":1,"prop2":2,"foo":"bar"}') 21 | test({prop1: 1, prop2: 2, foo: "bar"}, '{"prop1":1,"prop2":2,"foo":"bar"}') 22 | test({foo: "bar"}, '{"foo":"bar"}') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /spec/issues/204_options_schemas_data_together.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #204, options schemas and $data used together", () => { 6 | it("should use v5 metaschemas by default", () => { 7 | const ajv = new _Ajv({ 8 | schemas: [{$id: "str", type: "string"}], 9 | $data: true, 10 | }) 11 | 12 | const schema = {const: 42} 13 | const validate = ajv.compile(schema) 14 | 15 | validate(42).should.equal(true) 16 | validate(43).should.equal(false) 17 | 18 | ajv.validate("str", "foo").should.equal(true) 19 | ajv.validate("str", 42).should.equal(false) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /spec/issues/259_validate_meta_against_itself.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #259, support validating [meta-]schemas against themselves", () => { 6 | it('should add schema before validation if "id" is the same as "$schema"', () => { 7 | const ajv = new _Ajv({strict: false}) 8 | const hyperSchema = require("../remotes/hyper-schema.json") 9 | ajv.addMetaSchema(hyperSchema) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /spec/issues/273_error_schemaPath_refd_schema.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe.skip("issue #273, schemaPath in error in referenced schema", () => { 6 | it("should have canonic reference with hash after file name", () => { 7 | test(new _Ajv()) 8 | test(new _Ajv({inlineRefs: false})) 9 | 10 | function test(ajv) { 11 | const schema = { 12 | properties: { 13 | a: {$ref: "int"}, 14 | }, 15 | } 16 | 17 | const referencedSchema = { 18 | id: "int", 19 | type: "integer", 20 | } 21 | 22 | ajv.addSchema(referencedSchema) 23 | const validate = ajv.compile(schema) 24 | 25 | validate({a: "foo"}).should.equal(false) 26 | validate.errors[0].schemaPath.should.equal("int#/type") 27 | } 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /spec/issues/342_uniqueItems_non-json_objects.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #342, support uniqueItems with some non-JSON objects", () => { 6 | let validate 7 | 8 | before(() => { 9 | const ajv = new _Ajv() 10 | validate = ajv.compile({type: "array", uniqueItems: true}) 11 | }) 12 | 13 | it("should allow different RegExps", () => { 14 | validate([/foo/, /bar/]).should.equal(true) 15 | validate([/foo/gi, /foo/gi]).should.equal(false) 16 | validate([/foo/, {}]).should.equal(true) 17 | }) 18 | 19 | it("should allow different Dates", () => { 20 | validate([new Date("2016-11-11"), new Date("2016-11-12")]).should.equal(true) 21 | validate([new Date("2016-11-11"), new Date("2016-11-11")]).should.equal(false) 22 | validate([new Date("2016-11-11"), {}]).should.equal(true) 23 | }) 24 | 25 | it("should allow undefined properties", () => { 26 | validate([{}, {foo: undefined}]).should.equal(true) 27 | validate([{foo: undefined}, {}]).should.equal(true) 28 | validate([{foo: undefined}, {bar: undefined}]).should.equal(true) 29 | validate([{foo: undefined}, {foo: undefined}]).should.equal(false) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /spec/issues/485_type_validation_priority.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #485, order of type validation", () => { 6 | it("should validate types before keywords", () => { 7 | const ajv = new _Ajv({allErrors: true, strictTypes: false}) 8 | const validate: any = ajv.compile({ 9 | type: ["integer", "string"], 10 | required: ["foo"], 11 | minimum: 2, 12 | }) 13 | 14 | validate(2).should.equal(true) 15 | validate("foo").should.equal(true) 16 | 17 | validate(1.5).should.equal(false) 18 | checkErrors(["type", "minimum"]) 19 | 20 | validate({}).should.equal(false) 21 | checkErrors(["type", "required"]) 22 | 23 | function checkErrors(expectedErrs) { 24 | validate.errors.should.have.length(expectedErrs.length) 25 | expectedErrs.forEach((keyword, i) => validate.errors[i].keyword.should.equal(keyword)) 26 | } 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /spec/issues/521_wrong_warning_id_property.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe('issue #521, incorrect warning with "id" property', () => { 6 | it("should not log warning", () => { 7 | const ajv = new _Ajv() 8 | const consoleWarn = console.warn 9 | console.warn = () => { 10 | throw new Error("should not log warning") 11 | } 12 | 13 | try { 14 | ajv.compile({ 15 | $id: "http://example.com/schema.json", 16 | type: "object", 17 | properties: { 18 | id: {type: "string"}, 19 | }, 20 | required: ["id"], 21 | }) 22 | } finally { 23 | console.warn = consoleWarn 24 | } 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /spec/issues/743_removeAdditional_to_remove_proto.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #743, property __proto__ should be removed with removeAdditional option", () => { 6 | it("should remove additional properties", () => { 7 | const ajv = new _Ajv({removeAdditional: true}) 8 | 9 | const schema = { 10 | type: "object", 11 | properties: { 12 | obj: { 13 | type: "object", 14 | additionalProperties: false, 15 | properties: { 16 | a: {type: "string"}, 17 | b: {type: "string"}, 18 | c: {type: "string"}, 19 | d: {type: "string"}, 20 | e: {type: "string"}, 21 | f: {type: "string"}, 22 | g: {type: "string"}, 23 | h: {type: "string"}, 24 | i: {type: "string"}, 25 | }, 26 | }, 27 | }, 28 | } 29 | 30 | const obj = Object.create(null) 31 | obj.__proto__ = null // should be removed 32 | obj.additional = "will be removed" 33 | obj.a = "valid" 34 | obj.b = "valid" 35 | 36 | const data = {obj: obj} 37 | 38 | ajv.validate(schema, data).should.equal(true) 39 | Object.keys(data.obj).should.eql(["a", "b"]) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /spec/issues/815_id_updates_ref_base.spec.ts: -------------------------------------------------------------------------------- 1 | import type {ValidateFunction} from "../.." 2 | import _Ajv from "../ajv" 3 | import chai from "../chai" 4 | chai.should() 5 | 6 | describe("issue #815, id and $id fields should reset base", () => { 7 | let validate: ValidateFunction 8 | 9 | const schema = { 10 | type: "object", 11 | properties: { 12 | newRoot: { 13 | $id: "http://example.com/newRoot", 14 | type: "object", 15 | properties: { 16 | recurse: { 17 | $ref: "#", 18 | }, 19 | name: { 20 | type: "string", 21 | }, 22 | }, 23 | required: ["name"], 24 | additionalProperties: false, 25 | }, 26 | }, 27 | required: ["newRoot"], 28 | additionalProperties: false, 29 | } 30 | 31 | before(() => { 32 | validate = new _Ajv().compile(schema) 33 | }) 34 | 35 | it("should set # to reference the closest ancestor with $id", () => { 36 | validate({ 37 | newRoot: { 38 | name: "test", 39 | }, 40 | }).should.equal(true) 41 | 42 | validate({ 43 | newRoot: { 44 | name: "test", 45 | recurse: { 46 | name: "test2", 47 | }, 48 | }, 49 | }).should.equal(true) 50 | }) 51 | 52 | it("should NOT set # to reference the absolute document root", () => { 53 | validate({ 54 | newRoot: { 55 | name: "test", 56 | recurse: { 57 | newRoot: { 58 | name: "test2", 59 | }, 60 | }, 61 | }, 62 | }).should.equal(false) 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /spec/issues/8_shared_refs.spec.ts: -------------------------------------------------------------------------------- 1 | import type AjvCore from "../../dist/core" 2 | import type AjvPack from "../../dist/standalone/instance" 3 | import _Ajv from "../ajv" 4 | import {getStandalone} from "../ajv_standalone" 5 | import chai from "../chai" 6 | chai.should() 7 | 8 | describe("issue #8: schema with shared references", () => { 9 | const propertySchema = { 10 | type: "string", 11 | maxLength: 4, 12 | } 13 | 14 | const schema = { 15 | $id: "obj.json#", 16 | type: "object", 17 | properties: { 18 | foo: propertySchema, 19 | bar: propertySchema, 20 | }, 21 | } 22 | 23 | it("should be supported by addSchema", () => { 24 | spec(new _Ajv().addSchema(schema)) 25 | }) 26 | 27 | it("should be supported by compile", () => { 28 | const ajv = new _Ajv() 29 | ajv.compile(schema) 30 | spec(ajv) 31 | }) 32 | 33 | it("should be supported by addSchema: standalone", () => { 34 | spec(getStandalone(_Ajv).addSchema(schema)) 35 | }) 36 | 37 | it("should be supported by compile: standalone", () => { 38 | const ajv = getStandalone(_Ajv) 39 | ajv.compile(schema) 40 | spec(ajv) 41 | }) 42 | 43 | function spec(ajv: AjvCore | AjvPack): void { 44 | let result = ajv.validate("obj.json#", {foo: "abc", bar: "def"}) 45 | result.should.equal(true) 46 | 47 | result = ajv.validate("obj.json#", {foo: "abcde", bar: "fghg"}) 48 | result.should.equal(false) 49 | ajv.errors?.should.have.length(1) 50 | } 51 | }) 52 | -------------------------------------------------------------------------------- /spec/issues/955_removeAdditional_custom_keywords.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "../ajv" 2 | import chai from "../chai" 3 | chai.should() 4 | 5 | describe("issue #955: option removeAdditional breaks user-defined keywords", () => { 6 | it("should support user-defined keywords with option removeAdditional", () => { 7 | const ajv = new _Ajv({removeAdditional: "all"}) 8 | 9 | ajv.addKeyword({ 10 | keyword: "minTrimmedLength", 11 | type: "string", 12 | compile: function (schema: number) { 13 | return function (str: string): boolean { 14 | return str.trim().length >= schema 15 | } 16 | }, 17 | metaSchema: {type: "integer"}, 18 | }) 19 | 20 | const schema = { 21 | type: "object", 22 | properties: { 23 | foo: { 24 | type: "string", 25 | minTrimmedLength: 3, 26 | }, 27 | }, 28 | required: ["foo"], 29 | } 30 | 31 | const validate = ajv.compile(schema) 32 | 33 | let data = { 34 | foo: " bar ", 35 | baz: "", 36 | } 37 | validate(data).should.equal(true) 38 | data.should.not.have.property("baz") 39 | 40 | data = { 41 | foo: " ba ", 42 | baz: "", 43 | } 44 | validate(data).should.equal(false) 45 | data.should.not.have.property("baz") 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /spec/issues/re2.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | {name: "$data/format", test: require("../extras/$data/format.json")}, 3 | {name: "$data/pattern", test: require("../extras/$data/pattern.json")}, 4 | ] 5 | -------------------------------------------------------------------------------- /spec/javacript.spec.js: -------------------------------------------------------------------------------- 1 | const Ajv = require("./ajv") 2 | const Ajv2019 = require("./ajv2019") 3 | const assert = require("assert") 4 | 5 | describe("using Ajv with javascript", () => { 6 | describe("draft-07", () => it("should validate", () => test(Ajv))) 7 | describe("draft-2019-09", () => it("should validate", () => test(Ajv2019))) 8 | 9 | function test(_Ajv) { 10 | const ajv = new _Ajv() 11 | const validate = ajv.compile({type: "number"}) 12 | assert.strictEqual(validate(1), true) 13 | assert.strictEqual(validate("1"), false) 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /spec/options/schemaId.spec.ts: -------------------------------------------------------------------------------- 1 | import type Ajv from "../.." 2 | import _Ajv from "../ajv" 3 | import assert = require("assert") 4 | import chai from "../chai" 5 | const should = chai.should() 6 | 7 | describe("removed schemaId option", () => { 8 | it("should use $id and throw exception when id is used", () => { 9 | test(new _Ajv({logger: false})) 10 | 11 | function test(ajv) { 12 | ajv.addSchema({$id: "mySchema1", type: "string"}) 13 | const validate = ajv.getSchema("mySchema1") 14 | validate("foo").should.equal(true) 15 | validate(1).should.equal(false) 16 | 17 | should.throw( 18 | () => ajv.compile({id: "mySchema2", type: "string"}), 19 | /NOT SUPPORTED: keyword "id"/ 20 | ) 21 | } 22 | }) 23 | 24 | it("should use $id and throw exception for id when strict: false", () => { 25 | test(new _Ajv({logger: false, strict: false})) 26 | 27 | function test(ajv: Ajv) { 28 | ajv.addSchema({$id: "mySchema1", type: "string"}) 29 | const validate = ajv.getSchema("mySchema1") 30 | assert(typeof validate == "function") 31 | validate("foo").should.equal(true) 32 | validate(1).should.equal(false) 33 | 34 | should.throw( 35 | () => ajv.compile({id: "mySchema2", type: "string"}), 36 | /NOT SUPPORTED: keyword "id"/ 37 | ) 38 | should.not.exist(ajv.getSchema("mySchema2")) 39 | } 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /spec/remotes/bar.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/bar.json", 3 | "type": "string" 4 | } 5 | -------------------------------------------------------------------------------- /spec/remotes/buu.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/buu.json", 3 | "definitions": { 4 | "buu": { 5 | "type": "object", 6 | "properties": { 7 | "bar": {"$ref": "bar.json"} 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spec/remotes/first.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/first.json", 3 | "type": "string" 4 | } 5 | -------------------------------------------------------------------------------- /spec/remotes/foo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/foo.json", 3 | "type": "object", 4 | "properties": { 5 | "bar": {"$ref": "bar.json"} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/remotes/name.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "orNull": { 4 | "anyOf": [{"type": "null"}, {"$ref": "#"}] 5 | } 6 | }, 7 | "type": "string" 8 | } 9 | -------------------------------------------------------------------------------- /spec/remotes/node.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/node.json", 3 | "description": "node", 4 | "type": "object", 5 | "properties": { 6 | "value": {"type": "number"}, 7 | "subtree": {"$ref": "tree.json"} 8 | }, 9 | "required": ["value"] 10 | } 11 | -------------------------------------------------------------------------------- /spec/remotes/scope_change.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/scope_change.json", 3 | "definitions": { 4 | "foo": { 5 | "$id": "http://localhost:1234/scope_foo.json", 6 | "definitions": { 7 | "bar": { 8 | "type": "string" 9 | } 10 | } 11 | }, 12 | "baz": { 13 | "$id": "folder/", 14 | "type": "array", 15 | "items": {"$ref": "folderInteger.json"}, 16 | "bar": { 17 | "items": {"$ref": "folderInteger.json"} 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec/remotes/second.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/second.json", 3 | "type": "object", 4 | "properties": { 5 | "first": {"$ref": "first.json"} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/remotes/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "http://localhost:1234/tree.json", 3 | "description": "tree of nodes", 4 | "type": "object", 5 | "properties": { 6 | "meta": {"type": "string"}, 7 | "nodes": { 8 | "type": "array", 9 | "items": {"$ref": "node.json"} 10 | } 11 | }, 12 | "required": ["meta", "nodes"] 13 | } 14 | -------------------------------------------------------------------------------- /spec/security.spec.ts: -------------------------------------------------------------------------------- 1 | import _Ajv from "./ajv" 2 | import getAjvInstances from "./ajv_instances" 3 | import {withStandalone} from "./ajv_standalone" 4 | import jsonSchemaTest = require("json-schema-test") 5 | import options from "./ajv_options" 6 | import {afterError, afterEach} from "./after_test" 7 | import chai from "./chai" 8 | 9 | const instances = getAjvInstances(_Ajv, options, { 10 | schemas: [require("../dist/refs/json-schema-secure.json")], 11 | strictTypes: false, 12 | }) 13 | 14 | instances.forEach((ajv) => (ajv.opts.code.source = true)) 15 | 16 | jsonSchemaTest(withStandalone(instances), { 17 | description: 18 | "Secure schemas tests of " + instances.length + " ajv instances with different options", 19 | suites: {security: require("./_json/security")}, 20 | assert: chai.assert, 21 | afterError, 22 | afterEach, 23 | cwd: __dirname, 24 | hideFolder: "security/", 25 | }) 26 | -------------------------------------------------------------------------------- /spec/security/object.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "patternProperties keyword should be used together with propertyNames", 4 | "schema": { 5 | "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "patternProperties keyword used without propertyNames is invalid", 10 | "data": { 11 | "patternProperties": { 12 | ".*": {} 13 | } 14 | }, 15 | "valid": false 16 | }, 17 | { 18 | "description": "patternProperties keyword used with propertyNames is valid", 19 | "data": { 20 | "patternProperties": { 21 | ".*": {} 22 | }, 23 | "propertyNames": { 24 | "maxLength": "256" 25 | } 26 | }, 27 | "valid": true 28 | } 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /spec/security/string.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "pattern keyword should be used together with maxLength", 4 | "schema": { 5 | "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "pattern keyword used without maxLength is invalid", 10 | "data": { 11 | "pattern": ".*" 12 | }, 13 | "valid": false 14 | }, 15 | { 16 | "description": "pattern keyword used with maxLength is valid", 17 | "data": { 18 | "pattern": ".*", 19 | "maxLength": "256" 20 | }, 21 | "valid": true 22 | } 23 | ] 24 | }, 25 | { 26 | "description": "format keyword should be used together with maxLength", 27 | "schema": { 28 | "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" 29 | }, 30 | "tests": [ 31 | { 32 | "description": "format keyword used without maxLength is invalid", 33 | "data": { 34 | "format": "email" 35 | }, 36 | "valid": false 37 | }, 38 | { 39 | "description": "format keyword used with maxLength is valid", 40 | "data": { 41 | "format": "email", 42 | "maxLength": "256" 43 | }, 44 | "valid": true 45 | } 46 | ] 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /spec/tests/issues/12_restoring_root_after_resolve.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "restoring root after ref resolution (#12)", 4 | "schema": { 5 | "definitions": { 6 | "int": {"$ref": "http://localhost:1234/integer.json"}, 7 | "str": {"type": "string"} 8 | }, 9 | "anyOf": [{"$ref": "#/definitions/int"}, {"$ref": "#/definitions/str"}] 10 | }, 11 | "tests": [ 12 | { 13 | "description": "valid string", 14 | "data": "foo", 15 | "valid": true 16 | }, 17 | { 18 | "description": "valid number", 19 | "data": 1, 20 | "valid": true 21 | }, 22 | { 23 | "description": "invalid object", 24 | "data": {}, 25 | "valid": false 26 | } 27 | ] 28 | }, 29 | { 30 | "description": "all refs are in the same place", 31 | "schema": { 32 | "definitions": { 33 | "int": {"type": "integer"}, 34 | "str": {"type": "string"} 35 | }, 36 | "anyOf": [{"$ref": "#/definitions/int"}, {"$ref": "#/definitions/str"}] 37 | }, 38 | "tests": [ 39 | { 40 | "description": "valid string", 41 | "data": "foo", 42 | "valid": true 43 | }, 44 | { 45 | "description": "valid number", 46 | "data": 1, 47 | "valid": true 48 | }, 49 | { 50 | "description": "invalid object", 51 | "data": {}, 52 | "valid": false 53 | } 54 | ] 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "root ref in remote ref (#13)", 4 | "schema": { 5 | "$id": "http://localhost:1234/issue13", 6 | "type": "object", 7 | "properties": { 8 | "name": {"$ref": "name.json#/definitions/orNull"} 9 | } 10 | }, 11 | "tests": [ 12 | { 13 | "description": "string is valid", 14 | "data": { 15 | "name": "foo" 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "null is valid", 21 | "data": { 22 | "name": null 23 | }, 24 | "valid": true 25 | }, 26 | { 27 | "description": "object is invalid", 28 | "data": { 29 | "name": { 30 | "name": null 31 | } 32 | }, 33 | "valid": false 34 | } 35 | ] 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /spec/tests/issues/14_ref_in_remote_ref_with_id.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "ref in remote ref with ids", 4 | "schema": { 5 | "$id": "http://localhost:1234/issue14a.json", 6 | "type": "array", 7 | "items": {"$ref": "foo.json"} 8 | }, 9 | "tests": [ 10 | { 11 | "description": "string is valid", 12 | "data": [ 13 | { 14 | "bar": "any string" 15 | } 16 | ], 17 | "valid": true 18 | }, 19 | { 20 | "description": "not string is invalid", 21 | "data": [ 22 | { 23 | "bar": 1 24 | } 25 | ], 26 | "valid": false 27 | } 28 | ] 29 | }, 30 | { 31 | "description": "remote ref in definitions in remote ref with ids (#14)", 32 | "schema": { 33 | "$id": "http://localhost:1234/issue14b.json", 34 | "type": "array", 35 | "items": {"$ref": "buu.json#/definitions/buu"} 36 | }, 37 | "tests": [ 38 | { 39 | "description": "string is valid", 40 | "data": [ 41 | { 42 | "bar": "any string" 43 | } 44 | ], 45 | "valid": true 46 | }, 47 | { 48 | "description": "not string is invalid", 49 | "data": [ 50 | { 51 | "bar": 1 52 | } 53 | ], 54 | "valid": false 55 | } 56 | ] 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /spec/tests/issues/17_escaping_pattern_property.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "escaping pattern property (#17)", 4 | "schema": { 5 | "type": "object", 6 | "patternProperties": { 7 | "^.+$": { 8 | "type": "object", 9 | "required": ["unit"] 10 | } 11 | }, 12 | "additionalProperties": false 13 | }, 14 | "tests": [ 15 | { 16 | "description": "empty object", 17 | "data": {}, 18 | "valid": true 19 | } 20 | ] 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /spec/tests/issues/28_escaping_pattern_error.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "escaping pattern error (#28)", 4 | "schema": { 5 | "type": "object", 6 | "properties": { 7 | "mediaType": { 8 | "type": "string", 9 | "pattern": "^[a-zA-Z0-9!#$%^&*_\\-+{}|'.`~]+/[a-zA-Z0-9!#$%^&*_\\-+{}|'.`~]+$" 10 | } 11 | } 12 | }, 13 | "tests": [ 14 | { 15 | "description": "empty object", 16 | "data": {}, 17 | "valid": true 18 | } 19 | ] 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /spec/tests/issues/311_quotes_in_refs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "quotes in refs (#311)", 4 | "schema": { 5 | "properties": { 6 | "foo\"bar": {"$ref": "#/definitions/foo\"bar"} 7 | }, 8 | "definitions": { 9 | "foo\"bar": {"type": "number"} 10 | } 11 | }, 12 | "tests": [ 13 | { 14 | "description": "object with all numbers is valid", 15 | "data": { 16 | "foo\"bar": 1, 17 | "foo\\bar": 1, 18 | "foo\nbar": 1, 19 | "foo\rbar": 1, 20 | "foo\tbar": 1, 21 | "foo\fbar": 1 22 | }, 23 | "valid": true 24 | }, 25 | { 26 | "description": "object with strings is invalid", 27 | "data": { 28 | "foo\"bar": "1", 29 | "foo\\bar": "1", 30 | "foo\nbar": "1", 31 | "foo\rbar": "1", 32 | "foo\tbar": "1", 33 | "foo\fbar": "1" 34 | }, 35 | "valid": false 36 | } 37 | ] 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /spec/tests/issues/33_json_schema_latest.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "use latest json schema as v4 (#33)", 4 | "schema": { 5 | "$schema": "http://json-schema.org/schema", 6 | "type": "object", 7 | "properties": { 8 | "username": { 9 | "type": "string" 10 | } 11 | } 12 | }, 13 | "tests": [ 14 | { 15 | "description": "empty object", 16 | "data": {}, 17 | "valid": true 18 | } 19 | ] 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /spec/tests/issues/413_dependencies_with_quote.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "JSON with control characters - 'dependencies'", 4 | "schema": { 5 | "dependencies": { 6 | "foo'bar": { 7 | "not": {"required": ["bar"]} 8 | } 9 | } 10 | }, 11 | "tests": [ 12 | { 13 | "description": "valid object", 14 | "data": { 15 | "foo'bar": 1 16 | }, 17 | "valid": true 18 | }, 19 | { 20 | "description": "invalid object", 21 | "data": { 22 | "foo'bar": 1, 23 | "bar": 2 24 | }, 25 | "valid": false 26 | } 27 | ] 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /spec/tests/issues/490_integer_validation.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "integer validation (#490)", 4 | "schema": { 5 | "type": "integer", 6 | "minimum": 0 7 | }, 8 | "tests": [ 9 | { 10 | "description": "valid integer", 11 | "data": 1, 12 | "valid": true 13 | }, 14 | { 15 | "description": "invalid integer", 16 | "data": -1, 17 | "valid": false 18 | }, 19 | { 20 | "description": "non-integer number is invalid", 21 | "data": 1.1, 22 | "valid": false 23 | }, 24 | { 25 | "description": "string is invalid", 26 | "data": "foo", 27 | "valid": false 28 | } 29 | ] 30 | } 31 | ] 32 | -------------------------------------------------------------------------------- /spec/tests/issues/502_contains_empty_array_with_ref_in_another_property.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "\"contains\" allows empty array when ref is used in sibling property (#502)", 4 | "schema": { 5 | "type": "object", 6 | "properties": { 7 | "str": {"$ref": "#/definitions/str"}, 8 | "arr": { 9 | "type": "array", 10 | "contains": {"type": "number"} 11 | } 12 | }, 13 | "definitions": { 14 | "str": {"type": "string"} 15 | } 16 | }, 17 | "tests": [ 18 | { 19 | "description": "valid object 1", 20 | "data": { 21 | "str": "a", 22 | "arr": [1] 23 | }, 24 | "valid": true 25 | }, 26 | { 27 | "description": "valid object 2", 28 | "data": { 29 | "arr": [1] 30 | }, 31 | "valid": true 32 | }, 33 | { 34 | "description": "invalid object 1", 35 | "data": { 36 | "str": "a", 37 | "arr": ["b"] 38 | }, 39 | "valid": false 40 | }, 41 | { 42 | "description": "invalid object 2", 43 | "data": { 44 | "arr": ["b"] 45 | }, 46 | "valid": false 47 | }, 48 | { 49 | "description": "invalid object 3", 50 | "data": { 51 | "arr": [] 52 | }, 53 | "valid": false 54 | }, 55 | { 56 | "description": "invalid object 4 (fails in #502)", 57 | "data": { 58 | "str": "a", 59 | "arr": [] 60 | }, 61 | "valid": false 62 | } 63 | ] 64 | } 65 | ] 66 | -------------------------------------------------------------------------------- /spec/tests/issues/5_adding_dependency_after.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Adding dependency after dependent schema (#5)", 4 | "schema": "http://localhost:1234/second.json", 5 | "tests": [ 6 | { 7 | "description": "valid object", 8 | "data": {"first": "foo"}, 9 | "valid": true 10 | }, 11 | { 12 | "description": "valid object", 13 | "data": {"first": 1}, 14 | "valid": false 15 | } 16 | ] 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /spec/tests/issues/5_recursive_references.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Recursive references between schemas (#5)", 4 | "schema": "http://localhost:1234/tree.json", 5 | "tests": [ 6 | { 7 | "description": "valid tree", 8 | "data": { 9 | "meta": "root", 10 | "nodes": [ 11 | { 12 | "value": 1, 13 | "subtree": { 14 | "meta": "child", 15 | "nodes": [{"value": 1.1}, {"value": 1.2}] 16 | } 17 | }, 18 | { 19 | "value": 2, 20 | "subtree": { 21 | "meta": "child", 22 | "nodes": [{"value": 2.1}, {"value": 2.2}] 23 | } 24 | } 25 | ] 26 | }, 27 | "valid": true 28 | }, 29 | { 30 | "description": "invalid tree", 31 | "data": { 32 | "meta": "root", 33 | "nodes": [ 34 | { 35 | "value": 1, 36 | "subtree": { 37 | "meta": "child", 38 | "nodes": [{"value": "string is invalid"}, {"value": 1.2}] 39 | } 40 | }, 41 | { 42 | "value": 2, 43 | "subtree": { 44 | "meta": "child", 45 | "nodes": [{"value": 2.1}, {"value": 2.2}] 46 | } 47 | } 48 | ] 49 | }, 50 | "valid": false 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /spec/tests/issues/63_id_property_not_in_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "id property in referenced schema in object that is not a schema (#63)", 4 | "schema": { 5 | "type": "object", 6 | "properties": { 7 | "title": { 8 | "$ref": "http://json-schema.org/draft-07/schema#/properties/title" 9 | } 10 | } 11 | }, 12 | "tests": [ 13 | { 14 | "description": "empty object is valid", 15 | "data": {}, 16 | "valid": true 17 | }, 18 | { 19 | "description": "string is valid", 20 | "data": {"title": "foo"}, 21 | "valid": true 22 | }, 23 | { 24 | "description": "number is invalid", 25 | "data": {"title": 1}, 26 | "valid": false 27 | } 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /spec/tests/issues/861_empty_propertynames.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "propertyNames with empty schema (#861)", 4 | "schema": { 5 | "properties": { 6 | "foo": {"type": "string"} 7 | }, 8 | "propertyNames": {} 9 | }, 10 | "tests": [ 11 | { 12 | "description": "valid", 13 | "data": {"foo": "bar"}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "invalid", 18 | "data": {"foo": 1}, 19 | "valid": false 20 | } 21 | ] 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /spec/tests/issues/87_$_property.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "$ in properties (#87)", 4 | "schema": { 5 | "properties": { 6 | "$": {"type": "string"} 7 | } 8 | }, 9 | "tests": [ 10 | { 11 | "description": "valid", 12 | "data": {"$": "foo"}, 13 | "valid": true 14 | } 15 | ] 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /spec/tests/issues/94_dependencies_fail.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "second dependency is not checked (#94)", 4 | "schema": { 5 | "dependencies": { 6 | "bar": ["baz"], 7 | "foo": ["bar"] 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "object with only foo is invalid (bar is missing)", 13 | "data": {"foo": 1}, 14 | "valid": false 15 | }, 16 | { 17 | "description": "object with foo and bar is invalid (baz is missing)", 18 | "data": {"foo": 1, "bar": 2}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "object with foo, bar and baz is valid", 23 | "data": {"foo": 1, "bar": 2, "baz": 3}, 24 | "valid": true 25 | } 26 | ] 27 | }, 28 | { 29 | "description": "second dependency is checked when order is changed", 30 | "schema": { 31 | "dependencies": { 32 | "foo": ["bar"], 33 | "bar": ["baz"] 34 | } 35 | }, 36 | "tests": [ 37 | { 38 | "description": "object with only foo is invalid (bar is missing)", 39 | "data": {"foo": 1}, 40 | "valid": false 41 | }, 42 | { 43 | "description": "object with foo and bar is invalid (baz is missing)", 44 | "data": {"foo": 1, "bar": 2}, 45 | "valid": false 46 | }, 47 | { 48 | "description": "object with foo, bar and baz is valid", 49 | "data": {"foo": 1, "bar": 2, "baz": 3}, 50 | "valid": true 51 | } 52 | ] 53 | } 54 | ] 55 | -------------------------------------------------------------------------------- /spec/tests/rules/allOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "allOf with one empty schema", 4 | "schema": { 5 | "allOf": [{}] 6 | }, 7 | "tests": [ 8 | { 9 | "description": "any data is valid", 10 | "data": 1, 11 | "valid": true 12 | } 13 | ] 14 | }, 15 | { 16 | "description": "allOf with two empty schemas", 17 | "schema": { 18 | "allOf": [{}, {}] 19 | }, 20 | "tests": [ 21 | { 22 | "description": "any data is valid", 23 | "data": 1, 24 | "valid": true 25 | } 26 | ] 27 | }, 28 | { 29 | "description": "allOf with two schemas, the first is empty", 30 | "schema": { 31 | "allOf": [{}, {"type": "number"}] 32 | }, 33 | "tests": [ 34 | { 35 | "description": "number is valid", 36 | "data": 1, 37 | "valid": true 38 | }, 39 | { 40 | "description": "string is invalid", 41 | "data": "foo", 42 | "valid": false 43 | } 44 | ] 45 | }, 46 | { 47 | "description": "allOf with two schemas, the second is empty", 48 | "schema": { 49 | "allOf": [{"type": "number"}, {}] 50 | }, 51 | "tests": [ 52 | { 53 | "description": "number is valid", 54 | "data": 1, 55 | "valid": true 56 | }, 57 | { 58 | "description": "string is invalid", 59 | "data": "foo", 60 | "valid": false 61 | } 62 | ] 63 | } 64 | ] 65 | -------------------------------------------------------------------------------- /spec/tests/rules/anyOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "anyOf with one of schemas empty", 4 | "schema": { 5 | "anyOf": [{"type": "number"}, {}] 6 | }, 7 | "tests": [ 8 | { 9 | "description": "string is valid", 10 | "data": "foo", 11 | "valid": true 12 | }, 13 | { 14 | "description": "number is valid", 15 | "data": 123, 16 | "valid": true 17 | } 18 | ] 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /spec/tests/rules/comment.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "$comment keyword", 4 | "schema": { 5 | "$comment": "test" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "any value is valid", 10 | "data": 1, 11 | "valid": true 12 | } 13 | ] 14 | }, 15 | { 16 | "description": "$comment keyword in subschemas", 17 | "schema": { 18 | "type": "object", 19 | "properties": { 20 | "foo": { 21 | "$comment": "test" 22 | } 23 | } 24 | }, 25 | "tests": [ 26 | { 27 | "description": "empty object is valid", 28 | "data": {}, 29 | "valid": true 30 | }, 31 | { 32 | "description": "any value of property foo is valid object is valid", 33 | "data": { 34 | "foo": 1 35 | }, 36 | "valid": true 37 | } 38 | ] 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /spec/tests/rules/dependencies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "dependencies keyword with empty array", 4 | "schema": { 5 | "dependencies": { 6 | "foo": [] 7 | } 8 | }, 9 | "tests": [ 10 | { 11 | "description": "object with property is valid", 12 | "data": {"foo": 1}, 13 | "valid": true 14 | }, 15 | { 16 | "description": "empty object is valid", 17 | "data": {}, 18 | "valid": true 19 | }, 20 | { 21 | "description": "non-object is valid", 22 | "data": 1, 23 | "valid": true 24 | } 25 | ] 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /spec/tests/rules/format.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "allowed unknown format is valid", 4 | "schema": { 5 | "format": "allowedUnknown" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "any string is valid", 10 | "data": "any value", 11 | "valid": true 12 | } 13 | ] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /spec/tests/rules/required.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "required keyword with empty array", 4 | "schema": { 5 | "required": [] 6 | }, 7 | "tests": [ 8 | { 9 | "description": "object with property is valid", 10 | "data": {"foo": 1}, 11 | "valid": true 12 | }, 13 | { 14 | "description": "empty object is valid", 15 | "data": {}, 16 | "valid": true 17 | }, 18 | { 19 | "description": "non-object is valid", 20 | "data": 1, 21 | "valid": true 22 | } 23 | ] 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /spec/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "..", 3 | "include": ["."], 4 | "compilerOptions": { 5 | "types": ["node", "mocha"], 6 | "noImplicitAny": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /spec/types/error-parameters.spec.ts: -------------------------------------------------------------------------------- 1 | import {DefinedError} from "../.." 2 | import _Ajv from "../ajv" 3 | import chai from "../chai" 4 | const should = chai.should() 5 | 6 | describe("error object parameters type", () => { 7 | const ajv = new _Ajv({allErrors: true}) 8 | 9 | it("should be determined by the keyword", () => { 10 | const validate = ajv.compile({type: "number", minimum: 0, multipleOf: 2}) 11 | const valid = validate(-1) 12 | valid.should.equal(false) 13 | const errs = validate.errors 14 | if (errs) { 15 | errs.length.should.equal(2) 16 | for (const err of errs as DefinedError[]) { 17 | switch (err.keyword) { 18 | case "minimum": 19 | err.params.limit.should.equal(0) 20 | err.params.comparison.should.equal(">=") 21 | break 22 | case "multipleOf": 23 | err.params.multipleOf.should.equal(2) 24 | break 25 | default: 26 | should.fail() 27 | } 28 | } 29 | } 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ajv-validator/config", 3 | "include": ["lib"], 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "lib": ["ES2018", "DOM"], 7 | "types": ["node"], 8 | "allowJs": true, 9 | "target": "ES2018", 10 | "resolveJsonModule": true 11 | } 12 | } 13 | --------------------------------------------------------------------------------
17 | 18 | All news 19 |
13 | Read more 14 |
{{ getMsg() }}