@hyperjump/json-schema 0.23.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +487 -119
- package/draft-04/additionalItems.js +29 -0
- package/{lib/keywords → draft-04}/dependencies.js +9 -5
- package/draft-04/exclusiveMaximum.js +5 -0
- package/draft-04/exclusiveMinimum.js +5 -0
- package/draft-04/id.js +1 -0
- package/draft-04/index.d.ts +43 -0
- package/draft-04/index.js +69 -0
- package/draft-04/items.js +35 -0
- package/draft-04/maximum.js +26 -0
- package/draft-04/minimum.js +25 -0
- package/draft-04/ref.js +1 -0
- package/draft-04/schema.js +149 -0
- package/draft-06/contains.js +13 -0
- package/draft-06/index.d.ts +47 -0
- package/draft-06/index.js +66 -0
- package/draft-06/schema.js +154 -0
- package/{lib/draft-07.d.ts → draft-07/index.d.ts} +21 -19
- package/draft-07/index.js +72 -0
- package/draft-07/schema.js +172 -0
- package/{lib/keywords/contains-minContains-maxContains.js → draft-2019-09/contains.js} +14 -7
- package/{lib/draft-2019-09.d.ts → draft-2019-09/index.d.ts} +24 -22
- package/draft-2019-09/index.js +117 -0
- package/draft-2019-09/meta/applicator.js +55 -0
- package/draft-2019-09/meta/content.js +17 -0
- package/draft-2019-09/meta/core.js +57 -0
- package/draft-2019-09/meta/format.js +14 -0
- package/draft-2019-09/meta/meta-data.js +37 -0
- package/draft-2019-09/meta/validation.js +98 -0
- package/draft-2019-09/recursiveAnchor.js +1 -0
- package/draft-2019-09/recursiveRef.js +19 -0
- package/draft-2019-09/schema.js +42 -0
- package/draft-2020-12/dynamicAnchor.js +1 -0
- package/draft-2020-12/dynamicRef.js +35 -0
- package/draft-2020-12/index.d.ts +66 -0
- package/draft-2020-12/index.js +124 -0
- package/draft-2020-12/meta/applicator.js +46 -0
- package/draft-2020-12/meta/content.js +14 -0
- package/draft-2020-12/meta/core.js +54 -0
- package/draft-2020-12/meta/format-annotation.js +11 -0
- package/draft-2020-12/meta/format-assertion.js +11 -0
- package/draft-2020-12/meta/meta-data.js +34 -0
- package/draft-2020-12/meta/unevaluated.js +12 -0
- package/draft-2020-12/meta/validation.js +95 -0
- package/draft-2020-12/schema.js +44 -0
- package/lib/common.d.ts +5 -1
- package/lib/common.js +80 -9
- package/lib/configuration.d.ts +9 -0
- package/lib/configuration.js +18 -0
- package/lib/core.d.ts +48 -0
- package/lib/core.js +102 -0
- package/lib/experimental.d.ts +8 -0
- package/lib/experimental.js +4 -0
- package/lib/fetch.browser.js +1 -0
- package/lib/fetch.js +19 -0
- package/lib/index.d.ts +11 -42
- package/lib/index.js +130 -23
- package/lib/instance.d.ts +75 -0
- package/lib/instance.js +58 -0
- package/lib/invalid-schema-error.d.ts +8 -0
- package/lib/invalid-schema-error.js +7 -0
- package/lib/keywords/additionalProperties.js +19 -17
- package/lib/keywords/allOf.js +10 -7
- package/lib/keywords/anchor.js +1 -0
- package/lib/keywords/anyOf.js +10 -7
- package/lib/keywords/comment.js +4 -0
- package/lib/keywords/const.js +6 -3
- package/lib/keywords/contains.js +48 -5
- package/lib/keywords/contentEncoding.js +4 -0
- package/lib/keywords/contentMediaType.js +4 -0
- package/lib/keywords/contentSchema.js +4 -0
- package/lib/keywords/default.js +4 -0
- package/lib/keywords/definitions.js +7 -4
- package/lib/keywords/dependentRequired.js +6 -3
- package/lib/keywords/dependentSchemas.js +10 -6
- package/lib/keywords/deprecated.js +4 -0
- package/lib/keywords/description.js +4 -0
- package/lib/keywords/dynamicAnchor.js +1 -0
- package/lib/keywords/dynamicRef.js +13 -17
- package/lib/keywords/else.js +16 -11
- package/lib/keywords/enum.js +6 -3
- package/lib/keywords/examples.js +4 -0
- package/lib/keywords/exclusiveMaximum.js +5 -2
- package/lib/keywords/exclusiveMinimum.js +5 -2
- package/lib/keywords/format.js +4 -0
- package/lib/keywords/id.js +1 -0
- package/lib/keywords/if.js +8 -6
- package/lib/keywords/items.js +17 -19
- package/lib/keywords/maxContains.js +4 -0
- package/lib/keywords/maxItems.js +5 -2
- package/lib/keywords/maxLength.js +5 -2
- package/lib/keywords/maxProperties.js +5 -2
- package/lib/keywords/maximum.js +5 -2
- package/lib/keywords/meta-data.js +4 -0
- package/lib/keywords/minContains.js +4 -0
- package/lib/keywords/minItems.js +5 -2
- package/lib/keywords/minLength.js +5 -2
- package/lib/keywords/minProperties.js +5 -2
- package/lib/keywords/minimum.js +5 -2
- package/lib/keywords/multipleOf.js +5 -2
- package/lib/keywords/not.js +6 -4
- package/lib/keywords/oneOf.js +9 -6
- package/lib/keywords/pattern.js +5 -2
- package/lib/keywords/patternProperties.js +9 -5
- package/lib/keywords/prefixItems.js +28 -0
- package/lib/keywords/properties.js +11 -6
- package/lib/keywords/propertyDependencies.js +49 -0
- package/lib/keywords/propertyNames.js +7 -4
- package/lib/keywords/readOnly.js +4 -0
- package/lib/keywords/ref.js +9 -6
- package/lib/keywords/requireAllExcept.js +24 -0
- package/lib/keywords/required.js +5 -2
- package/lib/keywords/then.js +16 -11
- package/lib/keywords/title.js +4 -0
- package/lib/keywords/type.js +5 -2
- package/lib/keywords/unevaluatedItems.js +9 -5
- package/lib/keywords/unevaluatedProperties.js +9 -5
- package/lib/keywords/uniqueItems.js +6 -3
- package/lib/keywords/validation.js +123 -0
- package/lib/keywords/vocabulary.js +1 -0
- package/lib/keywords/writeOnly.js +4 -0
- package/lib/keywords.d.ts +19 -0
- package/lib/keywords.js +59 -0
- package/lib/media-types.d.ts +9 -0
- package/lib/media-types.js +26 -0
- package/lib/openapi.js +29 -0
- package/lib/pubsub.js +42 -0
- package/lib/reference.d.ts +11 -0
- package/lib/reference.js +11 -0
- package/lib/schema.d.ts +64 -0
- package/lib/schema.js +308 -0
- package/openapi-3-0/dialect.js +174 -0
- package/openapi-3-0/discriminator.js +4 -0
- package/openapi-3-0/example.js +4 -0
- package/openapi-3-0/externalDocs.js +4 -0
- package/openapi-3-0/index.d.ts +60 -0
- package/openapi-3-0/index.js +77 -0
- package/openapi-3-0/nullable.js +4 -0
- package/openapi-3-0/schema/2021-09-28.js +1404 -0
- package/openapi-3-0/type.js +16 -0
- package/openapi-3-0/xml.js +4 -0
- package/openapi-3-1/dialect/base.js +21 -0
- package/openapi-3-1/index.d.ts +88 -0
- package/openapi-3-1/index.js +48 -0
- package/openapi-3-1/meta/base.js +76 -0
- package/openapi-3-1/schema/2022-10-07.js +1440 -0
- package/openapi-3-1/schema-base/2022-10-07.js +23 -0
- package/package.json +34 -22
- package/{lib/draft-2020-12.d.ts → stable/index.d.ts} +26 -24
- package/stable/index.js +118 -0
- package/stable/meta/applicator.js +49 -0
- package/stable/meta/content.js +12 -0
- package/stable/meta/core.js +49 -0
- package/stable/meta/format-annotation.js +10 -0
- package/stable/meta/format-assertion.js +10 -0
- package/stable/meta/meta-data.js +16 -0
- package/stable/meta/unevaluated.js +11 -0
- package/stable/meta/validation.js +67 -0
- package/stable/validation.js +24 -0
- package/dist/json-schema-amd.js +0 -6644
- package/dist/json-schema-amd.js.map +0 -1
- package/dist/json-schema-amd.min.js +0 -2
- package/dist/json-schema-amd.min.js.map +0 -1
- package/dist/json-schema-cjs.js +0 -6642
- package/dist/json-schema-cjs.js.map +0 -1
- package/dist/json-schema-cjs.min.js +0 -2
- package/dist/json-schema-cjs.min.js.map +0 -1
- package/dist/json-schema-esm.js +0 -6638
- package/dist/json-schema-esm.js.map +0 -1
- package/dist/json-schema-esm.min.js +0 -2
- package/dist/json-schema-esm.min.js.map +0 -1
- package/dist/json-schema-iife.js +0 -6647
- package/dist/json-schema-iife.js.map +0 -1
- package/dist/json-schema-iife.min.js +0 -2
- package/dist/json-schema-iife.min.js.map +0 -1
- package/dist/json-schema-system.js +0 -6645
- package/dist/json-schema-system.js.map +0 -1
- package/dist/json-schema-system.min.js +0 -2
- package/dist/json-schema-system.min.js.map +0 -1
- package/dist/json-schema-umd.js +0 -6648
- package/dist/json-schema-umd.js.map +0 -1
- package/dist/json-schema-umd.min.js +0 -2
- package/dist/json-schema-umd.min.js.map +0 -1
- package/lib/draft-04.d.ts +0 -41
- package/lib/draft-04.js +0 -46
- package/lib/draft-06.d.ts +0 -45
- package/lib/draft-06.js +0 -51
- package/lib/draft-07.js +0 -55
- package/lib/draft-2019-09.js +0 -92
- package/lib/draft-2020-12.js +0 -103
- package/lib/index.mjs +0 -19
- package/lib/keywords/additionalItems.js +0 -27
- package/lib/keywords/additionalItems6.js +0 -23
- package/lib/keywords/additionalProperties6.js +0 -28
- package/lib/keywords/index.js +0 -53
- package/lib/keywords/items202012.js +0 -23
- package/lib/keywords/maximum-exclusiveMaximum.js +0 -20
- package/lib/keywords/minimum-exclusiveMinimum.js +0 -20
- package/lib/keywords/tupleItems.js +0 -24
- package/meta/draft/2019-09/meta/applicator.js +0 -55
- package/meta/draft/2019-09/meta/content.js +0 -17
- package/meta/draft/2019-09/meta/core.js +0 -57
- package/meta/draft/2019-09/meta/format.js +0 -14
- package/meta/draft/2019-09/meta/meta-data.js +0 -37
- package/meta/draft/2019-09/meta/validation.js +0 -98
- package/meta/draft/2019-09/schema.js +0 -42
- package/meta/draft/2020-12/meta/applicator.js +0 -49
- package/meta/draft/2020-12/meta/content.js +0 -17
- package/meta/draft/2020-12/meta/core.js +0 -57
- package/meta/draft/2020-12/meta/format-annotation.js +0 -14
- package/meta/draft/2020-12/meta/format-assertion.js +0 -14
- package/meta/draft/2020-12/meta/meta-data.js +0 -37
- package/meta/draft/2020-12/meta/unevaluated.js +0 -15
- package/meta/draft/2020-12/meta/validation.js +0 -98
- package/meta/draft/2020-12/schema.js +0 -44
- package/meta/draft-04/schema.js +0 -149
- package/meta/draft-06/schema.js +0 -154
- package/meta/draft-07/schema.js +0 -172
package/README.md
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
# Hyperjump - JSON Schema
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
* Create your own output format with [JSC](https://github.com/hyperjump-io/json-schema-core)
|
|
16
|
-
* Load schemas from filesystem (file://), network (http(s)://), or JavaScript
|
|
17
|
-
* Build non-validation JSON-Schema based tools with [JSC](https://github.com/hyperjump-io/json-schema-core)
|
|
1
|
+
# Hyperjump - JSON Schema
|
|
2
|
+
|
|
3
|
+
A collection of modules for working with JSON Schemas.
|
|
4
|
+
|
|
5
|
+
* Validate JSON-compatible values against a JSON Schema
|
|
6
|
+
* Dialects: draft-2020-12, draft-2019-09, draft-07, draft-06, draft-04
|
|
7
|
+
* OpenAPI
|
|
8
|
+
* Versions/Dialects: 3.0, 3.1
|
|
9
|
+
* Validate an OpenAPI document
|
|
10
|
+
* Validate values against a schema from an OpenAPI document
|
|
11
|
+
* Schemas can reference other schemas using a different dialect
|
|
12
|
+
* Work directly with schemas on the filesystem or HTTP
|
|
13
|
+
* Create custom keywords, vocabularies, and dialects
|
|
14
|
+
* Provides utilities for building non-validation JSON Schema tooling
|
|
18
15
|
|
|
19
16
|
## Install
|
|
20
|
-
|
|
21
|
-
TypeScript, and browsers.
|
|
17
|
+
Includes support for node.js (ES Modules, TypeScript) and browsers.
|
|
22
18
|
|
|
23
19
|
### Node.js
|
|
24
20
|
```bash
|
|
@@ -26,7 +22,7 @@ npm install @hyperjump/json-schema
|
|
|
26
22
|
```
|
|
27
23
|
|
|
28
24
|
### Browser
|
|
29
|
-
When in a browser context,
|
|
25
|
+
When in a browser context, this library is designed to use the browser's `fetch`
|
|
30
26
|
implementation instead of a node.js fetch clone. The Webpack bundler does this
|
|
31
27
|
properly without any extra configuration, but if you are using the Rollup
|
|
32
28
|
bundler you will need to include the `browser: true` option in your Rollup
|
|
@@ -36,72 +32,187 @@ configuration.
|
|
|
36
32
|
plugins: [
|
|
37
33
|
resolve({
|
|
38
34
|
browser: true
|
|
39
|
-
})
|
|
40
|
-
commonjs()
|
|
35
|
+
})
|
|
41
36
|
]
|
|
42
37
|
```
|
|
43
38
|
|
|
44
39
|
### Versioning
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
The API for this library is divided into two categories: Stable and
|
|
41
|
+
Experimental. The Stable API strictly follows semantic versioning, but the
|
|
42
|
+
Experimental API may have backward-incompatible changes between minor versions.
|
|
48
43
|
|
|
49
44
|
## Usage
|
|
45
|
+
This library supports many versions of JSON Schema. Use the pattern
|
|
46
|
+
`@hyperjump/json-schema/*` to import the version you need.
|
|
47
|
+
|
|
50
48
|
```javascript
|
|
51
|
-
|
|
49
|
+
import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
|
|
50
|
+
```
|
|
52
51
|
|
|
52
|
+
You can import support for additional versions as needed.
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"type": "string"
|
|
59
|
-
};
|
|
60
|
-
JsonSchema.add(schemaJson);
|
|
61
|
-
const schema = await JsonSchema.get("http://example.com/schemas/string");
|
|
54
|
+
```javascript
|
|
55
|
+
import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
|
|
56
|
+
import "@hyperjump/json-schema/draft-07";
|
|
57
|
+
```
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
**Note**: The default export (`@hyperjump/json-schema`) is reserved for the
|
|
60
|
+
stable version of JSON Schema that will hopefully be released in 2023.
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
**Validate schema from JavaScript**
|
|
63
|
+
```javascript
|
|
64
|
+
addSchema({
|
|
65
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
66
|
+
type: "string"
|
|
67
|
+
}, "http://example.com/schemas/string");
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
const output = await JsonSchema.validate(schema, "foo");
|
|
69
|
+
const output = await validate("http://example.com/schemas/string", "foo");
|
|
71
70
|
if (output.valid) {
|
|
72
71
|
console.log("Instance is valid :-)");
|
|
73
72
|
} else {
|
|
74
73
|
console.log("Instance is invalid :-(");
|
|
75
74
|
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Compile schema**
|
|
78
|
+
|
|
79
|
+
If you need to validate multiple instances against the same schema, you can
|
|
80
|
+
compile the schema into a reusable validation function.
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const isString = await validate("http://example.com/schemas/string");
|
|
84
|
+
const output1 = isString("foo");
|
|
85
|
+
const output2 = isString(42);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Fetching schemas**
|
|
89
|
+
|
|
90
|
+
You can fetch schemas from the web or from the file system, but when fetching
|
|
91
|
+
from the file system, there are limitations for security reasons. If your schema
|
|
92
|
+
has an identifier with an http(s) scheme (**https**://example.com), it's not
|
|
93
|
+
allowed to reference schemas with a file scheme
|
|
94
|
+
(**file**:///path/to/my/schemas).
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
const output = await validate("http://example.com/schemas/string", "foo");
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
const output = await validate(`file://${__dirname}/string.schema.json`, "foo");
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Media type plugins**
|
|
105
|
+
|
|
106
|
+
There is a plugin system for adding support for different media types. By
|
|
107
|
+
default it's configured to accept schemas that have the
|
|
108
|
+
`application/schema+json` Content-Type (web) or a `.schema.json` file extension
|
|
109
|
+
(filesystem). If, for example, you want to fetch schemas that are written in
|
|
110
|
+
YAML, you can add a MediaTypePlugin to support that.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
import { addMediaTypePlugin, validate } from "@hyperjump/json-schema/draft-2020-12";
|
|
114
|
+
import YAML from "yaml";
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// Add support for JSON Schemas written in YAML
|
|
118
|
+
addMediaTypePlugin("application/schema+yaml", {
|
|
119
|
+
parse: async (response) => [YAML.parse(await response.text()), undefined],
|
|
120
|
+
matcher: (path) => path.endsWith(".schema.yaml")
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Example: Fetch schema with Content-Type: application/schema+yaml from the web
|
|
124
|
+
const isString = await validate("http://example.com/schemas/string");
|
|
125
|
+
|
|
126
|
+
// Example: Fetch from file with JSON Schema YAML file extension
|
|
127
|
+
const isString = await validate(`file://${__dirname}/string.schema.yaml`);
|
|
76
128
|
|
|
77
|
-
//
|
|
78
|
-
const isString = await JsonSchema.validate(schema);
|
|
129
|
+
// Then validate against your schema like normal
|
|
79
130
|
const output = isString("foo");
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Open API**
|
|
134
|
+
|
|
135
|
+
The OpenAPI 3.0 and 3.1 meta-schemas are pre-loaded and the OpenAPI JSON Schema
|
|
136
|
+
dialects for each of those versions is supported. A document with a Content-Type
|
|
137
|
+
of `application/openapi+json` (web) or a file extension of `openapi.json`
|
|
138
|
+
(filesystem) is understood as an OpenAPI document.
|
|
80
139
|
|
|
81
|
-
|
|
82
|
-
|
|
140
|
+
Use the pattern `@hyperjump/json-schema/*` to import the version you need. The
|
|
141
|
+
available versions are `openapi-3-0` for 3.0 and `openapi-3-1` for 3.1.
|
|
83
142
|
|
|
84
|
-
|
|
85
|
-
|
|
143
|
+
YAML support isn't built in, but you can add it by writing a MediaTypePlugin.
|
|
144
|
+
You can use the one at `lib/openapi.js` as an example and replacing the JSON
|
|
145
|
+
parts with YAML.
|
|
86
146
|
|
|
87
|
-
|
|
88
|
-
|
|
147
|
+
```javascript
|
|
148
|
+
import { addSchema, validate } from "@hyperjump/json-schema/openapi-3-1";
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
// Validate an OpenAPI document
|
|
152
|
+
const output = await validate("https://spec.openapis.org/oas/3.1/schema-base", openapi);
|
|
153
|
+
|
|
154
|
+
// Validate an instance against a schema in an OpenAPI document
|
|
155
|
+
const output = await validate(`file://${__dirname}/example.openapi.json#/components/schemas/foo`, 42);
|
|
89
156
|
```
|
|
90
157
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
158
|
+
## API
|
|
159
|
+
These are available from any of the exports that refer to a version of JSON
|
|
160
|
+
Schema, such as `@hyperjump/json-schema/draft-2020-12`.
|
|
161
|
+
|
|
162
|
+
* **addSchema**: (schema: object, retrievalUri?: string, defaultDialectId?: string) => void
|
|
163
|
+
|
|
164
|
+
Load a schema manually rather than fetching it from the filesystem or over
|
|
165
|
+
the network.
|
|
166
|
+
* **validate**: (schemaURI: string, instance: any, outputFormat: OutputFormat = FLAG) => Promise<OutputUnit>
|
|
167
|
+
|
|
168
|
+
Validate an instance against a schema. This function is curried to allow
|
|
169
|
+
compiling the schema once and applying it to multiple instances.
|
|
170
|
+
* **validate**: (schemaURI: string) => Promise<(instance: any, outputFormat: OutputFormat = FLAG) => OutputUnit>
|
|
171
|
+
|
|
172
|
+
Compiling a schema to a validation function.
|
|
173
|
+
* **FLAG**: "FLAG"
|
|
97
174
|
|
|
98
|
-
|
|
175
|
+
An identifier for the `FLAG` output format as defined by the 2019-09 and
|
|
176
|
+
2020-12 specifications.
|
|
177
|
+
* **InvalidSchemaError**: Error & { output: OutputUnit }
|
|
178
|
+
|
|
179
|
+
This error is thrown if the schema being compiled is found to be invalid.
|
|
180
|
+
The `output` field contains an `OutputUnit` with information about the
|
|
181
|
+
error. You can use the `setMetaOutputFormat` configuration to set the output
|
|
182
|
+
format that is returned in `output`.
|
|
183
|
+
* **setMetaOutputFormat**: (outputFormat: OutputFormat) => void
|
|
184
|
+
|
|
185
|
+
Set the output format used for validating schemas.
|
|
186
|
+
* **getMetaOutputFormat**: () => OutputFormat
|
|
187
|
+
|
|
188
|
+
Get the output format used for validating schemas.
|
|
189
|
+
* **setShouldMetaValidate**: (isEnabled: boolean) => void
|
|
190
|
+
|
|
191
|
+
Enable or disable validating schemas.
|
|
192
|
+
* **getShouldMetaValidate**: (isEnabled: boolean) => void
|
|
193
|
+
|
|
194
|
+
Determine if validating schemas is enabled.
|
|
195
|
+
* **addMediaTypePlugin**: (contentType: string, plugin: MediaTypePlugin) => void
|
|
99
196
|
|
|
100
197
|
Add a custom media type handler to support things like YAML or to change the
|
|
101
198
|
way JSON is supported.
|
|
199
|
+
|
|
200
|
+
**Type Definitions**
|
|
201
|
+
|
|
202
|
+
The following types are used in the above definitions
|
|
203
|
+
|
|
204
|
+
* **OutputFormat**: **FLAG**
|
|
205
|
+
|
|
206
|
+
Only the `FLAG` output format is part of the Stable API. Additional output
|
|
207
|
+
formats are included as part of the Experimental API.
|
|
208
|
+
* **OutputUnit**: { valid: boolean }
|
|
209
|
+
|
|
210
|
+
Output is an experimental feature of the JSON Schema specification. There
|
|
211
|
+
may be additional fields present in the OutputUnit, but only the `valid`
|
|
212
|
+
property should be considered part of the Stable API.
|
|
102
213
|
* **MediaTypePlugin**: object
|
|
103
214
|
|
|
104
|
-
* parse: (response: Response, mediaTypeParameters: object) => [
|
|
215
|
+
* parse: (response: Response, mediaTypeParameters: object) => [object | boolean, string?]
|
|
105
216
|
|
|
106
217
|
Given a fetch Response object, parse the body of the request. Return the
|
|
107
218
|
parsed schema and an optional default dialectId.
|
|
@@ -110,96 +221,353 @@ MediaTypePlugin to support that.
|
|
|
110
221
|
Given a filesystem path, return whether or not the file should be
|
|
111
222
|
considered a member of this media type.
|
|
112
223
|
|
|
224
|
+
## Experimental
|
|
225
|
+
The JSON Schema specification includes several features that are experimental in
|
|
226
|
+
nature including the Vocabulary System, Output Formats, and Annotations. This
|
|
227
|
+
implementation aims to support only the latest version of experimental features
|
|
228
|
+
as they evolve. There will not be a major version bump if there needs to be
|
|
229
|
+
backward incompatible changes to the Experimental API.
|
|
230
|
+
|
|
231
|
+
### Usage
|
|
232
|
+
All experimental features are segregated into exports that include the word
|
|
233
|
+
"experimental" so you never accidentally depend on something that could change
|
|
234
|
+
or be removed in future releases.
|
|
235
|
+
|
|
113
236
|
```javascript
|
|
114
|
-
|
|
115
|
-
|
|
237
|
+
import { BASIC } from "@hyperjump/json-schema/experimental";
|
|
238
|
+
```
|
|
116
239
|
|
|
240
|
+
**Change the validation output format**
|
|
117
241
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
parse: async (response) => [YAML.parse(await response.text()), undefined],
|
|
121
|
-
matcher: (path) => path.endsWith(".schema.yaml")
|
|
122
|
-
});
|
|
242
|
+
The `FLAG` output format isn't very informative. You can change the output
|
|
243
|
+
format used for validation to get more information.
|
|
123
244
|
|
|
124
|
-
|
|
125
|
-
const
|
|
245
|
+
```javascript
|
|
246
|
+
const output = await validate("https://example.com/schema1", 42, BASIC);
|
|
247
|
+
```
|
|
126
248
|
|
|
127
|
-
|
|
128
|
-
const schema = await JsonSchema.get("file:///path/to/my/schemas/string.schema.yaml");
|
|
249
|
+
**Change the schema validation output format**
|
|
129
250
|
|
|
130
|
-
|
|
251
|
+
The output format used for validating schemas can be changed as well.
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
setMetaOutputFormat(BASIC);
|
|
255
|
+
try {
|
|
256
|
+
const output = await validate("https://example.com/invalid-schema");
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.log(error.output);
|
|
259
|
+
}
|
|
131
260
|
```
|
|
132
261
|
|
|
133
|
-
|
|
134
|
-
Although the package is written in JavaScript, type definitions are included for
|
|
135
|
-
TypeScript support. The following example shows the types you might want to
|
|
136
|
-
know.
|
|
262
|
+
**Keywords, Vocabularies, and Dialects**
|
|
137
263
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
264
|
+
In order to create and use a custom keyword, you need to define your keyword's
|
|
265
|
+
behavior, create a vocabulary that includes that keyword, and then create a
|
|
266
|
+
dialect that includes your vocabulary.
|
|
141
267
|
|
|
268
|
+
```javascript
|
|
269
|
+
import { addSchema, validate } from "@hyperjump/json-schema/draft-2020-12";
|
|
270
|
+
import { addKeyword, defineVocabulary, Validation } from "@hyperjump/json-schema/experimental";
|
|
271
|
+
import * as Schema from "@hyperjump/json-schema/schema/experimental";
|
|
142
272
|
|
|
143
|
-
const schemaJson: Draft202012Schema = {
|
|
144
|
-
"$id": "https://json-schema.hyperjump.io/schema",
|
|
145
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
146
273
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
274
|
+
// Define a keyword that's an array of schemas that are applied sequentially
|
|
275
|
+
// using implication: A -> B -> C -> D
|
|
276
|
+
addKeyword({
|
|
277
|
+
id: "https://example.com/keyword/implication",
|
|
150
278
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
} else {
|
|
160
|
-
console.log(error);
|
|
279
|
+
compile: (schema, ast) => {
|
|
280
|
+
return Schema.map(async (itemSchema) => Validation.compile(await itemSchema, ast), schema);
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
interpret: (implies, instance, ast, dynamicAnchors) => {
|
|
284
|
+
return implies.reduce((acc, schema) => {
|
|
285
|
+
return !acc || Validation.interpret(schema, instance, ast, dynamicAnchors);
|
|
286
|
+
}, true);
|
|
161
287
|
}
|
|
162
|
-
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Create a vocabulary with this keyword and call it "implies"
|
|
291
|
+
defineVocabulary("https://example.com/vocab/logic", {
|
|
292
|
+
"implies": "https://example.com/keyword/implication"
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Create a vocabulary schema for this vocabulary
|
|
296
|
+
addSchema({
|
|
297
|
+
"$id": "https://example.com/meta/logic",
|
|
298
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
299
|
+
|
|
300
|
+
"$dynamicAnchor": "meta",
|
|
301
|
+
"properties": {
|
|
302
|
+
"implies": {
|
|
303
|
+
"type": "array",
|
|
304
|
+
"items": { "$dynamicRef": "meta" },
|
|
305
|
+
"minItems": 2
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Create a dialect schema adding this vocabulary to the standard JSON Schema
|
|
311
|
+
// vocabularies
|
|
312
|
+
addSchema({
|
|
313
|
+
"$id": "https://example.com/dialect/logic",
|
|
314
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
315
|
+
|
|
316
|
+
"$vocabulary": {
|
|
317
|
+
"https://json-schema.org/vocab/core": true,
|
|
318
|
+
"https://json-schema.org/vocab/applicator": true,
|
|
319
|
+
"https://json-schema.org/vocab/unevaluated": true,
|
|
320
|
+
"https://json-schema.org/vocab/validation": true,
|
|
321
|
+
"https://json-schema.org/vocab/meta-data": true,
|
|
322
|
+
"https://json-schema.org/vocab/format-annotation": true,
|
|
323
|
+
"https://json-schema.org/vocab/content": true,
|
|
324
|
+
"https://example.com/vocab/logic": true
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
"$dynamicAnchor": "meta",
|
|
328
|
+
|
|
329
|
+
"allOf": [
|
|
330
|
+
{ "$ref": "https://json-schema.org/draft/2020-12/schema" },
|
|
331
|
+
{ "$ref": "/meta/logic" }
|
|
332
|
+
]
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Use your dialect to validate a JSON instance
|
|
336
|
+
addSchema({
|
|
337
|
+
"$schema": "https://example.com/dialect/logic",
|
|
338
|
+
|
|
339
|
+
"type": "number",
|
|
340
|
+
"implies": [
|
|
341
|
+
{ "minimum": 10 },
|
|
342
|
+
{ "multipleOf": 2 }
|
|
343
|
+
]
|
|
344
|
+
}, "https://example.com/schema1");
|
|
345
|
+
const output = await validate("https://example.com/schema1", 42);
|
|
163
346
|
```
|
|
164
347
|
|
|
165
|
-
|
|
166
|
-
* **add**: (schema: object, url?: URI, dialectId?: string) => SDoc
|
|
348
|
+
**Custom Meta Schema**
|
|
167
349
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
* **get**: (url: URI, contextDoc?: SDoc, recursive: boolean = false) => Promise<SDoc>
|
|
350
|
+
You can use a custom meta-schema to restrict users to a subset of JSON Schema
|
|
351
|
+
functionality. This example requires that no unknown keywords are used in the
|
|
352
|
+
schema.
|
|
172
353
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
354
|
+
```javascript
|
|
355
|
+
addSchema({
|
|
356
|
+
"$id": "https://example.com/meta-schema1",
|
|
357
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
176
358
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
359
|
+
"$vocabulary": {
|
|
360
|
+
"https://json-schema.org/draft/2020-12/vocab/core": true,
|
|
361
|
+
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
|
|
362
|
+
"https://json-schema.org/draft/2020-12/vocab/unevaluated": true,
|
|
363
|
+
"https://json-schema.org/draft/2020-12/vocab/validation": true,
|
|
364
|
+
"https://json-schema.org/draft/2020-12/vocab/meta-data": true,
|
|
365
|
+
"https://json-schema.org/draft/2020-12/vocab/format-annotation": true,
|
|
366
|
+
"https://json-schema.org/draft/2020-12/vocab/content": true
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
"$dynamicAnchor": "meta",
|
|
370
|
+
|
|
371
|
+
"$ref": "https://json-schema.org/draft/2020-12/schema",
|
|
372
|
+
"unevaluatedProperties": false
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
addSchema({
|
|
376
|
+
$schema: "https://example.com/meta-schema1",
|
|
377
|
+
type: "number",
|
|
378
|
+
foo: 42
|
|
379
|
+
}, "https://example.com/schema1");
|
|
380
|
+
|
|
381
|
+
const output = await validate("https://example.com/schema1", 42); // Expect InvalidSchemaError
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### API
|
|
385
|
+
These are available from the `@hyperjump/json-schema/experimental` export.
|
|
180
386
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
387
|
+
* **compile**: (schema: SchemaDocument) => Promise<CompiledSchema>
|
|
388
|
+
|
|
389
|
+
Return a compiled schema. This is useful if you're creating tooling for
|
|
390
|
+
something other than validation.
|
|
391
|
+
* **interpret**: (schema: CompiledSchema, instance: Instance, outputFormat: OutputFormat = BASIC) => OutputUnit
|
|
184
392
|
|
|
185
393
|
A curried function for validating an instance against a compiled schema.
|
|
186
|
-
|
|
394
|
+
This can be useful for creating custom output formats.
|
|
395
|
+
* **addKeyword**: (keywordHandler: Keyword) => void
|
|
396
|
+
|
|
397
|
+
Define a keyword for use in a vocabulary.
|
|
398
|
+
* **defineVocabulary**: (id: string, keywords: { [keyword: string]: string }) => void
|
|
399
|
+
|
|
400
|
+
Define a vocabulary that maps keyword name to keyword URIs defined using
|
|
401
|
+
`addKeyword`.
|
|
402
|
+
* **getKeyword**: (keywordId: string) => Keyword
|
|
403
|
+
|
|
404
|
+
Get a keyword object by its URI. This is useful for building non-validation
|
|
405
|
+
tooling.
|
|
406
|
+
* **getKeywordName**: (dialectId: string, keywordId: string) => string
|
|
407
|
+
|
|
408
|
+
Determine a keyword's name given its URI a dialect URI. This is useful when
|
|
409
|
+
defining a keyword that depends on the value of another keyword (such as how
|
|
410
|
+
`contains` depends on `minContains` and `maxContains`).
|
|
411
|
+
* **loadDialect**: (dialectId: string, dialect: { [vocabularyId: string] }) => void
|
|
412
|
+
|
|
413
|
+
Define a dialect. In most cases, dialects are loaded automatically from the
|
|
414
|
+
`$vocabulary` keyword in the meta-schema. The only time you would need to
|
|
415
|
+
load a dialect manually is if you're creating a distinct version of JSON
|
|
416
|
+
Schema rather than creating a dialect of an existing version of JSON Schema.
|
|
417
|
+
* **Validation**: Keyword
|
|
418
|
+
|
|
419
|
+
A Keyword object that represents a "validate" operation. You would use this
|
|
420
|
+
for compiling and evaluating sub-schemas when defining a custom keyword.
|
|
421
|
+
|
|
422
|
+
**Type Definitions**
|
|
423
|
+
|
|
424
|
+
The following types are used in the above definitions
|
|
425
|
+
|
|
426
|
+
* **OutputFormat**: **FLAG** | **BASIC** | **DETAILED** | **VERBOSE**
|
|
427
|
+
|
|
428
|
+
In addition to the `FLAG` output format in the Stable API, the Experimental
|
|
429
|
+
API includes support for the `BASIC`, `DETAILED`, and `VERBOSE` formats as
|
|
430
|
+
specified in the 2019-09 specification (with some minor customizations).
|
|
431
|
+
This implementation doesn't include annotations or human readable error
|
|
432
|
+
messages. The output can be processed to create human readable error
|
|
433
|
+
messages as needed.
|
|
434
|
+
* **Keyword**: object
|
|
435
|
+
* id: string
|
|
436
|
+
|
|
437
|
+
A URI that uniquely identifies the keyword. It should use a domain you
|
|
438
|
+
own to avoid conflict with keywords defined by others.
|
|
439
|
+
* compile: (schema: SchemaDocument, ast: AST, parentSchema: SchemaDocument) => Promise<A>
|
|
440
|
+
|
|
441
|
+
This function takes the keyword value, does whatever preprocessing it
|
|
442
|
+
can on it without an instance, and returns the result. The returned
|
|
443
|
+
value will be passed to the `interpret` function. The `ast` parameter is
|
|
444
|
+
needed for compiling sub-schemas. The `parentSchema` parameter is
|
|
445
|
+
primarily useful for looking up the value of an adjacent keyword that
|
|
446
|
+
might effect this one.
|
|
447
|
+
* interpret: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => boolean
|
|
448
|
+
|
|
449
|
+
This function takes the value returned by the `compile` function and the
|
|
450
|
+
instance value that is being validated and returns whether the value is
|
|
451
|
+
valid or not. The other parameters are only needed for validating
|
|
452
|
+
sub-schemas.
|
|
453
|
+
* collectEvaluatedProperties?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => string[] | false
|
|
454
|
+
|
|
455
|
+
If the keyword is an applicator, it will need to implements this
|
|
456
|
+
function for `unevaluatedProperties` to work as expected.
|
|
457
|
+
* collectEvaluatedItems?: (compiledKeywordValue: A, instance: JsonDocument, ast: AST, dynamicAnchors: Anchors) => Set<number> | false
|
|
458
|
+
|
|
459
|
+
If the keyword is an applicator, it will need to implements this
|
|
460
|
+
function for `unevaluatedItems` to work as expected.
|
|
461
|
+
|
|
462
|
+
### Schema
|
|
463
|
+
These functions are available from the
|
|
464
|
+
`@hyperjump/json-schema/schema/experimental` export.
|
|
465
|
+
|
|
466
|
+
This library uses SchemaDocument objects to represent a value in a schema.
|
|
467
|
+
You'll work with these objects if you create a custom keyword. This module is a
|
|
468
|
+
set of functions for working with SchemaDocuments.
|
|
469
|
+
|
|
470
|
+
* **Schema.add**: (schema: object, retrievalUri?: string, dialectId?: string) => string
|
|
471
|
+
|
|
472
|
+
Load a schema. Returns the identifier for the schema.
|
|
473
|
+
* **Schema.get**: (url: string, contextDoc?: SchemaDocument) => Promise<SchemaDocument>
|
|
474
|
+
|
|
475
|
+
Fetch a schema. Schemas can come from an HTTP request, a file, or a schema
|
|
476
|
+
that was added with `Schema.add`.
|
|
477
|
+
* **Schema.uri**: (doc: SchemaDocument) => string
|
|
478
|
+
|
|
479
|
+
Returns a URI for the value the SchemaDocument represents.
|
|
480
|
+
* **Schema.value**: (doc: SchemaDocument) => any
|
|
481
|
+
|
|
482
|
+
Returns the value the SchemaDocument represents.
|
|
483
|
+
* **Schema.typeOf**: (doc: SchemaDocument, type: string) => boolean
|
|
484
|
+
|
|
485
|
+
Determines if the JSON type of the given doc matches the given type.
|
|
486
|
+
* **Schema.has**: (key: string, doc: SchemaDocument) => Promise<SchemaDocument>
|
|
487
|
+
|
|
488
|
+
Similar to `key in schema`.
|
|
489
|
+
* **Schema.step**: (key: string, doc: SchemaDocument) => Promise<SchemaDocument>
|
|
490
|
+
|
|
491
|
+
Similar to `schema[key]`, but returns an SchemaDocument.
|
|
492
|
+
* **Schema.entries**: (doc: SchemaDocument) => Promise<[[string, SchemaDocument]]>
|
|
493
|
+
|
|
494
|
+
Similar to `Object.entries`, but returns SchemaDocuments for values.
|
|
495
|
+
* **Schema.keys**: (doc: SchemaDocument) => [string]
|
|
496
|
+
|
|
497
|
+
Similar to `Object.keys`.
|
|
498
|
+
* **Schema.map**: (fn: (item: Promise<SchemaDocument>, index: integer) => T, doc: SchemaDocument) => Promise<[T]>
|
|
499
|
+
|
|
500
|
+
A `map` function for an SchemaDocument whose value is an array.
|
|
501
|
+
* **Schema.length**: (doc: SchemaDocument) => number
|
|
502
|
+
|
|
503
|
+
Similar to `Array.prototype.length`.
|
|
504
|
+
* **Schema.toSchema**: (doc: SchemaDocument, options: ToSchemaOptions) => object
|
|
505
|
+
|
|
506
|
+
Get a raw schema from a Schema Document.
|
|
507
|
+
|
|
508
|
+
**Type Definitions**
|
|
509
|
+
|
|
510
|
+
The following types are used in the above definitions
|
|
511
|
+
|
|
512
|
+
* **ToSchemaOptions**: object
|
|
513
|
+
|
|
514
|
+
* parentId: string (default: "") -- `file://` URIs will be generated
|
|
515
|
+
relative to this path.
|
|
516
|
+
* parentDialect: string (default: "") -- If the dialect of the schema
|
|
517
|
+
* matches this value, the `$schema` keyword will be omitted.
|
|
518
|
+
* includeEmbedded: boolean (default: true) -- If false, embedded schemas
|
|
519
|
+
will be unbundled from the schema.
|
|
520
|
+
|
|
521
|
+
### Instance
|
|
522
|
+
These functions are available from the
|
|
523
|
+
`@hyperjump/json-schema/instance/experimental` export.
|
|
524
|
+
|
|
525
|
+
This library uses InstanceDocument objects to represent a value in an instance.
|
|
526
|
+
You'll work with these objects if you create a custom keyword. This module is a
|
|
527
|
+
set of functions for working with InstanceDocuments.
|
|
528
|
+
|
|
529
|
+
* **Instance.cons**: (instance: any, uri?: string) => InstanceDocument
|
|
530
|
+
|
|
531
|
+
Construct an InstanceDocument from a value.
|
|
532
|
+
* **Instance.get**: (url: string, contextDoc: InstanceDocument) => InstanceDocument
|
|
533
|
+
|
|
534
|
+
Apply a same-resource reference to a InstanceDocument.
|
|
535
|
+
* **Instance.uri**: (doc: InstanceDocument) => string
|
|
536
|
+
|
|
537
|
+
Returns a URI for the value the InstanceDocument represents.
|
|
538
|
+
* **Instance.value**: (doc: InstanceDocument) => any
|
|
539
|
+
|
|
540
|
+
Returns the value the InstanceDocument represents.
|
|
541
|
+
* **Instance.has**: (key: string, doc: InstanceDocument) => any
|
|
542
|
+
|
|
543
|
+
Similar to `key in instance`.
|
|
544
|
+
* **Instance.typeOf**: (doc: InstanceDocument, type: string) => boolean
|
|
545
|
+
|
|
546
|
+
Determines if the JSON type of the given doc matches the given type.
|
|
547
|
+
* **Instance.step**: (key: string, doc: InstanceDocument) => InstanceDocument
|
|
548
|
+
|
|
549
|
+
Similar to `schema[key]`, but returns a InstanceDocument.
|
|
550
|
+
* **Instance.entries**: (doc: InstanceDocument) => [string, InstanceDocument]
|
|
551
|
+
|
|
552
|
+
Similar to `Object.entries`, but returns IDocs for values.
|
|
553
|
+
* **Instance.keys**: (doc: InstanceDocument) => [string]
|
|
554
|
+
|
|
555
|
+
Similar to `Object.keys`.
|
|
556
|
+
* **Instance.map**: (fn: (item: InstanceDocument, index: integer) => T, doc: InstanceDocument) => [T]
|
|
187
557
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* **setShouldMetaValidate**: (isEnabled: boolean) => undefined
|
|
558
|
+
A `map` function for an InstanceDocument whose value is an array.
|
|
559
|
+
* **Instance.reduce**: (fn: (accumulator: T, item: InstanceDocument, index: integer) => T, initial: T, doc: InstanceDocument) => T
|
|
191
560
|
|
|
192
|
-
|
|
193
|
-
* **
|
|
561
|
+
A `reduce` function for an InstanceDocument whose value is an array.
|
|
562
|
+
* **Instance.every**: (fn: (doc: InstanceDocument, index: integer) => boolean, doc: InstanceDocument) => boolean
|
|
194
563
|
|
|
195
|
-
|
|
196
|
-
|
|
564
|
+
An `every` function for an InstanceDocument whose value is an array.
|
|
565
|
+
* **Instance.some**: (fn: (doc: InstanceDocument, index: integer) => boolean, doc: InstanceDocument) => boolean
|
|
197
566
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
optional features are not supported yet.
|
|
567
|
+
A `some` function for an InstanceDocument whose value is an array.
|
|
568
|
+
* **Instance.length**: (doc: InstanceDocument) => number
|
|
201
569
|
|
|
202
|
-
|
|
570
|
+
Similar to `Array.prototype.length`.
|
|
203
571
|
|
|
204
572
|
## Contributing
|
|
205
573
|
|