Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ajv-validator/ajv
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 8c4d3294226dcfc2ced9b5cbdcb3befca2f97782
Choose a base ref
...
head repository: ajv-validator/ajv
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 521c3a53f15f5502fb4a734194932535d311267c
Choose a head ref

Commits on Jul 10, 2017

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6a2225b View commit details
  2. 5.2.2

    epoberezkin committed Jul 10, 2017
    Copy the full SHA
    97a3185 View commit details

Commits on Jul 17, 2017

  1. Copy the full SHA
    1452f30 View commit details

Commits on Aug 6, 2017

  1. fix: use uri-js to resovle uri's

    Sondre Lefsaker committed Aug 6, 2017
    Copy the full SHA
    cf8f9d3 View commit details

Commits on Aug 15, 2017

  1. Copy the full SHA
    4f79ef0 View commit details

Commits on Sep 14, 2017

  1. Update KEYWORDS.md

    Fix of unclosed markdown character `
    dzuremar authored Sep 14, 2017
    Copy the full SHA
    40f614d View commit details
  2. Merge pull request #562 from dzuremar/patch-1

    Update KEYWORDS.md
    Evgeny Poberezkin authored Sep 14, 2017
    Copy the full SHA
    324cf30 View commit details

Commits on Sep 18, 2017

  1. Copy the full SHA
    f173fdb View commit details
  2. Merge pull request #564 from epoberezkin/greenkeeper/js-beautify-pin-…

    …1.6.14
    
    chore: pin js-beautify to 1.6.14
    Evgeny Poberezkin authored Sep 18, 2017
    Copy the full SHA
    54076b3 View commit details
  3. Copy the full SHA
    4ee5790 View commit details
  4. Merge pull request #566 from epoberezkin/greenkeeper/js-beautify-1.7.3

    chore(package): update js-beautify to version 1.7.3
    Evgeny Poberezkin authored Sep 18, 2017
    Copy the full SHA
    9a13b28 View commit details

Commits on Sep 21, 2017

  1. Merge branch 'master' into greenkeeper/regenerator-0.10.0

    Evgeny Poberezkin authored Sep 21, 2017
    Copy the full SHA
    afd4877 View commit details

Commits on Sep 23, 2017

  1. Superfluous bracket in README.md

    Deleted superfluous bracket
    George Kobelev authored Sep 23, 2017
    Copy the full SHA
    08f0b59 View commit details

Commits on Sep 24, 2017

  1. Merge pull request #571 from F1NYA/patch-1

    Superfluous bracket in README.md
    Evgeny Poberezkin authored Sep 24, 2017
    Copy the full SHA
    27a009c View commit details

Commits on Sep 25, 2017

  1. Copy the full SHA
    2b0f141 View commit details
  2. 5.2.3

    epoberezkin committed Sep 25, 2017
    Copy the full SHA
    e98d031 View commit details

Commits on Sep 26, 2017

  1. Merge branch 'master' into greenkeeper/regenerator-0.10.0

    Evgeny Poberezkin authored Sep 26, 2017
    Copy the full SHA
    2482f6d View commit details

Commits on Sep 28, 2017

  1. Copy the full SHA
    37e86e6 View commit details

Commits on Sep 30, 2017

  1. Merge pull request #575 from epoberezkin/greenkeeper/coveralls-3.0.0

    Update coveralls to the latest version 🚀
    Evgeny Poberezkin authored Sep 30, 2017
    Copy the full SHA
    d0c703a View commit details
  2. Merge pull request #548 from epoberezkin/greenkeeper/regenerator-0.10.0

    Update regenerator to the latest version 🚀
    Evgeny Poberezkin authored Sep 30, 2017
    Copy the full SHA
    ba05238 View commit details

Commits on Oct 3, 2017

  1. Copy the full SHA
    43aee28 View commit details
  2. Copy the full SHA
    9b0b013 View commit details
  3. Merge pull request #577 from hans-kinnek/patch-1

    That typo was bothering me
    Evgeny Poberezkin authored Oct 3, 2017
    Copy the full SHA
    aeecc77 View commit details
  4. Merge pull request #576 from epoberezkin/greenkeeper/mocha-4.0.0

    Update mocha to the latest version 🚀
    Evgeny Poberezkin authored Oct 3, 2017
    Copy the full SHA
    c843d18 View commit details

Commits on Oct 14, 2017

  1. Copy the full SHA
    baa2bd7 View commit details

Commits on Oct 15, 2017

  1. Copy the full SHA
    3db9656 View commit details
  2. 5
    Copy the full SHA
    b6d35aa View commit details
  3. Copy the full SHA
    2838678 View commit details

Commits on Oct 16, 2017

  1. Copy the full SHA
    1ceefdb View commit details
  2. Merge pull request #595 from epoberezkin/greenkeeper/uglify-js-pin-3.1.3

    chore: pin uglify-js to 3.1.3
    Evgeny Poberezkin authored Oct 16, 2017
    Copy the full SHA
    a30289e View commit details

Commits on Oct 17, 2017

  1. Fix minor grammatical issues and typos in README.md

    - Camel-case minProperties keyword
    - Update supported Node.js versions to those currently tested by Travis (55727d9)
    - Add JavaScript syntax highlighting to two code blocks
    gj committed Oct 17, 2017
    Copy the full SHA
    cbff507 View commit details

Commits on Oct 22, 2017

  1. docs: readme.md corrections

    Evgeny Poberezkin authored Oct 22, 2017
    Copy the full SHA
    5225354 View commit details
  2. docs: readme.md

    Evgeny Poberezkin authored Oct 22, 2017
    Copy the full SHA
    40bef80 View commit details
  3. Merge pull request #598 from gj/fix-typos-in-readme

    Fix minor grammatical issues and typos in README.md
    Evgeny Poberezkin authored Oct 22, 2017
    Copy the full SHA
    8f27e54 View commit details
  4. Merge pull request #592 from epoberezkin/fix-types

    Fix type definitions
    Evgeny Poberezkin authored Oct 22, 2017
    Copy the full SHA
    03b8d67 View commit details
  5. chore: update uglify-js

    epoberezkin committed Oct 22, 2017
    Copy the full SHA
    d5eecbd View commit details
  6. 5.2.4

    epoberezkin committed Oct 22, 2017
    Copy the full SHA
    80470c3 View commit details

Commits on Oct 24, 2017

  1. fix typings from #592, fixes #603

    Delagen committed Oct 24, 2017
    Copy the full SHA
    afae68a View commit details
  2. Merge pull request #606 from Delagen/master

    fix typings from #592, fixes #603
    Evgeny Poberezkin authored Oct 24, 2017
    Copy the full SHA
    4e12e8f View commit details
  3. 5.2.5

    epoberezkin committed Oct 24, 2017
    Copy the full SHA
    991b4be View commit details
  4. Copy the full SHA
    75c9595 View commit details
  5. 5.3.0

    epoberezkin committed Oct 24, 2017
    Copy the full SHA
    256100c View commit details
  6. Copy the full SHA
    819d49f View commit details

Commits on Oct 26, 2017

  1. Copy the full SHA
    d20da89 View commit details
  2. feat: format json-pointer only means string now, added format json-po…

    …inter-uri-fragment, closes #589
    epoberezkin committed Oct 26, 2017
    Copy the full SHA
    e6aa9e0 View commit details

Commits on Oct 28, 2017

  1. Copy the full SHA
    05f7226 View commit details
  2. reserve keyword $id

    epoberezkin committed Oct 28, 2017
    Copy the full SHA
    5d2f2b8 View commit details
  3. Copy the full SHA
    a442241 View commit details
  4. Copy the full SHA
    9845928 View commit details
  5. Copy the full SHA
    b456176 View commit details
Showing with 6,600 additions and 3,951 deletions.
  1. +1 −1 .eslintrc.yml
  2. +3 −0 .github/FUNDING.yml
  3. +8 −7 .github/ISSUE_TEMPLATE.md
  4. +81 −0 .github/ISSUE_TEMPLATE/bug-or-error-report.md
  5. +24 −0 .github/ISSUE_TEMPLATE/change.md
  6. +28 −0 .github/ISSUE_TEMPLATE/compatibility.md
  7. +33 −0 .github/ISSUE_TEMPLATE/installation.md
  8. +42 −0 .github/ISSUE_TEMPLATE/typescript.md
  9. +1 −1 .github/PULL_REQUEST_TEMPLATE.md
  10. +32 −0 .github/config.yml
  11. +2 −0 .gitignore
  12. +1 −0 .npmrc
  13. +4 −4 .travis.yml
  14. +76 −0 CODE_OF_CONDUCT.md
  15. +1 −1 COERCION.md
  16. +45 −15 CONTRIBUTING.md
  17. +99 −54 CUSTOM.md
  18. +33 −10 FAQ.md
  19. +51 −105 KEYWORDS.md
  20. +1 −1 LICENSE
  21. +485 −253 README.md
  22. +1 −1 bower.json
  23. +8 −4 karma.conf.js
  24. +13 −37 karma.sauce.js
  25. +179 −79 lib/ajv.d.ts
  26. +67 −38 lib/ajv.js
  27. +0 −31 lib/compile/_rules.js
  28. +2 −0 lib/compile/equal.js
  29. +29 −22 lib/compile/formats.js
  30. +30 −16 lib/compile/index.js
  31. +10 −11 lib/compile/resolve.js
  32. +14 −6 lib/compile/rules.js
  33. +16 −44 lib/compile/util.js
  34. +1 −1 lib/{$data.js → data.js}
  35. +37 −0 lib/definition_schema.js
  36. +17 −0 lib/dot/_limit.jst
  37. +2 −0 lib/dot/_limitItems.jst
  38. +2 −0 lib/dot/_limitLength.jst
  39. +2 −0 lib/dot/_limitProperties.jst
  40. +0 −2 lib/dot/allOf.jst
  41. +0 −2 lib/dot/anyOf.jst
  42. +9 −0 lib/dot/comment.jst
  43. +0 −2 lib/dot/contains.jst
  44. +2 −2 lib/dot/custom.jst
  45. +21 −6 lib/dot/defaults.def
  46. +10 −7 lib/dot/definitions.def
  47. +1 −2 lib/dot/dependencies.jst
  48. +8 −8 lib/dot/errors.def
  49. +3 −3 lib/dot/format.jst
  50. +73 −0 lib/dot/if.jst
  51. +0 −2 lib/dot/items.jst
  52. +2 −0 lib/dot/multipleOf.jst
  53. +17 −7 lib/dot/oneOf.jst
  54. +7 −89 lib/dot/properties.jst
  55. +2 −4 lib/dot/propertyNames.jst
  56. +4 −4 lib/dot/ref.jst
  57. +33 −9 lib/dot/uniqueItems.jst
  58. +43 −39 lib/dot/validate.jst
  59. +33 −0 lib/dotjs/index.js
  60. +34 −18 lib/keyword.js
  61. +0 −36 lib/patternGroups.js
  62. +3 −3 lib/refs/{$data.json → data.json}
  63. +3 −4 lib/refs/json-schema-draft-04.json
  64. +4 −0 lib/refs/json-schema-draft-06.json
  65. +168 −0 lib/refs/json-schema-draft-07.json
  66. +94 −0 lib/refs/json-schema-secure.json
  67. +0 −250 lib/refs/json-schema-v5.json
  68. +49 −46 package.json
  69. +4 −1 scripts/prepare-tests
  70. +32 −0 scripts/publish-built-version
  71. +1 −1 scripts/travis-gh-pages
  72. +1 −0 spec/.eslintrc.yml
  73. +1 −1 spec/JSON-Schema-Test-Suite
  74. +3 −0 spec/ajv-async.js
  75. +100 −33 spec/ajv.spec.js
  76. +5 −69 spec/ajv_async_instances.js
  77. +60 −25 spec/async.spec.js
  78. +53 −68 spec/async_validate.spec.js
  79. +2 −0 spec/browser_test_suite.js
  80. +75 −3 spec/coercion.spec.js
  81. +103 −0 spec/custom.spec.js
  82. +191 −9 spec/errors.spec.js
  83. +0 −1 spec/extras.spec.js
  84. +0 −271 spec/extras/patternGroups.json
  85. +0 −602 spec/issues.spec.js
  86. +20 −0 spec/issues/1001_addKeyword_and_schema_without_id.spec.js
  87. +58 −0 spec/issues/181_allErrors_custom_keyword_skipped.spec.js
  88. +26 −0 spec/issues/182_nan_validation.spec.js
  89. +23 −0 spec/issues/204_options_schemas_data_together.spec.js
  90. +79 −0 spec/issues/210_mutual_recur_frags.spec.js
  91. +210 −0 spec/issues/240_mutual_recur_frags_common_ref.spec.js
  92. +13 −0 spec/issues/259_validate_meta_against_itself.spec.js
  93. +31 −0 spec/issues/273_error_schemaPath_refd_schema.spec.js
  94. +33 −0 spec/issues/342_uniqueItems_non-json_objects.spec.js
  95. +32 −0 spec/issues/485_type_validation_priority.spec.js
  96. +49 −0 spec/issues/50_refs_with_definitions.spec.js
  97. +28 −0 spec/issues/521_wrong_warning_id_property.spec.js
  98. +29 −0 spec/issues/533_missing_ref_error_when_ignore.spec.js
  99. +47 −0 spec/issues/617_full_format_leap_year.spec.js
  100. +41 −0 spec/issues/743_removeAdditional_to_remove_proto.spec.js
  101. +114 −0 spec/issues/768_passContext_recursive_ref.spec.js
  102. +40 −0 spec/issues/8_shared_refs.spec.js
  103. +48 −0 spec/issues/955_removeAdditional_custom_keywords.spec.js
  104. +31 −15 spec/json-schema.spec.js
  105. +0 −1,222 spec/options.spec.js
  106. +92 −0 spec/options/comment.spec.js
  107. +79 −0 spec/options/meta_validateSchema.spec.js
  108. +97 −0 spec/options/nullable.spec.js
  109. +130 −0 spec/options/options_add_schemas.spec.js
  110. +115 −0 spec/options/options_code.spec.js
  111. +167 −0 spec/options/options_refs.spec.js
  112. +158 −0 spec/options/options_reporting.spec.js
  113. +116 −0 spec/options/options_validation.spec.js
  114. +178 −0 spec/options/ownProperties.spec.js
  115. +122 −0 spec/options/removeAdditional.spec.js
  116. +72 −0 spec/options/schemaId.spec.js
  117. +165 −0 spec/options/strictDefaults.spec.js
  118. +79 −0 spec/options/strictKeywords.spec.js
  119. +55 −0 spec/options/strictNumbers.spec.js
  120. +108 −0 spec/options/unknownFormats.spec.js
  121. +223 −0 spec/options/useDefaults.spec.js
  122. +1 −1 spec/remotes/bar.json
  123. +1 −1 spec/remotes/buu.json
  124. +1 −1 spec/remotes/first.json
  125. +1 −1 spec/remotes/foo.json
  126. +36 −134 spec/remotes/hyper-schema.json
  127. +1 −1 spec/remotes/node.json
  128. +3 −3 spec/remotes/scope_change.json
  129. +1 −1 spec/remotes/second.json
  130. +1 −1 spec/remotes/tree.json
  131. +52 −23 spec/resolve.spec.js
  132. +1 −4 spec/schema-tests.spec.js
  133. +27 −0 spec/security.spec.js
  134. +104 −0 spec/security/array.json
  135. +29 −0 spec/security/object.json
  136. +44 −0 spec/security/string.json
  137. +74 −0 spec/tests/issues/1061_alternative_time_offsets.json
  138. +6 −15 spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json
  139. +10 −24 spec/tests/issues/14_ref_in_remote_ref_with_id.json
  140. +24 −24 spec/tests/issues/170_ref_and_id_in_sibling.json
  141. +3 −3 spec/tests/issues/1_ids_in_refs.json
  142. +3 −3 spec/tests/issues/27_1_recursive_raml_schema.json
  143. +4 −4 spec/tests/issues/27_recursive_reference.json
  144. +5 −13 spec/tests/issues/63_id_property_not_in_schema.json
  145. +7 −13 spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json
  146. +40 −40 spec/tests/issues/70_swagger_schema.json
  147. +23 −0 spec/tests/issues/861_empty_propertynames.json
  148. +40 −0 spec/tests/rules/comment.json
  149. +87 −11 spec/tests/rules/format.json
  150. +134 −0 spec/tests/rules/if.json
  151. +113 −0 spec/tests/rules/uniqueItems.json
  152. +2 −2 spec/tests/schemas/advanced.json
  153. +2 −3 spec/tests/schemas/basic.json
  154. +8 −8 spec/tests/schemas/complex.json
  155. +9 −9 spec/tests/schemas/complex3.json
  156. +60 −0 spec/typescript/index.ts
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ env:
rules:
block-scoped-var: 2
callback-return: 2
complexity: [2, 13]
complexity: [2, 16]
curly: [2, multi-or-nest, consistent]
dot-location: [2, property]
dot-notation: 2
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github: epoberezkin
tidelift: "npm/ajv"
open_collective: "ajv"
15 changes: 8 additions & 7 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<!--
Frequently Asked Questions: https://github.com/epoberezkin/ajv/blob/master/FAQ.md
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
Please provide all info and reduce your schema and data to the smallest possible size.
This template is for bug reports. For other issues please use:
- a new feature/improvement: http://epoberezkin.github.io/ajv/contribute.html#changes
- browser/compatibility issues: http://epoberezkin.github.io/ajv/contribute.html#compatibility
- JSON-Schema standard: http://epoberezkin.github.io/ajv/contribute.html#json-schema
This template is for bug or error reports. For other issues please use:
- security vulnerability: https://tidelift.com/security)
- a new feature/improvement: https://ajv.js.org/contribute.html#changes
- browser/compatibility issues: https://ajv.js.org/contribute.html#compatibility
- JSON-Schema standard: https://ajv.js.org/contribute.html#json-schema
- Ajv usage questions: https://gitter.im/ajv-validator/ajv
-->

@@ -15,7 +16,7 @@ This template is for bug reports. For other issues please use:

**Ajv options object**

<!-- See https://github.com/epoberezkin/ajv#options -->
<!-- See https://github.com/ajv-validator/ajv#options -->

```javascript

