json-schema-library 5.2.1 → 6.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/.prettierignore +1 -0
- package/.prettierrc +7 -0
- package/README.md +9 -8
- package/dist/index.d.ts +13 -44
- package/dist/jsonSchemaLibrary.js +1 -1
- package/dist/lib/addValidator.d.ts +2 -1
- package/dist/lib/compile/index.d.ts +11 -0
- package/dist/lib/config/strings.d.ts +1 -39
- package/dist/lib/cores/CoreInterface.d.ts +28 -9
- package/dist/lib/cores/Draft04.d.ts +2 -2
- package/dist/lib/cores/Draft06.d.ts +15 -0
- package/dist/lib/cores/Draft07.d.ts +15 -0
- package/dist/lib/cores/JsonEditor.d.ts +2 -2
- package/dist/lib/draft06/addSchema.d.ts +7 -0
- package/dist/lib/draft06/compile/index.d.ts +15 -0
- package/dist/lib/draft06/validation/keyword.d.ts +3 -0
- package/dist/lib/draft06/validation/type.d.ts +17 -0
- package/dist/lib/draft06/validation/typeKeywordMapping.d.ts +13 -0
- package/dist/lib/getChildSchemaSelection.d.ts +7 -5
- package/dist/lib/getSchema.d.ts +1 -1
- package/dist/lib/getTemplate.d.ts +5 -1
- package/dist/lib/getTypeOf.d.ts +2 -1
- package/dist/lib/schema/getTypeId.d.ts +1 -1
- package/dist/lib/step.d.ts +4 -4
- package/dist/lib/types.d.ts +10 -2
- package/dist/lib/utils/filter.d.ts +0 -1
- package/dist/lib/utils/merge.d.ts +3 -0
- package/dist/lib/validate.d.ts +1 -1
- package/dist/lib/validation/format.d.ts +6 -0
- package/dist/lib/validation/keyword.d.ts +2 -27
- package/dist/lib/validation/type.d.ts +3 -10
- package/dist/lib/validation/typeKeywordMapping.d.ts +4 -4
- package/dist/module/index.js +13 -6
- package/dist/module/lib/addValidator.js +3 -4
- package/dist/module/lib/compile/getRef.js +1 -1
- package/dist/module/lib/compile/index.js +11 -0
- package/dist/module/lib/config/strings.js +15 -2
- package/dist/module/lib/cores/CoreInterface.js +22 -0
- package/dist/module/lib/cores/Draft06.js +61 -0
- package/dist/module/lib/cores/Draft07.js +61 -0
- package/dist/module/lib/createSchemaOf.js +1 -1
- package/dist/module/lib/draft06/addSchema.js +11 -0
- package/dist/module/lib/draft06/compile/index.js +65 -0
- package/dist/module/lib/draft06/validation/keyword.js +156 -0
- package/dist/module/lib/draft06/validation/type.js +30 -0
- package/dist/module/lib/draft06/validation/typeKeywordMapping.js +15 -0
- package/dist/module/lib/each.js +1 -1
- package/dist/module/lib/eachSchema.js +3 -3
- package/dist/module/lib/getChildSchemaSelection.js +7 -6
- package/dist/module/lib/getSchema.js +2 -1
- package/dist/module/lib/getTemplate.js +57 -23
- package/dist/module/lib/resolveAllOf.js +3 -4
- package/dist/module/lib/resolveOneOf.fuzzy.js +13 -3
- package/dist/module/lib/resolveOneOf.strict.js +11 -2
- package/dist/module/lib/resolveRef.strict.js +8 -0
- package/dist/module/lib/schema/getTypeDefs.js +12 -1
- package/dist/module/lib/schema/getTypeId.js +1 -1
- package/dist/module/lib/step.js +62 -11
- package/dist/module/lib/types.js +7 -1
- package/dist/module/lib/utils/filter.js +3 -5
- package/dist/module/lib/utils/merge.js +3 -0
- package/dist/module/lib/validate.js +33 -8
- package/dist/module/lib/validateAsync.js +7 -7
- package/dist/module/lib/validation/errors.js +15 -2
- package/dist/module/lib/validation/format.js +105 -4
- package/dist/module/lib/validation/keyword.js +77 -30
- package/dist/module/lib/validation/type.js +2 -1
- package/dist/module/remotes/draft06.json +155 -0
- package/dist/module/remotes/draft07.json +172 -0
- package/dist/module/remotes/index.js +0 -1
- package/dist/remotes/index.d.ts +0 -1
- package/index.ts +14 -5
- package/lib/addValidator.ts +5 -5
- package/lib/compile/getRef.ts +1 -1
- package/lib/compile/index.ts +11 -1
- package/lib/config/strings.ts +17 -3
- package/lib/cores/CoreInterface.ts +37 -10
- package/lib/cores/Draft04.ts +2 -4
- package/lib/cores/Draft06.ts +76 -0
- package/lib/cores/Draft07.ts +75 -0
- package/lib/cores/JsonEditor.ts +2 -4
- package/lib/createSchemaOf.ts +1 -3
- package/lib/draft06/addSchema.ts +14 -0
- package/lib/draft06/compile/index.ts +68 -0
- package/lib/draft06/validation/keyword.ts +177 -0
- package/lib/draft06/validation/type.ts +43 -0
- package/lib/draft06/validation/typeKeywordMapping.ts +15 -0
- package/lib/each.ts +8 -3
- package/lib/eachSchema.ts +3 -3
- package/lib/getChildSchemaSelection.ts +14 -7
- package/lib/getSchema.ts +15 -7
- package/lib/getTemplate.ts +148 -38
- package/lib/getTypeOf.ts +2 -1
- package/lib/resolveAllOf.ts +9 -5
- package/lib/resolveOneOf.fuzzy.ts +25 -8
- package/lib/resolveOneOf.strict.ts +17 -4
- package/lib/resolveRef.strict.ts +9 -0
- package/lib/schema/getTypeDefs.ts +14 -1
- package/lib/schema/getTypeId.ts +2 -2
- package/lib/step.ts +103 -22
- package/lib/types.ts +21 -4
- package/lib/utils/filter.ts +4 -6
- package/lib/utils/merge.ts +4 -0
- package/lib/validate.ts +45 -15
- package/lib/validateAsync.ts +13 -12
- package/lib/validation/errors.ts +15 -2
- package/lib/validation/format.ts +113 -4
- package/lib/validation/keyword.ts +147 -78
- package/lib/validation/type.ts +5 -1
- package/package.json +73 -63
- package/remotes/draft06.json +155 -0
- package/remotes/draft07.json +172 -0
- package/remotes/draft2019-09.json +86 -0
- package/remotes/index.ts +0 -2
- package/tsconfig.json +2 -9
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import copy from "./utils/copy";
|
|
2
2
|
import merge from "./utils/merge";
|
|
3
|
-
import errors from "./validation/errors";
|
|
4
3
|
export default function resolveAllOf(core, data, schema = core.rootSchema, pointer = "#") {
|
|
5
4
|
let mergedSchema = copy(schema);
|
|
6
5
|
for (let i = 0; i < schema.allOf.length; i += 1) {
|
|
7
6
|
const allOfSchema = core.resolveRef(schema.allOf[i]);
|
|
8
|
-
if (core.isValid(data, allOfSchema, pointer) === false) {
|
|
9
|
-
|
|
10
|
-
}
|
|
7
|
+
// if (core.isValid(data, allOfSchema, pointer) === false) {
|
|
8
|
+
// return errors.allOfError({ value: data, pointer, allOf: JSON.stringify(schema.allOf) });
|
|
9
|
+
// }
|
|
11
10
|
mergedSchema = merge(mergedSchema, allOfSchema);
|
|
12
11
|
}
|
|
13
12
|
delete mergedSchema.allOf;
|
|
@@ -2,6 +2,7 @@ import { errorOrPromise } from "./utils/filter";
|
|
|
2
2
|
import flattenArray from "./utils/flattenArray";
|
|
3
3
|
import getTypeOf from "./getTypeOf";
|
|
4
4
|
import settings from "./config/settings";
|
|
5
|
+
import { isJSONError } from "./types";
|
|
5
6
|
const { DECLARATOR_ONEOF } = settings;
|
|
6
7
|
/**
|
|
7
8
|
* Returns a ranking for the data and given schema
|
|
@@ -52,7 +53,7 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
|
|
|
52
53
|
for (let i = 0; i < schema.oneOf.length; i += 1) {
|
|
53
54
|
const one = core.resolveRef(schema.oneOf[i]);
|
|
54
55
|
const oneOfPropertySchema = core.step(oneOfProperty, one, data, pointer);
|
|
55
|
-
if (oneOfPropertySchema
|
|
56
|
+
if (isJSONError(oneOfPropertySchema)) {
|
|
56
57
|
return oneOfPropertySchema;
|
|
57
58
|
}
|
|
58
59
|
let result = flattenArray(core.validate(oneOfValue, oneOfPropertySchema, pointer));
|
|
@@ -64,7 +65,12 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
|
|
|
64
65
|
return one; // return resolved schema
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
|
-
return core.errors.oneOfPropertyError({
|
|
68
|
+
return core.errors.oneOfPropertyError({
|
|
69
|
+
property: oneOfProperty,
|
|
70
|
+
value: oneOfValue,
|
|
71
|
+
pointer,
|
|
72
|
+
errors
|
|
73
|
+
});
|
|
68
74
|
}
|
|
69
75
|
// keyword: oneOf
|
|
70
76
|
const matches = [];
|
|
@@ -90,7 +96,11 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
|
|
|
90
96
|
}
|
|
91
97
|
}
|
|
92
98
|
if (schemaOfItem === undefined) {
|
|
93
|
-
return core.errors.oneOfError({
|
|
99
|
+
return core.errors.oneOfError({
|
|
100
|
+
value: JSON.stringify(data),
|
|
101
|
+
pointer,
|
|
102
|
+
oneOf: schema.oneOf
|
|
103
|
+
});
|
|
94
104
|
}
|
|
95
105
|
return schemaOfItem;
|
|
96
106
|
}
|
|
@@ -27,7 +27,16 @@ export default function resolveOneOf(core, data, schema = core.rootSchema, point
|
|
|
27
27
|
return matches[0];
|
|
28
28
|
}
|
|
29
29
|
if (matches.length > 1) {
|
|
30
|
-
return core.errors.multipleOneOfError({
|
|
30
|
+
return core.errors.multipleOneOfError({
|
|
31
|
+
value: data,
|
|
32
|
+
pointer,
|
|
33
|
+
matches,
|
|
34
|
+
});
|
|
31
35
|
}
|
|
32
|
-
return core.errors.oneOfError({
|
|
36
|
+
return core.errors.oneOfError({
|
|
37
|
+
value: JSON.stringify(data),
|
|
38
|
+
pointer,
|
|
39
|
+
oneOf: schema.oneOf,
|
|
40
|
+
errors,
|
|
41
|
+
});
|
|
33
42
|
}
|
|
@@ -2,6 +2,14 @@ export default function resolveRef(schema, rootSchema) {
|
|
|
2
2
|
if (schema == null || schema.$ref == null) {
|
|
3
3
|
return schema;
|
|
4
4
|
}
|
|
5
|
+
if (schema.getRoot) {
|
|
6
|
+
// we actually always need to resolve the schema like this, since returned subschemas
|
|
7
|
+
// must resolve relative from their schema
|
|
8
|
+
const resolvedSchema = schema.getRoot().getRef(schema);
|
|
9
|
+
// console.log(schema.$ref, "=>", resolvedSchema);
|
|
10
|
+
return resolvedSchema;
|
|
11
|
+
}
|
|
12
|
+
// tryout - this should never be called, except we missed something
|
|
5
13
|
const resolvedSchema = rootSchema.getRef(schema);
|
|
6
14
|
return resolvedSchema;
|
|
7
15
|
}
|
|
@@ -14,7 +14,18 @@ export default function getTypeDefs(schema) {
|
|
|
14
14
|
if (id == null) {
|
|
15
15
|
return defs;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
let type;
|
|
18
|
+
if (Array.isArray(id)) {
|
|
19
|
+
// since types can also be declared as a set of types, merge the definitions
|
|
20
|
+
// maybe this will require a more sophisticated approach
|
|
21
|
+
type = {};
|
|
22
|
+
for (let i = 0, l = id.length; i < l; i += 1) {
|
|
23
|
+
Object.assign(type, types[id[i]]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
type = types[id];
|
|
28
|
+
}
|
|
18
29
|
if (type.definitions == null) {
|
|
19
30
|
return defs;
|
|
20
31
|
}
|
|
@@ -15,7 +15,7 @@ export default function getTypeId(schema) {
|
|
|
15
15
|
if (schema.enum) {
|
|
16
16
|
return "enum";
|
|
17
17
|
}
|
|
18
|
-
if (types[schema.type]) {
|
|
18
|
+
if (types[schema.type] || Array.isArray(schema.type)) {
|
|
19
19
|
return schema.type;
|
|
20
20
|
}
|
|
21
21
|
const ids = typeKeywords.filter(type => schema[type]);
|
package/dist/module/lib/step.js
CHANGED
|
@@ -2,6 +2,7 @@ import getTypeOf from "./getTypeOf";
|
|
|
2
2
|
import createSchemaOf from "./createSchemaOf";
|
|
3
3
|
import errors from "./validation/errors";
|
|
4
4
|
import merge from "./utils/merge";
|
|
5
|
+
import { isJSONError } from "./types";
|
|
5
6
|
const stepType = {
|
|
6
7
|
array: (core, key, schema, data, pointer) => {
|
|
7
8
|
const itemsType = getTypeOf(schema.items);
|
|
@@ -23,11 +24,27 @@ const stepType = {
|
|
|
23
24
|
return core.resolveRef(schema.items);
|
|
24
25
|
}
|
|
25
26
|
if (itemsType === "array") {
|
|
27
|
+
// @draft >= 7 bool schema, items:[true, false]
|
|
28
|
+
if (schema.items[key] === true) {
|
|
29
|
+
return createSchemaOf(data[key]);
|
|
30
|
+
}
|
|
31
|
+
// @draft >= 7 bool schema, items:[true, false]
|
|
32
|
+
if (schema.items[key] === false) {
|
|
33
|
+
return errors.invalidDataError({
|
|
34
|
+
key,
|
|
35
|
+
value: data[key],
|
|
36
|
+
pointer
|
|
37
|
+
});
|
|
38
|
+
}
|
|
26
39
|
if (schema.items[key]) {
|
|
27
40
|
return core.resolveRef(schema.items[key]);
|
|
28
41
|
}
|
|
29
42
|
if (schema.additionalItems === false) {
|
|
30
|
-
return errors.additionalItemsError({
|
|
43
|
+
return errors.additionalItemsError({
|
|
44
|
+
key,
|
|
45
|
+
value: data[key],
|
|
46
|
+
pointer
|
|
47
|
+
});
|
|
31
48
|
}
|
|
32
49
|
if (schema.additionalItems === true || schema.additionalItems === undefined) {
|
|
33
50
|
return createSchemaOf(data[key]);
|
|
@@ -50,21 +67,21 @@ const stepType = {
|
|
|
50
67
|
const oneOfSchema = core.resolveOneOf(data, schema, pointer);
|
|
51
68
|
// resolveOneOf does currently not apply merge with base schema
|
|
52
69
|
schema = merge(schema, oneOfSchema);
|
|
53
|
-
if (schema
|
|
70
|
+
if (isJSONError(schema)) {
|
|
54
71
|
return schema;
|
|
55
72
|
}
|
|
56
73
|
}
|
|
57
74
|
if (Array.isArray(schema.anyOf)) {
|
|
58
75
|
// update current schema
|
|
59
76
|
schema = core.resolveAnyOf(data, schema, pointer);
|
|
60
|
-
if (schema
|
|
77
|
+
if (isJSONError(schema)) {
|
|
61
78
|
return schema;
|
|
62
79
|
}
|
|
63
80
|
}
|
|
64
81
|
if (Array.isArray(schema.allOf)) {
|
|
65
82
|
// update current schema
|
|
66
83
|
schema = core.resolveAllOf(data, schema, pointer);
|
|
67
|
-
if (schema
|
|
84
|
+
if (isJSONError(schema)) {
|
|
68
85
|
return schema;
|
|
69
86
|
}
|
|
70
87
|
}
|
|
@@ -73,7 +90,7 @@ const stepType = {
|
|
|
73
90
|
if (schema.properties && schema.properties[key] !== undefined) {
|
|
74
91
|
// @todo patternProperties also validate properties
|
|
75
92
|
targetSchema = core.resolveRef(schema.properties[key]);
|
|
76
|
-
if (targetSchema
|
|
93
|
+
if (isJSONError(targetSchema)) {
|
|
77
94
|
return targetSchema;
|
|
78
95
|
}
|
|
79
96
|
// check if there is a oneOf selection, which must be resolved
|
|
@@ -81,8 +98,10 @@ const stepType = {
|
|
|
81
98
|
// @special case: this is a mix of a schema and optional definitions
|
|
82
99
|
// we resolve the schema here and add the original schema to `oneOfSchema`
|
|
83
100
|
let resolvedSchema = core.resolveOneOf(data[key], targetSchema, `${pointer}/${key}`);
|
|
101
|
+
const oneOfIndex = targetSchema.oneOf.findIndex((s) => s === resolvedSchema);
|
|
84
102
|
resolvedSchema = JSON.parse(JSON.stringify(resolvedSchema));
|
|
85
103
|
resolvedSchema.variableSchema = true;
|
|
104
|
+
resolvedSchema.oneOfIndex = oneOfIndex;
|
|
86
105
|
resolvedSchema.oneOfSchema = targetSchema;
|
|
87
106
|
return resolvedSchema;
|
|
88
107
|
}
|
|
@@ -91,6 +110,20 @@ const stepType = {
|
|
|
91
110
|
return targetSchema;
|
|
92
111
|
}
|
|
93
112
|
}
|
|
113
|
+
// @draft <= 07
|
|
114
|
+
const { dependencies } = schema;
|
|
115
|
+
if (getTypeOf(dependencies) === "object") {
|
|
116
|
+
const dependentProperties = Object.keys(dependencies).filter((propertyName) =>
|
|
117
|
+
// data[propertyName] !== undefined &&
|
|
118
|
+
getTypeOf(dependencies[propertyName]) === "object");
|
|
119
|
+
for (let i = 0, l = dependentProperties.length; i < l; i += 1) {
|
|
120
|
+
const dependentProperty = dependentProperties[i];
|
|
121
|
+
const schema = step(core, key, dependencies[dependentProperty], data);
|
|
122
|
+
if (!isJSONError(schema)) {
|
|
123
|
+
return schema;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
94
127
|
// find matching property key
|
|
95
128
|
if (getTypeOf(schema.patternProperties) === "object") {
|
|
96
129
|
let regex;
|
|
@@ -106,9 +139,14 @@ const stepType = {
|
|
|
106
139
|
return schema.additionalProperties;
|
|
107
140
|
}
|
|
108
141
|
if (schema.additionalProperties === true) {
|
|
109
|
-
return createSchemaOf(data);
|
|
142
|
+
return createSchemaOf(data[key]);
|
|
110
143
|
}
|
|
111
|
-
return errors.unknownPropertyError({
|
|
144
|
+
return errors.unknownPropertyError({
|
|
145
|
+
property: key,
|
|
146
|
+
value: data,
|
|
147
|
+
// pointer: `${pointer}/${key}`,
|
|
148
|
+
pointer: `${pointer}`
|
|
149
|
+
});
|
|
112
150
|
}
|
|
113
151
|
};
|
|
114
152
|
/**
|
|
@@ -118,14 +156,27 @@ const stepType = {
|
|
|
118
156
|
* This helper determines the location of the property within the schema (additional properties, oneOf, ...) and
|
|
119
157
|
* returns the correct schema.
|
|
120
158
|
*
|
|
121
|
-
* @param core
|
|
122
|
-
* @param key
|
|
123
|
-
* @param schema
|
|
159
|
+
* @param core - validator
|
|
160
|
+
* @param key - property-name or array-index
|
|
161
|
+
* @param schema - json schema of current data
|
|
124
162
|
* @param data - parent of key
|
|
125
|
-
* @param [pointer]
|
|
163
|
+
* @param [pointer] - pointer to schema and data (parent of key)
|
|
126
164
|
* @return Schema or Error if failed resolving key
|
|
127
165
|
*/
|
|
128
166
|
export default function step(core, key, schema, data, pointer = "#") {
|
|
167
|
+
// @draft >= 4 ?
|
|
168
|
+
if (Array.isArray(schema.type)) {
|
|
169
|
+
const dataType = getTypeOf(data);
|
|
170
|
+
if (schema.type.includes(dataType)) {
|
|
171
|
+
return stepType[dataType](core, key, schema, data, pointer);
|
|
172
|
+
}
|
|
173
|
+
return core.errors.typeError({
|
|
174
|
+
value: data,
|
|
175
|
+
pointer,
|
|
176
|
+
expected: schema.type,
|
|
177
|
+
received: dataType
|
|
178
|
+
});
|
|
179
|
+
}
|
|
129
180
|
const expectedType = schema.type || getTypeOf(data);
|
|
130
181
|
if (stepType[expectedType]) {
|
|
131
182
|
return stepType[expectedType](core, key, schema, data, pointer);
|
package/dist/module/lib/types.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
+
import { isJSONError } from "../types";
|
|
1
2
|
export function isPromise(obj) {
|
|
2
3
|
return obj instanceof Promise;
|
|
3
4
|
}
|
|
4
|
-
export function isError(obj) {
|
|
5
|
-
return obj && obj.type === "error";
|
|
6
|
-
}
|
|
7
5
|
export function errorOrPromise(error) {
|
|
8
|
-
return
|
|
6
|
+
return isJSONError(error) || isPromise(error);
|
|
9
7
|
}
|
|
10
8
|
export function errorsOnly(error) {
|
|
11
|
-
return
|
|
9
|
+
return isJSONError(error);
|
|
12
10
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import deepmerge from "deepmerge";
|
|
2
2
|
const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
|
|
3
|
+
/**
|
|
4
|
+
* returns a new json-schema, where properties are combined and arrays are replaced
|
|
5
|
+
*/
|
|
3
6
|
export default (a, b) => deepmerge(a, b, { arrayMerge: overwriteMerge });
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import getTypeOf from "./getTypeOf";
|
|
2
2
|
import { errorOrPromise } from "./utils/filter";
|
|
3
3
|
import flattenArray from "./utils/flattenArray";
|
|
4
|
+
import { isJSONError } from "./types";
|
|
5
|
+
import equal from "fast-deep-equal";
|
|
4
6
|
function getJsonSchemaType(value, expectedType) {
|
|
5
|
-
|
|
6
|
-
if (jsType === "number" &&
|
|
7
|
-
(
|
|
8
|
-
|
|
7
|
+
const jsType = getTypeOf(value);
|
|
8
|
+
if (jsType === "number" &&
|
|
9
|
+
(expectedType === "integer" ||
|
|
10
|
+
(Array.isArray(expectedType) && expectedType.includes("integer")))) {
|
|
11
|
+
return Number.isInteger(value) ? "integer" : "number";
|
|
9
12
|
}
|
|
10
13
|
return jsType;
|
|
11
14
|
}
|
|
@@ -19,14 +22,36 @@ function getJsonSchemaType(value, expectedType) {
|
|
|
19
22
|
* @return list of errors or empty
|
|
20
23
|
*/
|
|
21
24
|
export default function validate(core, value, schema = core.rootSchema, pointer = "#") {
|
|
22
|
-
|
|
25
|
+
schema = core.resolveRef(schema);
|
|
26
|
+
// this is a high level v7 schema validation
|
|
27
|
+
if (getTypeOf(schema) === "boolean") {
|
|
28
|
+
if (schema) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return [core.errors.invalidDataError({ value, pointer })];
|
|
32
|
+
}
|
|
33
|
+
if (isJSONError(schema)) {
|
|
23
34
|
return [schema];
|
|
24
35
|
}
|
|
25
|
-
|
|
36
|
+
// @draft >= 6 const
|
|
37
|
+
if (schema.const !== undefined) {
|
|
38
|
+
if (equal(schema.const, value)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return [core.errors.constError({ value, expected: schema.const, pointer })];
|
|
42
|
+
}
|
|
26
43
|
const receivedType = getJsonSchemaType(value, schema.type);
|
|
27
44
|
const expectedType = schema.type || receivedType;
|
|
28
|
-
if (receivedType !== expectedType &&
|
|
29
|
-
|
|
45
|
+
if (receivedType !== expectedType &&
|
|
46
|
+
(!Array.isArray(expectedType) || !expectedType.includes(receivedType))) {
|
|
47
|
+
return [
|
|
48
|
+
core.errors.typeError({
|
|
49
|
+
received: receivedType,
|
|
50
|
+
expected: expectedType,
|
|
51
|
+
value,
|
|
52
|
+
pointer
|
|
53
|
+
})
|
|
54
|
+
];
|
|
30
55
|
}
|
|
31
56
|
if (core.validateType[receivedType] == null) {
|
|
32
57
|
return [core.errors.invalidTypeError({ receivedType, pointer })];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errorsOnly } from "./utils/filter";
|
|
2
2
|
import flattenArray from "./utils/flattenArray";
|
|
3
|
+
import { isJSONError } from "./types";
|
|
3
4
|
function createErrorNotification(onError) {
|
|
4
5
|
return function notifyError(error) {
|
|
5
6
|
if (Array.isArray(error)) {
|
|
@@ -7,7 +8,7 @@ function createErrorNotification(onError) {
|
|
|
7
8
|
error.forEach(notifyError);
|
|
8
9
|
return error;
|
|
9
10
|
}
|
|
10
|
-
if (
|
|
11
|
+
if (isJSONError(error)) {
|
|
11
12
|
onError(error);
|
|
12
13
|
}
|
|
13
14
|
return error;
|
|
@@ -35,16 +36,15 @@ export default function validateAsync(core, value, options) {
|
|
|
35
36
|
if (errors[i] instanceof Promise) {
|
|
36
37
|
errors[i].then(notifyError);
|
|
37
38
|
}
|
|
38
|
-
else if (
|
|
39
|
+
else if (isJSONError(errors[i])) {
|
|
39
40
|
onError(errors[i]);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
return Promise
|
|
44
|
-
.all(errors)
|
|
44
|
+
return Promise.all(errors)
|
|
45
45
|
.then(flattenArray)
|
|
46
|
-
.then(resolvedErrors => resolvedErrors.filter(errorsOnly))
|
|
47
|
-
.catch(e => {
|
|
46
|
+
.then((resolvedErrors) => resolvedErrors.filter(errorsOnly))
|
|
47
|
+
.catch((e) => {
|
|
48
48
|
console.log("Failed resolving promises", e.message);
|
|
49
49
|
console.log(e.stack);
|
|
50
50
|
throw e;
|
|
@@ -5,17 +5,30 @@ const errors = {
|
|
|
5
5
|
additionalPropertiesError: createCustomError("AdditionalPropertiesError"),
|
|
6
6
|
anyOfError: createCustomError("AnyOfError"),
|
|
7
7
|
allOfError: createCustomError("AllOfError"),
|
|
8
|
+
constError: createCustomError("ConstError"),
|
|
9
|
+
containsError: createCustomError("ContainsError"),
|
|
10
|
+
containsArrayError: createCustomError("ContainsArrayError"),
|
|
11
|
+
containsAnyError: createCustomError("ContainsAnyError"),
|
|
8
12
|
enumError: createCustomError("EnumError"),
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
formatURLError: createCustomError("FormatURLError"),
|
|
14
|
+
formatURIError: createCustomError("FormatURIError"),
|
|
15
|
+
formatURIReferenceError: createCustomError("FormatURIReferenceError"),
|
|
16
|
+
formatURITemplateError: createCustomError("FormatURITemplateError"),
|
|
17
|
+
formatDateError: createCustomError("FormatDateaError"),
|
|
11
18
|
formatDateTimeError: createCustomError("FormatDateTimeError"),
|
|
12
19
|
formatEmailError: createCustomError("FormatEmailError"),
|
|
13
20
|
formatHostnameError: createCustomError("FormatHostnameError"),
|
|
14
21
|
formatIPV4Error: createCustomError("FormatIPV4Error"),
|
|
22
|
+
formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"),
|
|
15
23
|
formatIPV6Error: createCustomError("FormatIPV6Error"),
|
|
24
|
+
formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"),
|
|
25
|
+
formatJSONPointerError: createCustomError("FormatJSONPointerError"),
|
|
16
26
|
formatRegExError: createCustomError("FormatRegExError"),
|
|
27
|
+
formatTimeError: createCustomError("FormatTimeError"),
|
|
17
28
|
invalidSchemaError: createCustomError("InvalidSchemaError"),
|
|
29
|
+
invalidDataError: createCustomError("InvalidDataError"),
|
|
18
30
|
invalidTypeError: createCustomError("InvalidTypeError"),
|
|
31
|
+
invalidPropertyNameError: createCustomError("InvalidPropertyNameError"),
|
|
19
32
|
maximumError: createCustomError("MaximumError"),
|
|
20
33
|
maxItemsError: createCustomError("MaxItemsError"),
|
|
21
34
|
maxLengthError: createCustomError("MaxLengthError"),
|
|
@@ -1,13 +1,43 @@
|
|
|
1
|
-
/* eslint-disable max-len */
|
|
1
|
+
/* eslint-disable max-len, no-control-regex */
|
|
2
2
|
import errors from "./errors";
|
|
3
3
|
import validUrl from "valid-url";
|
|
4
|
+
// referenced
|
|
5
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
4
6
|
// https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8
|
|
5
7
|
const isValidDateTime = new RegExp("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([\\+|\\-]([01][0-9]|2[0-3]):[0-5][0-9]))$");
|
|
6
8
|
const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
|
|
7
9
|
const isValidIPV6 = /^((([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}))|:)))$/i;
|
|
8
10
|
const isValidHostname = /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/;
|
|
11
|
+
const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
12
|
+
const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
|
|
13
|
+
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
14
|
+
const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
15
|
+
const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
16
|
+
const isValidURIRef = /^(?:[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;
|
|
17
|
+
// uri-template: https://tools.ietf.org/html/rfc6570
|
|
18
|
+
const isValidURITemplate = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
|
|
9
19
|
// Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref
|
|
10
20
|
export default {
|
|
21
|
+
date: (core, schema, value, pointer) => {
|
|
22
|
+
if (typeof value !== "string") {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
26
|
+
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
27
|
+
const matches = value.match(matchDate);
|
|
28
|
+
if (!matches) {
|
|
29
|
+
return errors.formatDateTimeError({ value, pointer });
|
|
30
|
+
}
|
|
31
|
+
const year = +matches[1];
|
|
32
|
+
const month = +matches[2];
|
|
33
|
+
const day = +matches[3];
|
|
34
|
+
// https://tools.ietf.org/html/rfc3339#appendix-C
|
|
35
|
+
const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
36
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return errors.formatDateError({ value, pointer });
|
|
40
|
+
},
|
|
11
41
|
"date-time": (core, schema, value, pointer) => {
|
|
12
42
|
if (typeof value !== "string") {
|
|
13
43
|
return undefined;
|
|
@@ -56,6 +86,10 @@ export default {
|
|
|
56
86
|
if (typeof value !== "string" || value === "") {
|
|
57
87
|
return undefined;
|
|
58
88
|
}
|
|
89
|
+
if (value && value[0] === "0") {
|
|
90
|
+
// leading zeroes should be rejected, as they are treated as octals
|
|
91
|
+
return errors.formatIPV4LeadingZeroError({ value, pointer });
|
|
92
|
+
}
|
|
59
93
|
if (value.length <= 15 && isValidIPV4.test(value)) {
|
|
60
94
|
return undefined;
|
|
61
95
|
}
|
|
@@ -65,25 +99,92 @@ export default {
|
|
|
65
99
|
if (typeof value !== "string" || value === "") {
|
|
66
100
|
return undefined;
|
|
67
101
|
}
|
|
102
|
+
if (value && value[0] === "0") {
|
|
103
|
+
// leading zeroes should be rejected, as they are treated as octals
|
|
104
|
+
return errors.formatIPV6LeadingZeroError({ value, pointer });
|
|
105
|
+
}
|
|
68
106
|
if (value.length <= 45 && isValidIPV6.test(value)) {
|
|
69
107
|
return undefined;
|
|
70
108
|
}
|
|
71
109
|
return errors.formatIPV6Error({ value, pointer });
|
|
72
110
|
},
|
|
111
|
+
"json-pointer": (core, schema, value, pointer) => {
|
|
112
|
+
if (typeof value !== "string" || value === "") {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
if (isValidJSONPointer.test(value)) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
return errors.formatJSONPointerError({ value, pointer });
|
|
119
|
+
},
|
|
120
|
+
"relative-json-pointer": (core, schema, value, pointer) => {
|
|
121
|
+
if (typeof value !== "string" || value === "") {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
if (isValidRelativeJSONPointer.test(value)) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return errors.formatJSONPointerError({ value, pointer });
|
|
128
|
+
},
|
|
73
129
|
regex: (core, schema, value, pointer) => {
|
|
74
130
|
if (typeof value === "string" && /\\Z$/.test(value) === false) {
|
|
131
|
+
try {
|
|
132
|
+
new RegExp(value);
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
catch (e) { } // eslint-disable-line no-empty
|
|
136
|
+
return errors.formatRegExError({ value, pointer });
|
|
137
|
+
}
|
|
138
|
+
// v7 tests, ignore non-regex values
|
|
139
|
+
if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) {
|
|
75
140
|
return undefined;
|
|
76
141
|
}
|
|
77
142
|
return errors.formatRegExError({ value, pointer });
|
|
78
143
|
},
|
|
79
|
-
|
|
144
|
+
time: (core, schema, value, pointer) => {
|
|
80
145
|
if (typeof value !== "string") {
|
|
81
146
|
return undefined;
|
|
82
147
|
}
|
|
83
|
-
|
|
148
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
149
|
+
const matches = value.match(matchTime);
|
|
150
|
+
if (!matches) {
|
|
151
|
+
return errors.formatDateTimeError({ value, pointer });
|
|
152
|
+
}
|
|
153
|
+
const hour = +matches[1];
|
|
154
|
+
const minute = +matches[2];
|
|
155
|
+
const second = +matches[3];
|
|
156
|
+
const timeZone = !!matches[5];
|
|
157
|
+
if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
return errors.formatTimeError({ value, pointer });
|
|
161
|
+
},
|
|
162
|
+
uri: (core, schema, value, pointer) => {
|
|
163
|
+
if (typeof value !== "string" || value === "") {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
if (validUrl.isUri(value)) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
return errors.formatURIError({ value, pointer });
|
|
170
|
+
},
|
|
171
|
+
"uri-reference": (core, schema, value, pointer) => {
|
|
172
|
+
if (typeof value !== "string" || value === "") {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
if (isValidURIRef.test(value)) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
return errors.formatURIReferenceError({ value, pointer });
|
|
179
|
+
},
|
|
180
|
+
"uri-template": (core, schema, value, pointer) => {
|
|
181
|
+
if (typeof value !== "string" || value === "") {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
if (isValidURITemplate.test(value)) {
|
|
84
185
|
return undefined;
|
|
85
186
|
}
|
|
86
|
-
return errors.
|
|
187
|
+
return errors.formatURITemplateError({ value, pointer });
|
|
87
188
|
},
|
|
88
189
|
url: (core, schema, value, pointer) => {
|
|
89
190
|
if (value === "" || validUrl.isWebUri(value)) {
|