@@ -48,7 +49,7 @@ This template is for bug reports. For other issues please use:
<!--
Please:
- make it as small as posssible to reproduce the issue
- use one of the usage patterns from https://github.com/epoberezkin/ajv#getting-started
- use one of the usage patterns from https://github.com/ajv-validator/ajv#getting-started
- use `options`, `schema` and `data` as variables, do not repeat their values here
- post a working code sample in RunKit notebook cloned from https://runkit.com/esp/ajv-issue and include the link here.
81 changes: 81 additions & 0 deletions .github/ISSUE_TEMPLATE/bug-or-error-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
name: Bug or error report
about: Please use for issues related to incorrect validation behaviour
title: ''
labels: 'bug report'
assignees: ''

---

<!--
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
Please provide all info and reduce your schema and data to the smallest possible size.
This template is for bug or error reports.
For other issues please see https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md
-->

**What version of Ajv are you using? Does the issue happen if you use the latest version?**



**Ajv options object**

<!-- See https://github.com/ajv-validator/ajv#options -->

```javascript


```


**JSON Schema**

<!-- Please make it as small as possible to reproduce the issue -->

```json


```


**Sample data**

<!-- Please make it as small as posssible to reproduce the issue -->

```json


```


**Your code**

<!--
Please:
- make it as small as posssible to reproduce the issue
- use one of the usage patterns from https://github.com/ajv-validator/ajv#getting-started
- use `options`, `schema` and `data` as variables, do not repeat their values here
- post a working code sample in RunKit notebook cloned from https://runkit.com/esp/ajv-issue and include the link here.
It would make understanding your problem easier and the issue more useful to others.
Thank you!
-->

```javascript


```


**Validation result, data AFTER validation, error messages**

```
```

**What results did you expect?**


**Are you going to resolve the issue?**
24 changes: 24 additions & 0 deletions .github/ISSUE_TEMPLATE/change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: Feature or change proposal
about: For proposals of new features, options or some other improvements
title: ''
labels: 'enhancement'
assignees: ''

---

<!--
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
Please provide all info and reduce your schema and data to the smallest possible size.
This template is for change proposals.
For other issues please see https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md
-->

**What version of Ajv you are you using?**

**What problem do you want to solve?**

**What do you think is the correct solution to problem?**

**Will you be able to implement it?**
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: Browser and compatibility issue
about: For issues that only happen in a specific environment
title: ''
labels: 'compatibility'
assignees: ''

---

<!--
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
Please provide all info and reduce your schema and data to the smallest possible size.
This template is for compatibility issues.
For other issues please see https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md
-->

**The version of Ajv you are using**

**The environment you have the problem with**

**Your code (please make it as small as possible to reproduce the issue)**

**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**

**Results in node.js v8+**

**Results and error messages in your platform**
33 changes: 33 additions & 0 deletions .github/ISSUE_TEMPLATE/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: Installation and dependency issue
about: For issues that happen during installation
title: ''
labels: 'installation'
assignees: ''

---

<!--
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
Please provide all info and reduce your schema and data to the smallest possible size.
This template is for installation and dependency issues.
For other issues please see https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md
Before submitting the issue, please try the following:
- use the latest stable Node.js and npm
- use yarn instead of npm - the issue can be related to https://github.com/npm/npm/issues/19877
- remove node_modules and package-lock.json and run install again
-->

**The version of Ajv you are using**

**Operating system and node.js version**

**Package manager and its version**

**Link to (or contents of) package.json**

**Error messages**

**The output of `npm ls`**
42 changes: 42 additions & 0 deletions .github/ISSUE_TEMPLATE/typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
name: Missing or incorrect type definition
about: Please use for issues related to typescript types
title: ''
labels: 'typescript'
assignees: ''

---

<!--
Frequently Asked Questions: https://github.com/ajv-validator/ajv/blob/master/FAQ.md
This template is for issues about missing or incorrect type definition and other typescript-related issues.
For other issues please see https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md
-->

**What version of Ajv are you using? Does the issue happen if you use the latest version?**


**Your typescript code**

<!--
Please make it as small as posssible to reproduce the issue
-->

```typescript


```


**Typescript compiler error messages**

```
```

**Describe the change that should be made to address the issue?**


**Are you going to resolve the issue?**
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
Thank you for submitting a pull request to Ajv.
Before continuing, please read the guidelines:
https://github.com/epoberezkin/ajv/blob/master/CONTRIBUTING.md#pull-requests
https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md#pull-requests
If the pull request contains code please make sure there is an issue that we agreed to resolve (if it is a documentation improvement there is no need for an issue).
32 changes: 32 additions & 0 deletions .github/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Please supply comments to be used for GitHub labels
githubLabels:
bug: >
Bug confirmed - to be fixed. PR is welcome!
# duplicate: >
# enhancement: >
# good first issue: >
# help wanted: >
# invalid: >
# question: >
# wont fix: >

bug report: >
Thank you for the report! If you didn't post a code sample to RunKit yet,
please clone this notebook https://runkit.com/esp/ajv-issue,
post the code sample that demonstrates the bug and post the link here.
It will speed up the investigation and fixing!
json schema: >
This question is about the usage of JSON Schema specification - it is not specific to Ajv.
Please use JSON Schema reference materials or [submit the question to Stack Overflow](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv).
- [JSON Schema specification](http://json-schema.org/)
- [Tutorial by Space Telescope Science Institute](http://json-schema.org/understanding-json-schema/)
- [validation keywords](https://github.com/ajv-validator/ajv#validation-keywords) (in Ajv docs)
- [combining schemas](https://github.com/ajv-validator/ajv#ref) (in Ajv docs)
- [Tutorial by @epoberezkin](https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343)
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -37,3 +37,5 @@ lib/dotjs/*.js

# bundles
dist/

package-lock.json
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -3,14 +3,14 @@ before_script:
- git submodule update --init
- npm install -g codeclimate-test-reporter
node_js:
- "4"
- "6"
- "7"
- "8"
- 10
- 12
- 14
after_script:
- codeclimate-test-reporter < coverage/lcov.info
- coveralls < coverage/lcov.info
- scripts/travis-gh-pages
- scripts/publish-built-version
notifications:
webhooks:
urls:
76 changes: 76 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at ajv.validator@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
2 changes: 1 addition & 1 deletion COERCION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Ajv type coercion rules

To enable type coercion pass option `coerceTypes` to Ajv with `true` or `array` (it is `false` by default). See [example](https://github.com/epoberezkin/ajv#coercing-data-types).
To enable type coercion pass option `coerceTypes` to Ajv with `true` or `array` (it is `false` by default). See [example](https://github.com/ajv-validator/ajv#coercing-data-types).

The coercion rules are different from JavaScript:
- to validate user input as expected
60 changes: 45 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -5,9 +5,11 @@ Thank you for your help making Ajv better! Every contribution is appreciated. If
- [Documentation](#documentation)
- [Issues](#issues)
- [Bug reports](#bug-reports)
- [Security vulnerabilities](#security-vulnerabilities)
- [Change proposals](#changes)
- [Browser and compatibility issues](#compatibility)
- [JSON schema standard](#json-schema)
- [Installation and dependency issues](#installation)
- [JSON Schema standard](#json-schema)
- [Ajv usage questions](#usage)
- [Code](#code)
- [Development](#development)
@@ -22,7 +24,7 @@ Ajv has a lot of features and maintaining documentation takes time. I appreciate

## Issues

Before submitting the issue please search the existing issues and also review [Frequently Asked Questions](https://github.com/epoberezkin/ajv/blob/master/FAQ.md).
Before submitting the issue please search the existing issues and also review [Frequently Asked Questions](https://github.com/ajv-validator/ajv/blob/master/FAQ.md).

I would really appreciate the time you spend providing all the information and reducing both your schema and data to the smallest possible size when they still have the issue. Simplifying the issue also makes it more valuable for other users (in cases it turns out to be an incorrect usage rather than a bug).

@@ -32,18 +34,29 @@ I would really appreciate the time you spend providing all the information and r
Please make sure to include the following information in the issue:

1. What version of Ajv are you using? Does the issue happen if you use the latest version?
2. Ajv options object (see https://github.com/epoberezkin/ajv#options).
3. JSON schema and the data you are validating (please make it as small as possible to reproduce the issue).
2. Ajv options object (see https://github.com/ajv-validator/ajv#options).
3. JSON Schema and the data you are validating (please make it as small as possible to reproduce the issue).
4. Your code (please use `options`, `schema` and `data` as variables).
5. Validation result, data AFTER validation, error messages.
6. What results did you expect?

[Create bug report](https://github.com/epoberezkin/ajv/issues/new).
Please include the link to the working code sample at Runkit.com (please clone https://runkit.com/esp/ajv-issue) - it will speed up investigation and fixing.

[Create bug report](https://github.com/ajv-validator/ajv/issues/new?template=bug-or-error-report.md).


#### Security vulnerabilities

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

Please do NOT report security vulnerabilities via GitHub issues.


#### <a name="changes"></a>Change proposals

[Create a proposal](https://github.com/epoberezkin/ajv/issues/new?labels=suggestion&body=**What%20version%20of%20Ajv%20you%20are%20you%20using%3F**%0A%0A**What%20problem%20do%20you%20want%20to%20solve%3F**%0A%0A**What%20do%20you%20think%20is%20the%20correct%20solution%20to%20problem?**%0A%0A**Will%20you%20be%20able%20to%20implement%20it%3F**%0A%0A) for a new feature, option or some other improvement.
[Create a proposal](https://github.com/ajv-validator/ajv/issues/new?template=change.md) for a new feature, option or some other improvement.

Please include this information:

@@ -63,25 +76,42 @@ Please include as much details as possible.

#### <a name="compatibility"></a>Browser and compatibility issues

[Create an issue](https://github.com/epoberezkin/ajv/issues/new?labels=compatibility&body=**The%20version%20of%20Ajv%20you%20are%20using**%0A%0A**The%20environment%20you%20have%20the%20problem%20with.**%0A%0A**Your%20code%20(please%20make%20it%20as%20small%20as%20possible%20to%20reproduce%20the%20issue).**%0A%0A**If%20your%20issue%20is%20in%20the%20browser,%20please%20list%20the%20other%20packages%20loaded%20in%20the%20page%20in%20the%20order%20they%20are%20loaded.%20Please%20check%20if%20the%20issue%20gets%20resolved%20(or%20results%20change)%20if%20you%20move%20Ajv%20bundle%20closer%20to%20the%20top.**%0A%0A**Results%20in%20node.js%20v4.**%0A%0A**Results%20and%20error%20messages%20in%20your%20platform.**%0A%0A) to report a compatibility problem that only happens in a particular environemnt (when your code works correctly in node.js v4 in linux systems but fails in some other environment).
[Create an issue](https://github.com/ajv-validator/ajv/issues/new?template=compatibility.md) to report a compatibility problem that only happens in a particular environment (when your code works correctly in node.js v8+ in linux systems but fails in some other environment).

Please include this information:

1. The version of Ajv you are using.
2. The environment you have the problem with.
3. Your code (please make it as small as possible to reproduce the issue).
4. 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.
5. Results in node.js v4.
5. Results in node.js v8+.
6. Results and error messages in your platform.


#### <a name="json-schema"></a>Using JSON schema standard
#### <a name="installation"></a>Installation and dependency issues

[Create an issue](https://github.com/ajv-validator/ajv/issues/new?template=installation.md) to report problems that happen during Ajv installation or when Ajv is missing some dependency.

Before submitting the issue, please try the following:
- use the latest stable Node.js and `npm`
- use `yarn` instead of `npm` - the issue can be related to https://github.com/npm/npm/issues/19877
- remove `node_modules` and `package-lock.json` and run install again

If nothing helps, please submit:

1. The version of Ajv you are using
2. Operating system and node.js version
3. Package manager and its version
4. Link to (or contents of) package.json
5. Error messages
6. The output of `npm ls`


Ajv implements JSON schema standard draft 4 and the proposed extensions for the next version of the standard (available when you use the option `v5: true`).
#### <a name="json-schema"></a>Using JSON Schema standard

If the issue is related to using v5 extensions please submit it as a [bug report](https://github.com/epoberezkin/ajv/issues/new).
Ajv implements JSON Schema standard draft-04 and draft-06/07.

If it is a general issue related to using the standard keywords included in JSON Schema or implementing some advanced validation logic please ask the question on [Stack Overflow](http://stackoverflow.com/questions/ask?tags=jsonschema,ajv) (my account is [esp](http://stackoverflow.com/users/1816503/esp)) or submitting the question to [JSON-Schema.org](https://github.com/json-schema-org/json-schema-spec/issues/new). Please mention @epoberezkin.
If it is a general issue related to using the standard keywords included in JSON Schema or implementing some advanced validation logic please ask the question on [Stack Overflow](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv) (my account is [esp](https://stackoverflow.com/users/1816503/esp)) or submitting the question to [JSON-Schema.org](https://github.com/json-schema-org/json-schema-spec/issues/new). Please mention @epoberezkin.


#### <a name="usage"></a>Ajv usage questions
@@ -113,9 +143,9 @@ npm run test-fast
git commit -nm 'type: message'
```

All validation functions are generated using doT templates in [dot](https://github.com/epoberezkin/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency.
All validation functions are generated using doT templates in [dot](https://github.com/ajv-validator/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency.

`npm run build` - compiles templates to [dotjs](https://github.com/epoberezkin/ajv/tree/master/lib/dotjs) folder.
`npm run build` - compiles templates to [dotjs](https://github.com/ajv-validator/ajv/tree/master/lib/dotjs) folder.

`npm run watch` - automatically compiles templates when files in dot folder change

@@ -124,7 +154,7 @@ All validation functions are generated using doT templates in [dot](https://gith

To make accepting your changes faster please follow these steps:

1. Submit an [issue with the bug](https://github.com/epoberezkin/ajv/issues/new) or with the proposed change (unless the contribution is to fix the documentation typos and mistakes).
1. Submit an [issue with the bug](https://github.com/ajv-validator/ajv/issues/new) or with the proposed change (unless the contribution is to fix the documentation typos and mistakes).
2. Please describe the proposed api and implementation plan (unless the issue is a relatively simple bug and fixing it doesn't change any api).
3. Once agreed, please write as little code as possible to achieve the desired result.
4. Please avoid unnecessary changes, refactoring or changing coding styles as part of your change (unless the change was proposed as refactoring).
153 changes: 99 additions & 54 deletions CUSTOM.md
Original file line number Diff line number Diff line change
@@ -34,26 +34,35 @@ This way to define keywords is useful for:
- testing your keywords before converting them to compiled/inlined keywords
- defining keywords that do not depend on the schema value (e.g., when the value is always `true`). In this case you can add option `schema: false` to the keyword definition and the schemas won't be passed to the validation function, it will only receive the same 4 parameters as compiled validation function (see the next section).
- defining keywords where the schema is a value used in some expression.
- defining keywords that support [$data reference](https://github.com/epoberezkin/ajv#data-reference) - in this case validation function is required, either as the only option or in addition to compile, macro or inline function (see below).
- defining keywords that support [$data reference](https://github.com/ajv-validator/ajv#data-reference) - in this case validation function is required, either as the only option or in addition to compile, macro or inline function (see below).

__Please note__: In cases when validation flow is different depending on the schema and you have to use `if`s, this way to define keywords will have worse performance than compiled keyword returning different validation functions depending on the schema.


Example. `constant` keyword (a synonym for draft6 keyword `const`, it is equivalent to `enum` keyword with one item):
Example. `constant` keyword (a synonym for draft-06 keyword `const`, it is equivalent to `enum` keyword with one item):

```javascript
ajv.addKeyword('constant', { validate: function (schema, data) {
return typeof schema == 'object' && schema !== null
? deepEqual(schema, data)
: schema === data;
}, errors: false });
ajv.addKeyword('constant', {
validate: function (schema, data) {
return typeof schema == 'object' && schema !== null
? deepEqual(schema, data)
: schema === data;
},
errors: false
});

var schema = { "constant": 2 };
var schema = {
"constant": 2
};
var validate = ajv.compile(schema);
console.log(validate(2)); // true
console.log(validate(3)); // false

var schema = { "constant": { "foo": "bar" } };
var schema = {
"constant": {
"foo": "bar"
}
};
var validate = ajv.compile(schema);
console.log(validate({foo: 'bar'})); // true
console.log(validate({foo: 'baz'})); // false
@@ -79,27 +88,40 @@ The access to the parent data object and the current property name allow to crea

The function should return validation result as boolean. It can return an array of validation errors via `.errors` property of itself (otherwise a standard error will be used).

In some cases it is the best approach to define keywords, but it has the performance cost of an extra function call during validation. If keyword logic can be expressed via some other JSON-schema then `macro` keyword definition is more efficient (see below).
In some cases it is the best approach to define keywords, but it has the performance cost of an extra function call during validation. If keyword logic can be expressed via some other JSON Schema then `macro` keyword definition is more efficient (see below).

All custom keywords types can have an optional `metaSchema` property in their definitions. It is a schema against which the value of keyword will be validated during schema compilation.

Custom keyword can also have an optional `dependencies` property in their definitions - it is a list of required keywords in a containing (parent) schema.

Example. `range` and `exclusiveRange` keywords using compiled schema:

```javascript
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];

return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
}, errors: false, metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
additionalItems: false
} });
ajv.addKeyword('range', {
type: 'number',
compile: function (sch, parentSchema) {
var min = sch[0];
var max = sch[1];

return parentSchema.exclusiveRange === true
? function (data) { return data > min && data < max; }
: function (data) { return data >= min && data <= max; }
},
errors: false,
metaSchema: {
type: 'array',
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});

var schema = { "range": [2, 4], "exclusiveRange": true };
var schema = {
"range": [2, 4],
"exclusiveRange": true
};
var validate = ajv.compile(schema);
console.log(validate(2.01)); // true
console.log(validate(3.99)); // true
@@ -114,35 +136,38 @@ See note on custom errors and asynchronous keywords in the previous section.

"Macro" function is called during schema compilation. It is passed schema, parent schema and [schema compilation context](#schema-compilation-context) and it should return another schema that will be applied to the data in addition to the original schema.

It is the most efficient approach (in cases when the keyword logic can be expressed with another JSON-schema) because it is usually easy to implement and there is no extra function call during validation.
It is the most efficient approach (in cases when the keyword logic can be expressed with another JSON Schema) because it is usually easy to implement and there is no extra function call during validation.

In addition to the errors from the expanded schema macro keyword will add its own error in case validation fails.


Example. `range` and `exclusiveRange` keywords from the previous example defined with macro:

```javascript
ajv.addKeyword('range', { type: 'number', macro: function (schema, parentSchema) {
return {
minimum: schema[0],
maximum: schema[1],
exclusiveMinimum: !!parentSchema.exclusiveRange,
exclusiveMaximum: !!parentSchema.exclusiveRange
};
}, metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
additionalItems: false
} });
ajv.addKeyword('range', {
type: 'number',
macro: function (schema, parentSchema) {
return {
minimum: schema[0],
maximum: schema[1],
exclusiveMinimum: !!parentSchema.exclusiveRange,
exclusiveMaximum: !!parentSchema.exclusiveRange
};
},
metaSchema: {
type: 'array',
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});
```

Example. `contains` keyword from version 5 proposals that requires that the array has at least one item matching schema (see https://github.com/json-schema/json-schema/wiki/contains-(v5-proposal)):

```javascript
ajv.addKeyword('contains', { type: 'array', macro: function (schema) {
return { "not": { "items": { "not": schema } } };
} });

var schema = {
"contains": {
"type": "number",
@@ -151,15 +176,28 @@ var schema = {
}
};

var validate = ajv.compile(schema);
var validate = ajv.addKeyword('contains', {
type: 'array',
macro: function (schema) {
return {
"not": {
"items": {
"not": schema
}
}
};
}
})
.compile(schema);

console.log(validate([1,2,3])); // false
console.log(validate([2,3,4])); // false
console.log(validate([3,4,5])); // true, number 5 matches schema inside "contains"
```

`contains` keyword is already available in Ajv with option `v5: true`.

See the example of defining recursive macro keyword `deepProperties` in the [test](https://github.com/epoberezkin/ajv/blob/master/spec/custom.spec.js#L151).
See the example of defining recursive macro keyword `deepProperties` in the [test](https://github.com/ajv-validator/ajv/blob/master/spec/custom.spec.js#L151).


### Define keyword with "inline" compilation function
@@ -177,14 +215,18 @@ While it can be more challenging to define keywords with "inline" functions, it
Example `even` keyword:

```javascript
ajv.addKeyword('even', { type: 'number', inline: function (it, keyword, schema) {
var op = schema ? '===' : '!==';
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
}, metaSchema: { type: 'boolean' } });

var schema = { "even": true };

var validate = ajv.compile(schema);
var validate = ajv.addKeyword('even', {
type: 'number',
inline: function (it, keyword, schema) {
var op = schema ? '===' : '!==';
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
},
metaSchema: { type: 'boolean' }
})
.compile(schema);

console.log(validate(2)); // true
console.log(validate(3)); // false
```
@@ -214,7 +256,10 @@ ajv.addKeyword('range', {
statements: true,
metaSchema: {
type: 'array',
items: [ { type: 'number' }, { type: 'number' } ],
items: [
{ type: 'number' },
{ type: 'number' }
],
additionalItems: false
}
});
@@ -249,7 +294,7 @@ The first parameter passed to inline keyword compilation function (and the 3rd p
- _opts_ - Ajv instance option. You should not be changing them.
- _formats_ - all formats available in Ajv instance, including the custom ones.
- _compositeRule_ - boolean indicating that the current schema is inside the compound keyword where failing some rule doesn't mean validation failure (`anyOf`, `oneOf`, `not`, `if` in `switch`). This flag is used to determine whether you can return validation result immediately after any error in case the option `allErrors` is not `true. You only need to do it if you have many steps in your keywords and potentially can define multiple errors.
- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/epoberezkin/ajv/blob/master/lib/dot/v5/switch.jst) of `switch` keyword for example).
- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/ajv-validator/ajv-keywords/blob/master/keywords/dot/switch.jst) of `switch` keyword for example).
- _util_ - [Ajv utilities](#ajv-utilities) you can use in your inline compilation functions.
- _self_ - Ajv instance.

@@ -266,8 +311,8 @@ There is a number of variables and expressions you can use in the generated (val
- `'validate.schema' + it.schemaPath` - current level schema available at validation time (the same schema at compile time is `it.schema`).
- `'validate.schema' + it.schemaPath + '.' + keyword` - the value of your custom keyword at validation-time. Keyword is passed as the second parameter to the inline compilation function to allow using the same function to compile multiple keywords.
- `'valid' + it.level` - the variable that you have to declare and to assign the validation result to if your keyword returns statements rather than expression (`statements: true`).
- `'errors'` - the number of encountered errors. See [Reporting errors in custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords).
- `'vErrors'` - the array with errors collected so far. See [Reporting errors in custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords).
- `'errors'` - the number of encountered errors. See [Reporting errors in custom keywords](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords).
- `'vErrors'` - the array with errors collected so far. See [Reporting errors in custom keywords](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords).


## Ajv utilities
@@ -365,7 +410,7 @@ All custom keywords but macro keywords can optionally create custom error messag

Synchronous validating and compiled keywords should define errors by assigning them to `.errors` property of the validation function. Asynchronous keywords can return promise that rejects with `new Ajv.ValidationError(errors)`, where `errors` is an array of custom validation errors (if you don't want to define custom errors in asynchronous keyword, its validation function can return the promise that resolves with `false`).

Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/epoberezkin/ajv/blob/master/spec/custom_rules/range_with_errors.jst).
Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/ajv-validator/ajv/blob/master/spec/custom_rules/range_with_errors.jst).

When inline keyword performs validation Ajv checks whether it created errors by comparing errors count before and after validation. To skip this check add option `errors` (can be `"full"`, `true` or `false`) to keyword definition:

@@ -384,7 +429,7 @@ Each error object should at least have properties `keyword`, `message` and `para

Inlined keywords can optionally define `dataPath` and `schemaPath` properties in error objects, that will be assigned by Ajv unless `errors` option of the keyword is `"full"`.

If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](https://github.com/epoberezkin/ajv#validation-errors)).
If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](https://github.com/ajv-validator/ajv#validation-errors)).


## Short-circuit validation
43 changes: 33 additions & 10 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -3,9 +3,30 @@
The purpose of this document is to help find answers quicker. I am happy to continue the discussion about these issues, so please comment on some of the issues mentioned below or create a new issue if it seems more appropriate.



## Using JSON schema

Ajv implements JSON schema specification. Before submitting the issue about the behaviour of any validation keywords please review them in:

- [JSON Schema specification](https://tools.ietf.org/html/draft-handrews-json-schema-validation-00) (draft-07)
- [Validation keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md) in Ajv documentation
- [JSON Schema tutorial](https://spacetelescope.github.io/understanding-json-schema/) (for draft-04)


##### Why Ajv validates empty object as valid?

"properties" keyword does not require the presence of any properties, you need to use "required" keyword. It also doesn't require that the data is an object, so any other type of data will also be valid. To require a specific type use "type" keyword.


##### Why Ajv validates only the first item of the array?

"items" keyword support [two syntaxes](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#items) - 1) when the schema applies to all items; 2) when there is a different schema for each item in the beginning of the array. This problem means you are using the second syntax.



## Ajv API for returning validation errors

See [#65](https://github.com/epoberezkin/ajv/issues/65), [#212](https://github.com/epoberezkin/ajv/issues/212), [#236](https://github.com/epoberezkin/ajv/issues/236), [#242](https://github.com/epoberezkin/ajv/issues/242), [#256](https://github.com/epoberezkin/ajv/issues/256).
See [#65](https://github.com/ajv-validator/ajv/issues/65), [#212](https://github.com/ajv-validator/ajv/issues/212), [#236](https://github.com/ajv-validator/ajv/issues/236), [#242](https://github.com/ajv-validator/ajv/issues/242), [#256](https://github.com/ajv-validator/ajv/issues/256).


##### Why Ajv assigns errors as a property of validation function (or instance) instead of returning an object with validation results and errors?
@@ -29,17 +50,18 @@ No. In many cases there is a module responsible for the validation in the applic

Doing this would create a precedent where validated data is used in error messages, creating a vulnerability (e.g., when ajv is used to validate API data/parameters and error messages are logged).

Since the property name is already in the params object, in an application you can modify messages in any way you need. ajv-errors package will allow to modify messages as well - templating is [not there yet](https://github.com/epoberezkin/ajv-errors/issues/4), though.
Since the property name is already in the params object, in an application you can modify messages in any way you need. ajv-errors package allows modifying messages as well.



## Additional properties inside compound keywords anyOf, oneOf, etc.

See [#127](https://github.com/epoberezkin/ajv/issues/127), [#129](https://github.com/epoberezkin/ajv/issues/129), [#134](https://github.com/epoberezkin/ajv/issues/134), [#140](https://github.com/epoberezkin/ajv/issues/140), [#193](https://github.com/epoberezkin/ajv/issues/193), [#205](https://github.com/epoberezkin/ajv/issues/205), [#238](https://github.com/epoberezkin/ajv/issues/238), [#264](https://github.com/epoberezkin/ajv/issues/264).
See [#127](https://github.com/ajv-validator/ajv/issues/127), [#129](https://github.com/ajv-validator/ajv/issues/129), [#134](https://github.com/ajv-validator/ajv/issues/134), [#140](https://github.com/ajv-validator/ajv/issues/140), [#193](https://github.com/ajv-validator/ajv/issues/193), [#205](https://github.com/ajv-validator/ajv/issues/205), [#238](https://github.com/ajv-validator/ajv/issues/238), [#264](https://github.com/ajv-validator/ajv/issues/264).


##### Why the keyword `additionalProperties: false` fails validation when some properties are "declared" inside a subschema in `anyOf`/etc.?

The keyword `additionalProperties` creates the restriction on validated data based on its own value (`false` or schema object) and on the keywords `properties` and `patternProperties` in the SAME schema object. JSON-schema validators must NOT take into account properties used in other schema objects.
The keyword `additionalProperties` creates the restriction on validated data based on its own value (`false` or schema object) and on the keywords `properties` and `patternProperties` in the SAME schema object. JSON Schema validators must NOT take into account properties used in other schema objects.

While you can expect that the schema below would allow the objects either with properties `foo` and `bar` or with properties `foo` and `baz` and all other properties will be prohibited, this schema will only allow objects with one property `foo` (an empty object and any non-objects will also be valid):

@@ -61,14 +83,15 @@ There are several ways to implement the described logic that would allow two pro

##### Why the validation fails when I use option `removeAdditional` with the keyword `anyOf`/etc.?

This problem is related to the problem explained above - properties treated as additional in the sence of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object.
This problem is related to the problem explained above - properties treated as additional in the sense of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object.

See the exemple in [Filtering Data](https://github.com/ajv-validator/ajv#filtering-data) section of readme.

See the exemple in [Filtering Data](https://github.com/epoberezkin/ajv#filtering-data) section of readme.


## Generating schemas with resolved references ($ref)

See [#22](https://github.com/epoberezkin/ajv/issues/22), [#125](https://github.com/epoberezkin/ajv/issues/125), [#146](https://github.com/epoberezkin/ajv/issues/146), [#228](https://github.com/epoberezkin/ajv/issues/228), [#336](https://github.com/epoberezkin/ajv/issues/336), [#454](https://github.com/epoberezkin/ajv/issues/454).
See [#22](https://github.com/ajv-validator/ajv/issues/22), [#125](https://github.com/ajv-validator/ajv/issues/125), [#146](https://github.com/ajv-validator/ajv/issues/146), [#228](https://github.com/ajv-validator/ajv/issues/228), [#336](https://github.com/ajv-validator/ajv/issues/336), [#454](https://github.com/ajv-validator/ajv/issues/454).


##### Why Ajv does not replace references ($ref) with the actual referenced schemas as some validators do?
@@ -77,12 +100,12 @@ See [#22](https://github.com/epoberezkin/ajv/issues/22), [#125](https://github.c
2. When schemas are recursive (or mutually recursive) resolving references would result in self-referencing recursive data-structures that can be difficult to process.
3. There are cases when such inlining would also require adding (or modyfing) `id` attribute in the inlined schema fragment to make the resulting schema equivalent.

There were many conversations about the meaning of `$ref` in [JSON Schema GitHub organisation](https://github.com/json-schema-org). The consesus is that while it is possible to treat `$ref` as schema inclusion with two caveats (above), this interpretation is unnecessary complex. A more efficient approach is to treat `$ref` as a delegation, i.e. a special keyword that validates the current data instance against the referenced schema. The analogy with programming languages is that `$ref` is a function call rather than a macro. See [here](https://github.com/json-schema-org/json-schema-spec/issues/279), for example.
There were many conversations about the meaning of `$ref` in [JSON Schema GitHub organisation](https://github.com/json-schema-org). The consensus is that while it is possible to treat `$ref` as schema inclusion with two caveats (above), this interpretation is unnecessary complex. A more efficient approach is to treat `$ref` as a delegation, i.e. a special keyword that validates the current data instance against the referenced schema. The analogy with programming languages is that `$ref` is a function call rather than a macro. See [here](https://github.com/json-schema-org/json-schema-spec/issues/279), for example.


##### How can I generate a schema where `$ref` keywords are replaced with referenced schemas?

There are two possible approaches:

1. Write code to traverse schema and replace every `$ref` with the referenced schema. An additional limitation is that `"$ref"` inside keywords "properties", "patternProperties" and "dependencies" means property name (or pattern) rather than the reference to another schema.
2. Use a specially constructed JSON Schema with a [custom keyword](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md) to traverse and modify your schema.
1. Traverse schema (e.g. with json-schema-traverse) and replace every `$ref` with the referenced schema.
2. Use a specially constructed JSON Schema with a [custom keyword](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md) to traverse and modify your schema.
156 changes: 51 additions & 105 deletions KEYWORDS.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Evgeny Poberezkin
Copyright (c) 2015-2017 Evgeny Poberezkin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
738 changes: 485 additions & 253 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
"schema",
"validator"
],
"homepage": "https://github.com/epoberezkin/ajv",
"homepage": "https://github.com/ajv-validator/ajv",
"moduleType": [
"amd",
"globals",
12 changes: 8 additions & 4 deletions karma.conf.js
Original file line number Diff line number Diff line change
@@ -17,8 +17,7 @@ module.exports = function(config) {
files: [
'dist/ajv.min.js',
'node_modules/chai/chai.js',
'dist/regenerator.min.js',
'dist/nodent.min.js',
'node_modules/ajv-async/dist/ajv-async.min.js',
'node_modules/bluebird/js/browser/bluebird.core.min.js',
'.browser/*.spec.js'
],
@@ -54,8 +53,13 @@ module.exports = function(config) {
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['Chrome'],

browsers: ['HeadlessChrome'],
customLaunchers: {
HeadlessChrome:{
base: 'ChromeHeadless',
flags: ['--no-sandbox']
},
},

// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
50 changes: 13 additions & 37 deletions karma.sauce.js
Original file line number Diff line number Diff line change
@@ -22,29 +22,18 @@ module.exports = function(config) {
browserName: 'chrome',
version: '27'
},
// 'SL_Chrome_37': {
// base: 'SauceLabs',
// browserName: 'chrome',
// version: '37'
// },
'SL_Chrome': {
base: 'SauceLabs',
browserName: 'chrome'
},
'SL_InternetExplorer_9': {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '9'
},
'SL_InternetExplorer_10': {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '10'
},
'SL_InternetExplorer_11': {
'SL_InternetExplorer': {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '11' // default
browserName: 'internet explorer'
},
'SL_MicrosoftEdge': {
base: 'SauceLabs',
@@ -55,45 +44,32 @@ module.exports = function(config) {
browserName: 'firefox',
version: '17'
},
// 'SL_FireFox_24': {
// base: 'SauceLabs',
// browserName: 'firefox',
// version: '24'
// },
'SL_FireFox': {
base: 'SauceLabs',
browserName: 'firefox'
},
'SL_Safari_5': {
'SL_Safari_7': {
base: 'SauceLabs',
browserName: 'safari',
version: '5' // default
version: '7'
},
// 'SL_Safari_7': {
// base: 'SauceLabs',
// browserName: 'safari',
// version: '7'
// },
'SL_Safari_9': {
'SL_Safari': {
base: 'SauceLabs',
browserName: 'safari',
version: '9'
browserName: 'safari'
},
'SL_iPhone_8': {
base: 'SauceLabs',
browserName: 'iphone',
version: '8.4'
},
'SL_iPhone_9': {
'SL_iPhone': {
base: 'SauceLabs',
browserName: 'iphone',
version: '9.2'
browserName: 'iphone'
},
'SL_Android': {
base: 'SauceLabs',
browserName: 'android'
}
// 'SL_Android_4': {
// base: 'SauceLabs',
// browserName: 'android',
// version: '4'
// }
};


@@ -112,7 +88,7 @@ module.exports = function(config) {
files: [
'dist/ajv.min.js',
'node_modules/chai/chai.js',
'dist/nodent.min.js',
'node_modules/ajv-async/dist/ajv-async.min.js',
'node_modules/bluebird/js/browser/bluebird.core.min.js',
'.browser/*.spec.js'
],
258 changes: 179 additions & 79 deletions lib/ajv.d.ts

Large diffs are not rendered by default.

105 changes: 67 additions & 38 deletions lib/ajv.js
Original file line number Diff line number Diff line change
@@ -4,13 +4,11 @@ var compileSchema = require('./compile')
, resolve = require('./compile/resolve')
, Cache = require('./cache')
, SchemaObject = require('./compile/schema_obj')
, stableStringify = require('json-stable-stringify')
, stableStringify = require('fast-json-stable-stringify')
, formats = require('./compile/formats')
, rules = require('./compile/rules')
, $dataMetaSchema = require('./$data')
, patternGroups = require('./patternGroups')
, util = require('./compile/util')
, co = require('co');
, $dataMetaSchema = require('./data')
, util = require('./compile/util');

module.exports = Ajv;

@@ -32,15 +30,16 @@ var customKeyword = require('./keyword');
Ajv.prototype.addKeyword = customKeyword.add;
Ajv.prototype.getKeyword = customKeyword.get;
Ajv.prototype.removeKeyword = customKeyword.remove;
Ajv.prototype.validateKeyword = customKeyword.validate;

var errorClasses = require('./compile/error_classes');
Ajv.ValidationError = errorClasses.Validation;
Ajv.MissingRefError = errorClasses.MissingRef;
Ajv.$dataMetaSchema = $dataMetaSchema;

var META_SCHEMA_ID = 'http://json-schema.org/draft-06/schema';
var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema';

var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ];
var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'strictDefaults' ];
var META_SUPPORT_DATA = ['/properties'];

/**
@@ -52,12 +51,11 @@ var META_SUPPORT_DATA = ['/properties'];
function Ajv(opts) {
if (!(this instanceof Ajv)) return new Ajv(opts);
opts = this._opts = util.copy(opts) || {};
setLogger(this);
this._schemas = {};
this._refs = {};
this._fragments = {};
this._formats = formats(opts.format);
var schemaUriFormat = this._schemaUriFormat = this._formats['uri-reference'];
this._schemaUriFormatFunc = function (str) { return schemaUriFormat.test(str); };

this._cache = opts.cache || new Cache;
this._loadingSchemas = {};
@@ -71,17 +69,18 @@ function Ajv(opts) {
this._metaOpts = getMetaSchemaOptions(this);

if (opts.formats) addInitialFormats(this);
addDraft6MetaSchema(this);
if (opts.keywords) addInitialKeywords(this);
addDefaultMetaSchema(this);
if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta);
if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: 'boolean'}});
addInitialSchemas(this);
if (opts.patternGroups) patternGroups(this);
}



/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key. [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize.
* Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize.
* @this Ajv
* @param {String|Object} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated
@@ -98,9 +97,7 @@ function validate(schemaKeyRef, data) {
}

var valid = v(data);
if (v.$async === true)
return this._opts.async == '*' ? co(valid) : valid;
this.errors = v.errors;
if (v.$async !== true) this.errors = v.errors;
return valid;
}

@@ -125,18 +122,20 @@ function compile(schema, _meta) {
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
* @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead.
* @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead.
* @return {Ajv} this for method chaining
*/
function addSchema(schema, key, _skipValidation, _meta) {
if (Array.isArray(schema)){
for (var i=0; i<schema.length; i++) this.addSchema(schema[i], undefined, _skipValidation, _meta);
return;
return this;
}
var id = this._getId(schema);
if (id !== undefined && typeof id != 'string')
throw new Error('schema id must be string');
key = resolve.normalizeId(key || id);
checkUnique(this, key);
this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
return this;
}


@@ -147,9 +146,11 @@ function addSchema(schema, key, _skipValidation, _meta) {
* @param {Object} schema schema object
* @param {String} key optional schema key
* @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema
* @return {Ajv} this for method chaining
*/
function addMetaSchema(schema, key, skipValidation) {
this.addSchema(schema, key, skipValidation, true);
return this;
}


@@ -166,20 +167,14 @@ function validateSchema(schema, throwOrLogError) {
throw new Error('$schema must be a string');
$schema = $schema || this._opts.defaultMeta || defaultMeta(this);
if (!$schema) {
console.warn('meta-schema not available');
this.logger.warn('meta-schema not available');
this.errors = null;
return true;
}
var currentUriFormat = this._formats.uri;
this._formats.uri = typeof currentUriFormat == 'function'
? this._schemaUriFormatFunc
: this._schemaUriFormat;
var valid;
try { valid = this.validate($schema, schema); }
finally { this._formats.uri = currentUriFormat; }
var valid = this.validate($schema, schema);
if (!valid && throwOrLogError) {
var message = 'schema is invalid: ' + this.errorsText();
if (this._opts.validateSchema == 'log') console.error(message);
if (this._opts.validateSchema == 'log') this.logger.error(message);
else throw new Error(message);
}
return valid;
@@ -246,25 +241,26 @@ function _getSchemaObj(self, keyRef) {
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
* @this Ajv
* @param {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object
* @return {Ajv} this for method chaining
*/
function removeSchema(schemaKeyRef) {
if (schemaKeyRef instanceof RegExp) {
_removeAllSchemas(this, this._schemas, schemaKeyRef);
_removeAllSchemas(this, this._refs, schemaKeyRef);
return;
return this;
}
switch (typeof schemaKeyRef) {
case 'undefined':
_removeAllSchemas(this, this._schemas);
_removeAllSchemas(this, this._refs);
this._cache.clear();
return;
return this;
case 'string':
var schemaObj = _getSchemaObj(this, schemaKeyRef);
if (schemaObj) this._cache.del(schemaObj.cacheKey);
delete this._schemas[schemaKeyRef];
delete this._refs[schemaKeyRef];
return;
return this;
case 'object':
var serialize = this._opts.serialize;
var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef;
@@ -276,6 +272,7 @@ function removeSchema(schemaKeyRef) {
delete this._refs[id];
}
}
return this;
}


@@ -349,6 +346,10 @@ function _compile(schemaObj, root) {

var v;
try { v = compileSchema.call(this, schemaObj.schema, root, schemaObj.localRefs); }
catch(e) {
delete schemaObj.validate;
throw e;
}
finally {
schemaObj.compiling = false;
if (schemaObj.meta) this._opts = currentOpts;
@@ -361,9 +362,11 @@ function _compile(schemaObj, root) {
return v;


/* @this {*} - custom context, see passContext option */
function callValidate() {
/* jshint validthis: true */
var _validate = schemaObj.validate;
var result = _validate.apply(null, arguments);
var result = _validate.apply(this, arguments);
callValidate.errors = _validate.errors;
return result;
}
@@ -372,21 +375,21 @@ function _compile(schemaObj, root) {

function chooseGetId(opts) {
switch (opts.schemaId) {
case '$id': return _get$Id;
case 'auto': return _get$IdOrId;
case 'id': return _getId;
default: return _get$IdOrId;
default: return _get$Id;
}
}


/* @this Ajv */
function _getId(schema) {
if (schema.$id) console.warn('schema $id ignored', schema.$id);
if (schema.$id) this.logger.warn('schema $id ignored', schema.$id);
return schema.id;
}


/* @this Ajv */
function _get$Id(schema) {
if (schema.id) console.warn('schema id ignored', schema.id);
if (schema.id) this.logger.warn('schema id ignored', schema.id);
return schema.$id;
}

@@ -426,21 +429,23 @@ function errorsText(errors, options) {
* @this Ajv
* @param {String} name format name
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
* @return {Ajv} this for method chaining
*/
function addFormat(name, format) {
if (typeof format == 'string') format = new RegExp(format);
this._formats[name] = format;
return this;
}


function addDraft6MetaSchema(self) {
function addDefaultMetaSchema(self) {
var $dataSchema;
if (self._opts.$data) {
$dataSchema = require('./refs/$data.json');
$dataSchema = require('./refs/data.json');
self.addMetaSchema($dataSchema, $dataSchema.$id, true);
}
if (self._opts.meta === false) return;
var metaSchema = require('./refs/json-schema-draft-06.json');
var metaSchema = require('./refs/json-schema-draft-07.json');
if (self._opts.$data) metaSchema = $dataMetaSchema(metaSchema, META_SUPPORT_DATA);
self.addMetaSchema(metaSchema, META_SCHEMA_ID, true);
self._refs['http://json-schema.org/schema'] = META_SCHEMA_ID;
@@ -463,6 +468,14 @@ function addInitialFormats(self) {
}


function addInitialKeywords(self) {
for (var name in self._opts.keywords) {
var keyword = self._opts.keywords[name];
self.addKeyword(name, keyword);
}
}


function checkUnique(self, id) {
if (self._schemas[id] || self._refs[id])
throw new Error('schema with key or id "' + id + '" already exists');
@@ -475,3 +488,19 @@ function getMetaSchemaOptions(self) {
delete metaOpts[META_IGNORE_OPTIONS[i]];
return metaOpts;
}


function setLogger(self) {
var logger = self._opts.logger;
if (logger === false) {
self.logger = {log: noop, warn: noop, error: noop};
} else {
if (logger === undefined) logger = console;
if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error))
throw new Error('logger must implement log, warn and error methods');
self.logger = logger;
}
}


function noop() {}
31 changes: 0 additions & 31 deletions lib/compile/_rules.js

This file was deleted.

2 changes: 2 additions & 0 deletions lib/compile/equal.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

// do NOT remove this file - it would break pre-compiled schemas
// https://github.com/ajv-validator/ajv/issues/889
module.exports = require('fast-deep-equal');
51 changes: 29 additions & 22 deletions lib/compile/formats.js
Original file line number Diff line number Diff line change
@@ -2,10 +2,10 @@

var util = require('./util');

var DATE = /^\d\d\d\d-(\d\d)-(\d\d)$/;
var DAYS = [0,31,29,31,30,31,30,31,31,30,31,30,31];
var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i;
var HOSTNAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i;
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
var DAYS = [0,31,28,31,30,31,30,31,31,30,31,30,31];
var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
var HOSTNAME = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i;
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
// uri-template: https://tools.ietf.org/html/rfc6570
@@ -16,7 +16,8 @@ var URITEMPLATE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|
// var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i;
var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
var JSON_POINTER_URI_FRAGMENT = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;


@@ -32,11 +33,11 @@ formats.fast = {
// date: http://tools.ietf.org/html/rfc3339#section-5.6
date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/,
// date-time: http://tools.ietf.org/html/rfc3339#section-5.6
time: /^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,
'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,
time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,
'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,
// uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js
uri: /^(?:[a-z][a-z0-9+-.]*)(?::|\/)\/?[^\s]*$/i,
'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i,
uri: /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i,
'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,
'uri-template': URITEMPLATE,
url: URL,
// email (sources from jsen validator):
@@ -54,6 +55,7 @@ formats.fast = {
// JSON-pointer: https://tools.ietf.org/html/rfc6901
// uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A
'json-pointer': JSON_POINTER,
'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
// relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00
'relative-json-pointer': RELATIVE_JSON_POINTER
};
@@ -67,25 +69,35 @@ formats.full = {
'uri-reference': URIREF,
'uri-template': URITEMPLATE,
url: URL,
email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,
hostname: hostname,
email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,
hostname: HOSTNAME,
ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
regex: regex,
uuid: UUID,
'json-pointer': JSON_POINTER,
'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
'relative-json-pointer': RELATIVE_JSON_POINTER
};


function isLeapYear(year) {
// https://tools.ietf.org/html/rfc3339#appendix-C
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}


function date(str) {
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
var matches = str.match(DATE);
if (!matches) return false;

var month = +matches[1];
var day = +matches[2];
return month >= 1 && month <= 12 && day >= 1 && day <= DAYS[month];
var year = +matches[1];
var month = +matches[2];
var day = +matches[3];

return month >= 1 && month <= 12 && day >= 1 &&
day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]);
}


@@ -97,7 +109,9 @@ function time(str, full) {
var minute = matches[2];
var second = matches[3];
var timeZone = matches[5];
return hour <= 23 && minute <= 59 && second <= 59 && (!full || timeZone);
return ((hour <= 23 && minute <= 59 && second <= 59) ||
(hour == 23 && minute == 59 && second == 60)) &&
(!full || timeZone);
}


@@ -109,13 +123,6 @@ function date_time(str) {
}


function hostname(str) {
// https://tools.ietf.org/html/rfc1034#section-3.5
// https://tools.ietf.org/html/rfc1123#section-2
return str.length <= 255 && HOSTNAME.test(str);
}


var NOT_URI_FRAGMENT = /\/|:/;
function uri(str) {
// http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "."
46 changes: 30 additions & 16 deletions lib/compile/index.js
Original file line number Diff line number Diff line change
@@ -3,15 +3,14 @@
var resolve = require('./resolve')
, util = require('./util')
, errorClasses = require('./error_classes')
, stableStringify = require('json-stable-stringify');
, stableStringify = require('fast-json-stable-stringify');

var validateGenerator = require('../dotjs/validate');

/**
* Functions below are used inside compiled validations function
*/

var co = require('co');
var ucs2length = util.ucs2length;
var equal = require('fast-deep-equal');

@@ -70,9 +69,11 @@ function compile(schema, root, localRefs, baseId) {
endCompiling.call(this, schema, root, baseId);
}

/* @this {*} - custom context, see passContext option */
function callValidate() {
/* jshint validthis: true */
var validate = compilation.validate;
var result = validate.apply(null, arguments);
var result = validate.apply(this, arguments);
callValidate.errors = validate.errors;
return result;
}
@@ -104,14 +105,15 @@ function compile(schema, root, localRefs, baseId) {
useCustomRule: useCustomRule,
opts: opts,
formats: formats,
logger: self.logger,
self: self
});

sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode)
+ vars(defaults, defaultCode) + vars(customRules, customRuleCode)
+ sourceCode;

if (opts.processCode) sourceCode = opts.processCode(sourceCode);
if (opts.processCode) sourceCode = opts.processCode(sourceCode, _schema);
// console.log('\n\n\n *** \n', JSON.stringify(sourceCode));
var validate;
try {
@@ -123,7 +125,6 @@ function compile(schema, root, localRefs, baseId) {
'refVal',
'defaults',
'customRules',
'co',
'equal',
'ucs2length',
'ValidationError',
@@ -138,15 +139,14 @@ function compile(schema, root, localRefs, baseId) {
refVal,
defaults,
customRules,
co,
equal,
ucs2length,
ValidationError
);

refVal[0] = validate;
} catch(e) {
console.error('Error compiling schema, function code:', sourceCode);
self.logger.error('Error compiling schema, function code:', sourceCode);
throw e;
}

@@ -196,7 +196,9 @@ function compile(schema, root, localRefs, baseId) {
}
}

if (v !== undefined) {
if (v === undefined) {
removeLocalRef(ref);
} else {
replaceLocalRef(ref, v);
return resolvedRef(v, refCode);
}
@@ -209,6 +211,10 @@ function compile(schema, root, localRefs, baseId) {
return 'refVal' + refId;
}

function removeLocalRef(ref) {
delete refs[ref];
}

function replaceLocalRef(ref, v) {
var refId = refs[ref];
refVal[refId] = v;
@@ -217,7 +223,7 @@ function compile(schema, root, localRefs, baseId) {
function resolvedRef(refVal, code) {
return typeof refVal == 'object' || typeof refVal == 'boolean'
? { code: code, schema: refVal, inline: true }
: { code: code, $async: refVal && refVal.$async };
: { code: code, $async: refVal && !!refVal.$async };
}

function usePattern(regexStr) {
@@ -249,13 +255,21 @@ function compile(schema, root, localRefs, baseId) {
}

function useCustomRule(rule, schema, parentSchema, it) {
var validateSchema = rule.definition.validateSchema;
if (validateSchema && self._opts.validateSchema !== false) {
var valid = validateSchema(schema);
if (!valid) {
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
if (self._opts.validateSchema == 'log') console.error(message);
else throw new Error(message);
if (self._opts.validateSchema !== false) {
var deps = rule.definition.dependencies;
if (deps && !deps.every(function(keyword) {
return Object.prototype.hasOwnProperty.call(parentSchema, keyword);
}))
throw new Error('parent schema must have all required keywords: ' + deps.join(','));

var validateSchema = rule.definition.validateSchema;
if (validateSchema) {
var valid = validateSchema(schema);
if (!valid) {
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
if (self._opts.validateSchema == 'log') self.logger.error(message);
else throw new Error(message);
}
}
}

21 changes: 10 additions & 11 deletions lib/compile/resolve.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var url = require('url')
var URI = require('uri-js')
, equal = require('fast-deep-equal')
, util = require('./util')
, SchemaObject = require('./schema_obj')
@@ -67,10 +67,10 @@ function resolve(compile, root, ref) {
*/
function resolveSchema(root, ref) {
/* jshint validthis: true */
var p = url.parse(ref, false, true)
var p = URI.parse(ref)
, refPath = _getFullPath(p)
, baseId = getFullPath(this._getId(root.schema));
if (refPath !== baseId) {
if (Object.keys(root.schema).length === 0 || refPath !== baseId) {
var id = normalizeId(refPath);
var refVal = this._refs[id];
if (typeof refVal == 'string') {
@@ -115,9 +115,9 @@ var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum
/* @this Ajv */
function getJsonPointer(parsedRef, baseId, schema, root) {
/* jshint validthis: true */
parsedRef.hash = parsedRef.hash || '';
if (parsedRef.hash.slice(0,2) != '#/') return;
var parts = parsedRef.hash.split('/');
parsedRef.fragment = parsedRef.fragment || '';
if (parsedRef.fragment.slice(0,1) != '/') return;
var parts = parsedRef.fragment.split('/');

for (var i = 1; i < parts.length; i++) {
var part = parts[i];
@@ -206,14 +206,13 @@ function countKeys(schema) {

function getFullPath(id, normalize) {
if (normalize !== false) id = normalizeId(id);
var p = url.parse(id, false, true);
var p = URI.parse(id);
return _getFullPath(p);
}


function _getFullPath(p) {
var protocolSeparator = p.protocol || p.href.slice(0,2) == '//' ? '//' : '';
return (p.protocol||'') + protocolSeparator + (p.host||'') + (p.path||'') + '#';
return URI.serialize(p).split('#')[0] + '#';
}


@@ -225,7 +224,7 @@ function normalizeId(id) {

function resolveUrl(baseId, id) {
id = normalizeId(id);
return url.resolve(baseId, id);
return URI.resolve(baseId, id);
}


@@ -246,7 +245,7 @@ function resolveIds(schema) {
fullPath += '/' + (typeof keyIndex == 'number' ? keyIndex : util.escapeFragment(keyIndex));

if (typeof id == 'string') {
id = baseId = normalizeId(baseId ? url.resolve(baseId, id) : id);
id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id);

var refVal = self._refs[id];
if (typeof refVal == 'string') refVal = self._refs[refVal];
20 changes: 14 additions & 6 deletions lib/compile/rules.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var ruleModules = require('./_rules')
var ruleModules = require('../dotjs')
, toHash = require('./util').toHash;

module.exports = function rules() {
@@ -11,17 +11,20 @@ module.exports = function rules() {
{ type: 'string',
rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] },
{ type: 'array',
rules: [ 'maxItems', 'minItems', 'uniqueItems', 'contains', 'items' ] },
rules: [ 'maxItems', 'minItems', 'items', 'contains', 'uniqueItems' ] },
{ type: 'object',
rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames',
{ 'properties': ['additionalProperties', 'patternProperties'] } ] },
{ rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf' ] }
{ rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] }
];

var ALL = [ 'type' ];
var ALL = [ 'type', '$comment' ];
var KEYWORDS = [
'additionalItems', '$schema', 'id', 'title',
'description', 'default', 'definitions'
'$schema', '$id', 'id', '$data', '$async', 'title',
'description', 'default', 'definitions',
'examples', 'readOnly', 'writeOnly',
'contentMediaType', 'contentEncoding',
'additionalItems', 'then', 'else'
];
var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ];
RULES.all = toHash(ALL);
@@ -48,6 +51,11 @@ module.exports = function rules() {
return rule;
});

RULES.all.$comment = {
keyword: '$comment',
code: ruleModules.$comment
};

if (group.type) RULES.types[group.type] = group;
});

60 changes: 16 additions & 44 deletions lib/compile/util.js
Original file line number Diff line number Diff line change
@@ -13,10 +13,9 @@ module.exports = {
ucs2length: require('./ucs2length'),
varOccurences: varOccurences,
varReplace: varReplace,
cleanUpCode: cleanUpCode,
finalCleanUpCode: finalCleanUpCode,
schemaHasRules: schemaHasRules,
schemaHasRulesExcept: schemaHasRulesExcept,
schemaUnknownRules: schemaUnknownRules,
toQuotedString: toQuotedString,
getPathExpr: getPathExpr,
getPath: getPath,
@@ -35,7 +34,7 @@ function copy(o, to) {
}


function checkDataType(dataType, data, negate) {
function checkDataType(dataType, data, strictNumbers, negate) {
var EQUAL = negate ? ' !== ' : ' === '
, AND = negate ? ' || ' : ' && '
, OK = negate ? '!' : ''
@@ -48,15 +47,18 @@ function checkDataType(dataType, data, negate) {
NOT + 'Array.isArray(' + data + '))';
case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
NOT + '(' + data + ' % 1)' +
AND + data + EQUAL + data + ')';
AND + data + EQUAL + data +
(strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')';
case 'number': return '(typeof ' + data + EQUAL + '"' + dataType + '"' +
(strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')';
default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
}
}


function checkDataTypes(dataTypes, data) {
function checkDataTypes(dataTypes, data, strictNumbers) {
switch (dataTypes.length) {
case 1: return checkDataType(dataTypes[0], data, true);
case 1: return checkDataType(dataTypes[0], data, strictNumbers, true);
default:
var code = '';
var types = toHash(dataTypes);
@@ -69,7 +71,7 @@ function checkDataTypes(dataTypes, data) {
}
if (types.number) delete types.integer;
for (var t in types)
code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
code += (code ? ' && ' : '' ) + checkDataType(t, data, strictNumbers, true);

return code;
}
@@ -135,42 +137,6 @@ function varReplace(str, dataVar, expr) {
}


var EMPTY_ELSE = /else\s*{\s*}/g
, EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
, EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
function cleanUpCode(out) {
return out.replace(EMPTY_ELSE, '')
.replace(EMPTY_IF_NO_ELSE, '')
.replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
}


var ERRORS_REGEXP = /[^v.]errors/g
, REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
, REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
, RETURN_VALID = 'return errors === 0;'
, RETURN_TRUE = 'validate.errors = null; return true;'
, RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
, RETURN_DATA_ASYNC = 'return data;'
, ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
, REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;

function finalCleanUpCode(out, async) {
var matches = out.match(ERRORS_REGEXP);
if (matches && matches.length == 2) {
out = async
? out.replace(REMOVE_ERRORS_ASYNC, '')
.replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
: out.replace(REMOVE_ERRORS, '')
.replace(RETURN_VALID, RETURN_TRUE);
}

matches = out.match(ROOTDATA_REGEXP);
if (!matches || matches.length !== 3) return out;
return out.replace(REMOVE_ROOTDATA, '');
}


function schemaHasRules(schema, rules) {
if (typeof schema == 'boolean') return !schema;
for (var key in schema) if (rules[key]) return true;
@@ -183,6 +149,12 @@ function schemaHasRulesExcept(schema, rules, exceptKeyword) {
}


function schemaUnknownRules(schema, rules) {
if (typeof schema == 'boolean') return;
for (var key in schema) if (!rules[key]) return key;
}


function toQuotedString(str) {
return '\'' + escapeQuotes(str) + '\'';
}
@@ -243,7 +215,7 @@ function getData($data, lvl, paths) {

function joinPaths (a, b) {
if (a == '""') return b;
return (a + ' + ' + b).replace(/' \+ '/g, '');
return (a + ' + ' + b).replace(/([^\\])' \+ '/g, '$1');
}


2 changes: 1 addition & 1 deletion lib/$data.js → lib/data.js
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ module.exports = function (metaSchema, keywordsJsonPointers) {
keywords[key] = {
anyOf: [
schema,
{ $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#' }
{ $ref: 'https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#' }
]
};
}
37 changes: 37 additions & 0 deletions lib/definition_schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

var metaSchema = require('./refs/json-schema-draft-07.json');

module.exports = {
$id: 'https://github.com/ajv-validator/ajv/blob/master/lib/definition_schema.js',
definitions: {
simpleTypes: metaSchema.definitions.simpleTypes
},
type: 'object',
dependencies: {
schema: ['validate'],
$data: ['validate'],
statements: ['inline'],
valid: {not: {required: ['macro']}}
},
properties: {
type: metaSchema.properties.type,
schema: {type: 'boolean'},
statements: {type: 'boolean'},
dependencies: {
type: 'array',
items: {type: 'string'}
},
metaSchema: {type: 'object'},
modifying: {type: 'boolean'},
valid: {type: 'boolean'},
$data: {type: 'boolean'},
async: {type: 'boolean'},
errors: {
anyOf: [
{type: 'boolean'},
{const: 'full'}
]
}
}
};
17 changes: 17 additions & 0 deletions lib/dot/_limit.jst
Original file line number Diff line number Diff line change
@@ -17,6 +17,15 @@
, $op = $isMax ? '<' : '>'
, $notOp = $isMax ? '>' : '<'
, $errorKeyword = undefined;

if (!($isData || typeof $schema == 'number' || $schema === undefined)) {
throw new Error($keyword + ' must be number');
}
if (!($isDataExcl || $schemaExcl === undefined
|| typeof $schemaExcl == 'number'
|| typeof $schemaExcl == 'boolean')) {
throw new Error($exclusiveKeyword + ' must be number or boolean');
}
}}

{{? $isDataExcl }}
@@ -50,6 +59,14 @@
)
|| {{=$data}} !== {{=$data}}) {
var op{{=$lvl}} = {{=$exclusive}} ? '{{=$op}}' : '{{=$op}}=';
{{
if ($schema === undefined) {
$errorKeyword = $exclusiveKeyword;
$errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
$schemaValue = $schemaValueExcl;
$isData = $isDataExcl;
}
}}
{{??}}
{{
var $exclIsNumber = typeof $schemaExcl == 'number'
2 changes: 2 additions & 0 deletions lib/dot/_limitItems.jst
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxItems' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{=$data}}.length {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
2 changes: 2 additions & 0 deletions lib/dot/_limitLength.jst
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxLength' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{# def.strLength }} {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
2 changes: 2 additions & 0 deletions lib/dot/_limitProperties.jst
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

{{ var $op = $keyword == 'maxProperties' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} Object.keys({{=$data}}).length {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
2 changes: 0 additions & 2 deletions lib/dot/allOf.jst
Original file line number Diff line number Diff line change
@@ -30,5 +30,3 @@
{{= $closingBraces.slice(0,-1) }}
{{?}}
{{?}}

{{# def.cleanUp }}
2 changes: 0 additions & 2 deletions lib/dot/anyOf.jst
Original file line number Diff line number Diff line change
@@ -39,8 +39,6 @@
} else {
{{# def.resetErrors }}
{{? it.opts.allErrors }} } {{?}}

{{# def.cleanUp }}
{{??}}
{{? $breakOnError }}
if (true) {
9 changes: 9 additions & 0 deletions lib/dot/comment.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{# def.definitions }}
{{# def.setupKeyword }}

{{ var $comment = it.util.toQuotedString($schema); }}
{{? it.opts.$comment === true }}
console.log({{=$comment}});
{{?? typeof it.opts.$comment == 'function' }}
self._opts.$comment({{=$comment}}, {{=it.util.toQuotedString($errSchemaPath)}}, validate.root.schema);
{{?}}
2 changes: 0 additions & 2 deletions lib/dot/contains.jst
Original file line number Diff line number Diff line change
@@ -53,5 +53,3 @@ var {{=$valid}};
{{# def.resetErrors }}
{{?}}
{{? it.opts.allErrors }} } {{?}}

{{# def.cleanUp }}
4 changes: 2 additions & 2 deletions lib/dot/custom.jst
Original file line number Diff line number Diff line change
@@ -112,13 +112,13 @@ var {{=$valid}};
{{# def.storeDefOut:def_callRuleValidate }}

{{? $rDef.errors === false }}
{{=$valid}} = {{? $asyncKeyword }}{{=it.yieldAwait}}{{?}}{{= def_callRuleValidate }};
{{=$valid}} = {{? $asyncKeyword }}await {{?}}{{= def_callRuleValidate }};
{{??}}
{{? $asyncKeyword }}
{{ $ruleErrs = 'customErrors' + $lvl; }}
var {{=$ruleErrs}} = null;
try {
{{=$valid}} = {{=it.yieldAwait}}{{= def_callRuleValidate }};
{{=$valid}} = await {{= def_callRuleValidate }};
} catch (e) {
{{=$valid}} = false;
if (e instanceof ValidationError) {{=$ruleErrs}} = e.errors;
27 changes: 21 additions & 6 deletions lib/dot/defaults.def
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
{{## def.assignDefault:
if ({{=$passData}} === undefined)
{{=$passData}} = {{? it.opts.useDefaults == 'shared' }}
{{= it.useDefault($sch.default) }}
{{??}}
{{= JSON.stringify($sch.default) }}
{{?}};
{{? it.compositeRule }}
{{
if (it.opts.strictDefaults) {
var $defaultMsg = 'default is ignored for: ' + $passData;
if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg);
else throw new Error($defaultMsg);
}
}}
{{??}}
if ({{=$passData}} === undefined
{{? it.opts.useDefaults == 'empty' }}
|| {{=$passData}} === null
|| {{=$passData}} === ''
{{?}}
)
{{=$passData}} = {{? it.opts.useDefaults == 'shared' }}
{{= it.useDefault($sch.default) }}
{{??}}
{{= JSON.stringify($sch.default) }}
{{?}};
{{?}}
#}}


17 changes: 10 additions & 7 deletions lib/dot/definitions.def
Original file line number Diff line number Diff line change
@@ -63,7 +63,9 @@


{{## def.nonEmptySchema:_schema:
it.util.schemaHasRules(_schema, it.RULES.all)
(it.opts.strictKeywords
? typeof _schema == 'object' && Object.keys(_schema).length > 0
: it.util.schemaHasRules(_schema, it.RULES.all))
#}}


@@ -110,12 +112,6 @@
#}}


{{## def.cleanUp: {{ out = it.util.cleanUpCode(out); }} #}}


{{## def.finalCleanUp: {{ out = it.util.finalCleanUpCode(out, $async); }} #}}


{{## def.$data:
{{
var $isData = it.opts.$data && $schema && $schema.$data
@@ -142,6 +138,13 @@
#}}


{{## def.numberKeyword:
{{? !($isData || typeof $schema == 'number') }}
{{ throw new Error($keyword + ' must be number'); }}
{{?}}
#}}


{{## def.beginDefOut:
{{
var $$outStack = $$outStack || [];
3 changes: 1 addition & 2 deletions lib/dot/dependencies.jst
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
, $ownProperties = it.opts.ownProperties;

for ($property in $schema) {
if ($property == '__proto__') continue;
var $sch = $schema[$property];
var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps;
$deps[$property] = $sch;
@@ -76,5 +77,3 @@ var missing{{=$lvl}};
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
16 changes: 8 additions & 8 deletions lib/dot/errors.def
Original file line number Diff line number Diff line change
@@ -94,23 +94,23 @@
'false schema': "'boolean schema is false'",
$ref: "'can\\\'t resolve reference {{=it.util.escapeQuotes($schema)}}'",
additionalItems: "'should NOT have more than {{=$schema.length}} items'",
additionalProperties: "'should NOT have additional properties'",
additionalProperties: "'{{? it.opts._errorDataPathProperty }}is an invalid additional property{{??}}should NOT have additional properties{{?}}'",
anyOf: "'should match some schema in anyOf'",
const: "'should be equal to constant'",
contains: "'should contain a valid item'",
dependencies: "'should have {{? $deps.length == 1 }}property {{= it.util.escapeQuotes($deps[0]) }}{{??}}properties {{= it.util.escapeQuotes($deps.join(\", \")) }}{{?}} when property {{= it.util.escapeQuotes($property) }} is present'",
'enum': "'should be equal to one of the allowed values'",
format: "'should match format \"{{#def.concatSchemaEQ}}\"'",
'if': "'should match \"' + {{=$ifClause}} + '\" schema'",
_limit: "'should be {{=$opStr}} {{#def.appendSchema}}",
_exclusiveLimit: "'{{=$exclusiveKeyword}} should be boolean'",
_limitItems: "'should NOT have {{?$keyword=='maxItems'}}more{{??}}less{{?}} than {{#def.concatSchema}} items'",
_limitItems: "'should NOT have {{?$keyword=='maxItems'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} items'",
_limitLength: "'should NOT be {{?$keyword=='maxLength'}}longer{{??}}shorter{{?}} than {{#def.concatSchema}} characters'",
_limitProperties:"'should NOT have {{?$keyword=='maxProperties'}}more{{??}}less{{?}} than {{#def.concatSchema}} properties'",
_limitProperties:"'should NOT have {{?$keyword=='maxProperties'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} properties'",
multipleOf: "'should be multiple of {{#def.appendSchema}}",
not: "'should NOT be valid'",
oneOf: "'should match exactly one schema in oneOf'",
pattern: "'should match pattern \"{{#def.concatSchemaEQ}}\"'",
patternGroups: "'should NOT have {{=$moreOrLess}} than {{=$limit}} properties matching pattern \"{{=it.util.escapeQuotes($pgProperty)}}\"'",
propertyNames: "'property name \\'{{=$invalidName}}\\' is invalid'",
required: "'{{? it.opts._errorDataPathProperty }}is a required property{{??}}should have required property \\'{{=$missingProperty}}\\'{{?}}'",
type: "'should be {{? $typeIsArray }}{{= $typeSchema.join(\",\") }}{{??}}{{=$typeSchema}}{{?}}'",
@@ -137,6 +137,7 @@
dependencies: "validate.schema{{=$schemaPath}}",
'enum': "validate.schema{{=$schemaPath}}",
format: "{{#def.schemaRefOrQS}}",
'if': "validate.schema{{=$schemaPath}}",
_limit: "{{#def.schemaRefOrVal}}",
_exclusiveLimit: "validate.schema{{=$schemaPath}}",
_limitItems: "{{#def.schemaRefOrVal}}",
@@ -146,7 +147,6 @@
not: "validate.schema{{=$schemaPath}}",
oneOf: "validate.schema{{=$schemaPath}}",
pattern: "{{#def.schemaRefOrQS}}",
patternGroups: "validate.schema{{=$schemaPath}}",
propertyNames: "validate.schema{{=$schemaPath}}",
required: "validate.schema{{=$schemaPath}}",
type: "validate.schema{{=$schemaPath}}",
@@ -167,21 +167,21 @@
additionalItems: "{ limit: {{=$schema.length}} }",
additionalProperties: "{ additionalProperty: '{{=$additionalProperty}}' }",
anyOf: "{}",
const: "{}",
const: "{ allowedValue: schema{{=$lvl}} }",
contains: "{}",
dependencies: "{ property: '{{= it.util.escapeQuotes($property) }}', missingProperty: '{{=$missingProperty}}', depsCount: {{=$deps.length}}, deps: '{{= it.util.escapeQuotes($deps.length==1 ? $deps[0] : $deps.join(\", \")) }}' }",
'enum': "{ allowedValues: schema{{=$lvl}} }",
format: "{ format: {{#def.schemaValueQS}} }",
'if': "{ failingKeyword: {{=$ifClause}} }",
_limit: "{ comparison: {{=$opExpr}}, limit: {{=$schemaValue}}, exclusive: {{=$exclusive}} }",
_exclusiveLimit: "{}",
_limitItems: "{ limit: {{=$schemaValue}} }",
_limitLength: "{ limit: {{=$schemaValue}} }",
_limitProperties:"{ limit: {{=$schemaValue}} }",
multipleOf: "{ multipleOf: {{=$schemaValue}} }",
not: "{}",
oneOf: "{}",
oneOf: "{ passingSchemas: {{=$passingSchemas}} }",
pattern: "{ pattern: {{#def.schemaValueQS}} }",
patternGroups: "{ reason: '{{=$reason}}', limit: {{=$limit}}, pattern: '{{=it.util.escapeQuotes($pgProperty)}}' }",
propertyNames: "{ propertyName: '{{=$invalidName}}' }",
required: "{ missingProperty: '{{=$missingProperty}}' }",
type: "{ type: '{{? $typeIsArray }}{{= $typeSchema.join(\",\") }}{{??}}{{=$typeSchema}}{{?}}' }",
6 changes: 3 additions & 3 deletions lib/dot/format.jst
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
({{=$format}} && {{=$formatType}} == '{{=$ruleType}}'
&& !(typeof {{=$format}} == 'function'
? {{? it.async}}
(async{{=$lvl}} ? {{=it.yieldAwait}} {{=$format}}({{=$data}}) : {{=$format}}({{=$data}}))
(async{{=$lvl}} ? await {{=$format}}({{=$data}}) : {{=$format}}({{=$data}}))
{{??}}
{{=$format}}({{=$data}})
{{?}}
@@ -71,7 +71,7 @@
{{ var $format = it.formats[$schema]; }}
{{? !$format }}
{{? $unknownFormats == 'ignore' }}
{{ console.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
{{ it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
{{# def.skipFormat }}
{{?? $allowUnknown && $unknownFormats.indexOf($schema) >= 0 }}
{{# def.skipFormat }}
@@ -97,7 +97,7 @@
if (!it.async) throw new Error('async format in sync schema');
var $formatRef = 'formats' + it.util.getProperty($schema) + '.validate';
}}
if (!({{=it.yieldAwait}} {{=$formatRef}}({{=$data}}))) {
if (!(await {{=$formatRef}}({{=$data}}))) {
{{??}}
if (!{{# def.checkFormat }}) {
{{?}}
73 changes: 73 additions & 0 deletions lib/dot/if.jst
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.setupNextLevel }}


{{## def.validateIfClause:_clause:
{{
$it.schema = it.schema['_clause'];
$it.schemaPath = it.schemaPath + '._clause';
$it.errSchemaPath = it.errSchemaPath + '/_clause';
}}
{{# def.insertSubschemaCode }}
{{=$valid}} = {{=$nextValid}};
{{? $thenPresent && $elsePresent }}
{{ $ifClause = 'ifClause' + $lvl; }}
var {{=$ifClause}} = '_clause';
{{??}}
{{ $ifClause = '\'_clause\''; }}
{{?}}
#}}

{{
var $thenSch = it.schema['then']
, $elseSch = it.schema['else']
, $thenPresent = $thenSch !== undefined && {{# def.nonEmptySchema:$thenSch }}
, $elsePresent = $elseSch !== undefined && {{# def.nonEmptySchema:$elseSch }}
, $currentBaseId = $it.baseId;
}}

{{? $thenPresent || $elsePresent }}
{{
var $ifClause;
$it.createErrors = false;
$it.schema = $schema;
$it.schemaPath = $schemaPath;
$it.errSchemaPath = $errSchemaPath;
}}
var {{=$errs}} = errors;
var {{=$valid}} = true;

{{# def.setCompositeRule }}
{{# def.insertSubschemaCode }}
{{ $it.createErrors = true; }}
{{# def.resetErrors }}
{{# def.resetCompositeRule }}

{{? $thenPresent }}
if ({{=$nextValid}}) {
{{# def.validateIfClause:then }}
}
{{? $elsePresent }}
else {
{{?}}
{{??}}
if (!{{=$nextValid}}) {
{{?}}

{{? $elsePresent }}
{{# def.validateIfClause:else }}
}
{{?}}

if (!{{=$valid}}) {
{{# def.extraError:'if' }}
}
{{? $breakOnError }} else { {{?}}
{{??}}
{{? $breakOnError }}
if (true) {
{{?}}
{{?}}

2 changes: 0 additions & 2 deletions lib/dot/items.jst
Original file line number Diff line number Diff line change
@@ -96,5 +96,3 @@ var {{=$valid}};
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
2 changes: 2 additions & 0 deletions lib/dot/multipleOf.jst
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.$data }}

{{# def.numberKeyword }}

var division{{=$lvl}};
if ({{?$isData}}
{{=$schemaValue}} !== undefined && (
24 changes: 17 additions & 7 deletions lib/dot/oneOf.jst
Original file line number Diff line number Diff line change
@@ -3,11 +3,17 @@
{{# def.setupKeyword }}
{{# def.setupNextLevel }}

var {{=$errs}} = errors;
var prevValid{{=$lvl}} = false;
var {{=$valid}} = false;
{{
var $currentBaseId = $it.baseId
, $prevValid = 'prevValid' + $lvl
, $passingSchemas = 'passingSchemas' + $lvl;
}}

var {{=$errs}} = errors
, {{=$prevValid}} = false
, {{=$valid}} = false
, {{=$passingSchemas}} = null;

{{ var $currentBaseId = $it.baseId; }}
{{# def.setCompositeRule }}

{{~ $schema:$sch:$i }}
@@ -24,13 +30,17 @@ var {{=$valid}} = false;
{{?}}

{{? $i }}
if ({{=$nextValid}} && prevValid{{=$lvl}})
if ({{=$nextValid}} && {{=$prevValid}}) {
{{=$valid}} = false;
else {
{{=$passingSchemas}} = [{{=$passingSchemas}}, {{=$i}}];
} else {
{{ $closingBraces += '}'; }}
{{?}}

if ({{=$nextValid}}) {{=$valid}} = prevValid{{=$lvl}} = true;
if ({{=$nextValid}}) {
{{=$valid}} = {{=$prevValid}} = true;
{{=$passingSchemas}} = {{=$i}};
}
{{~}}

{{# def.resetCompositeRule }}
96 changes: 7 additions & 89 deletions lib/dot/properties.jst
Original file line number Diff line number Diff line change
@@ -28,9 +28,9 @@
, $nextData = 'data' + $dataNxt
, $dataProperties = 'dataProperties' + $lvl;

var $schemaKeys = Object.keys($schema || {})
var $schemaKeys = Object.keys($schema || {}).filter(notProto)
, $pProperties = it.schema.patternProperties || {}
, $pPropertyKeys = Object.keys($pProperties)
, $pPropertyKeys = Object.keys($pProperties).filter(notProto)
, $aProperties = it.schema.additionalProperties
, $someProperties = $schemaKeys.length || $pPropertyKeys.length
, $noAdditional = $aProperties === false
@@ -42,13 +42,11 @@
, $currentBaseId = it.baseId;

var $required = it.schema.required;
if ($required && !(it.opts.v5 && $required.$data) && $required.length < it.opts.loopRequired)
if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) {
var $requiredHash = it.util.toHash($required);

if (it.opts.patternGroups) {
var $pgProperties = it.schema.patternGroups || {}
, $pgPropertyKeys = Object.keys($pgProperties);
}

function notProto(p) { return p !== '__proto__'; }
}}


@@ -63,8 +61,8 @@ var {{=$nextValid}} = true;
{{? $someProperties }}
var isAdditional{{=$lvl}} = !(false
{{? $schemaKeys.length }}
{{? $schemaKeys.length > 5 }}
|| validate.schema{{=$schemaPath}}[{{=$key}}]
{{? $schemaKeys.length > 8 }}
|| validate.schema{{=$schemaPath}}.hasOwnProperty({{=$key}})
{{??}}
{{~ $schemaKeys:$propertyKey }}
|| {{=$key}} == {{= it.util.toQuotedString($propertyKey) }}
@@ -76,11 +74,6 @@ var {{=$nextValid}} = true;
|| {{= it.usePattern($pProperty) }}.test({{=$key}})
{{~}}
{{?}}
{{? it.opts.patternGroups && $pgPropertyKeys.length }}
{{~ $pgPropertyKeys:$pgProperty:$i }}
|| {{= it.usePattern($pgProperty) }}.test({{=$key}})
{{~}}
{{?}}
);

if (isAdditional{{=$lvl}}) {
@@ -246,82 +239,7 @@ var {{=$nextValid}} = true;
{{?}}


{{? it.opts.patternGroups && $pgPropertyKeys.length }}
{{~ $pgPropertyKeys:$pgProperty }}
{{
var $pgSchema = $pgProperties[$pgProperty]
, $sch = $pgSchema.schema;
}}

{{? {{# def.nonEmptySchema:$sch}} }}
{{
$it.schema = $sch;
$it.schemaPath = it.schemaPath + '.patternGroups' + it.util.getProperty($pgProperty) + '.schema';
$it.errSchemaPath = it.errSchemaPath + '/patternGroups/'
+ it.util.escapeFragment($pgProperty)
+ '/schema';
}}

var pgPropCount{{=$lvl}} = 0;

{{# def.iterateProperties }}
if ({{= it.usePattern($pgProperty) }}.test({{=$key}})) {
pgPropCount{{=$lvl}}++;

{{
$it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers);
var $passData = $data + '[' + $key + ']';
$it.dataPathArr[$dataNxt] = $key;
}}

{{# def.generateSubschemaCode }}
{{# def.optimizeValidate }}

{{? $breakOnError }} if (!{{=$nextValid}}) break; {{?}}
}
{{? $breakOnError }} else {{=$nextValid}} = true; {{?}}
}

{{# def.ifResultValid }}

{{
var $pgMin = $pgSchema.minimum
, $pgMax = $pgSchema.maximum;
}}
{{? $pgMin !== undefined || $pgMax !== undefined }}
var {{=$valid}} = true;

{{ var $currErrSchemaPath = $errSchemaPath; }}

{{? $pgMin !== undefined }}
{{ var $limit = $pgMin, $reason = 'minimum', $moreOrLess = 'less'; }}
{{=$valid}} = pgPropCount{{=$lvl}} >= {{=$pgMin}};
{{ $errSchemaPath = it.errSchemaPath + '/patternGroups/minimum'; }}
{{# def.checkError:'patternGroups' }}
{{? $pgMax !== undefined }}
else
{{?}}
{{?}}

{{? $pgMax !== undefined }}
{{ var $limit = $pgMax, $reason = 'maximum', $moreOrLess = 'more'; }}
{{=$valid}} = pgPropCount{{=$lvl}} <= {{=$pgMax}};
{{ $errSchemaPath = it.errSchemaPath + '/patternGroups/maximum'; }}
{{# def.checkError:'patternGroups' }}
{{?}}

{{ $errSchemaPath = $currErrSchemaPath; }}

{{# def.ifValid }}
{{?}}
{{?}} {{ /* def.nonEmptySchema */ }}
{{~}}
{{?}}


{{? $breakOnError }}
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
6 changes: 2 additions & 4 deletions lib/dot/propertyNames.jst
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
{{# def.setupKeyword }}
{{# def.setupNextLevel }}

var {{=$errs}} = errors;

{{? {{# def.nonEmptySchema:$schema }} }}
{{
$it.schema = $schema;
@@ -22,8 +24,6 @@
, $currentBaseId = it.baseId;
}}

var {{=$errs}} = errors;

{{? $ownProperties }}
var {{=$dataProperties}} = undefined;
{{?}}
@@ -50,5 +50,3 @@
{{= $closingBraces }}
if ({{=$errs}} == errors) {
{{?}}

{{# def.cleanUp }}
8 changes: 4 additions & 4 deletions lib/dot/ref.jst
Original file line number Diff line number Diff line change
@@ -27,11 +27,11 @@
{{? $refVal === undefined }}
{{ var $message = it.MissingRefError.message(it.baseId, $schema); }}
{{? it.opts.missingRefs == 'fail' }}
{{ console.error($message); }}
{{ it.logger.error($message); }}
{{# def.error:'$ref' }}
{{? $breakOnError }} if (false) { {{?}}
{{?? it.opts.missingRefs == 'ignore' }}
{{ console.warn($message); }}
{{ it.logger.warn($message); }}
{{? $breakOnError }} if (true) { {{?}}
{{??}}
{{ throw new it.MissingRefError(it.baseId, $schema, $message); }}
@@ -50,7 +50,7 @@
{{?}}
{{??}}
{{
$async = $refVal.$async === true;
$async = $refVal.$async === true || (it.async && $refVal.$async !== false);
$refCode = $refVal.code;
}}
{{?}}
@@ -65,7 +65,7 @@
{{ if (!it.async) throw new Error('async schema referenced by sync schema'); }}
{{? $breakOnError }} var {{=$valid}}; {{?}}
try {
{{=it.yieldAwait}} {{=__callValidate}};
await {{=__callValidate}};
{{? $breakOnError }} {{=$valid}} = true; {{?}}
} catch (e) {
if (!(e instanceof ValidationError)) throw e;
42 changes: 33 additions & 9 deletions lib/dot/uniqueItems.jst
Original file line number Diff line number Diff line change
@@ -14,18 +14,42 @@
else {
{{?}}

var {{=$valid}} = true;
if ({{=$data}}.length > 1) {
var i = {{=$data}}.length, j;
outer:
for (;i--;) {
for (j = i; j--;) {
if (equal({{=$data}}[i], {{=$data}}[j])) {
var i = {{=$data}}.length
, {{=$valid}} = true
, j;
if (i > 1) {
{{
var $itemType = it.schema.items && it.schema.items.type
, $typeIsArray = Array.isArray($itemType);
}}
{{? !$itemType || $itemType == 'object' || $itemType == 'array' ||
($typeIsArray && ($itemType.indexOf('object') >= 0 || $itemType.indexOf('array') >= 0)) }}
outer:
for (;i--;) {
for (j = i; j--;) {
if (equal({{=$data}}[i], {{=$data}}[j])) {
{{=$valid}} = false;
break outer;
}
}
}
{{??}}
var itemIndices = {}, item;
for (;i--;) {
var item = {{=$data}}[i];
{{ var $method = 'checkDataType' + ($typeIsArray ? 's' : ''); }}
if ({{= it.util[$method]($itemType, 'item', it.opts.strictNumbers, true) }}) continue;
{{? $typeIsArray}}
if (typeof item == 'string') item = '"' + item;
{{?}}
if (typeof itemIndices[item] == 'number') {
{{=$valid}} = false;
break outer;
j = itemIndices[item];
break;
}
itemIndices[item] = i;
}
}
{{?}}
}

{{? $isData }} } {{?}}
Loading