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
package/lib/step.ts
CHANGED
|
@@ -2,13 +2,17 @@ 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 { JSONSchema, JSONPointer, JSONError } from "./types";
|
|
5
|
+
import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
|
|
6
6
|
import Core from "./cores/CoreInterface";
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
const stepType = {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
array: (
|
|
10
|
+
core: Core,
|
|
11
|
+
key: string | number,
|
|
12
|
+
schema: JSONSchema,
|
|
13
|
+
data: any,
|
|
14
|
+
pointer: JSONPointer
|
|
15
|
+
): JSONSchema | JSONError => {
|
|
12
16
|
const itemsType = getTypeOf(schema.items);
|
|
13
17
|
|
|
14
18
|
if (itemsType === "object") {
|
|
@@ -33,12 +37,29 @@ const stepType = {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
if (itemsType === "array") {
|
|
40
|
+
// @draft >= 7 bool schema, items:[true, false]
|
|
41
|
+
if (schema.items[key] === true) {
|
|
42
|
+
return createSchemaOf(data[key]);
|
|
43
|
+
}
|
|
44
|
+
// @draft >= 7 bool schema, items:[true, false]
|
|
45
|
+
if (schema.items[key] === false) {
|
|
46
|
+
return errors.invalidDataError({
|
|
47
|
+
key,
|
|
48
|
+
value: data[key],
|
|
49
|
+
pointer
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
if (schema.items[key]) {
|
|
37
54
|
return core.resolveRef(schema.items[key]);
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
if (schema.additionalItems === false) {
|
|
41
|
-
return errors.additionalItemsError({
|
|
58
|
+
return errors.additionalItemsError({
|
|
59
|
+
key,
|
|
60
|
+
value: data[key],
|
|
61
|
+
pointer
|
|
62
|
+
});
|
|
42
63
|
}
|
|
43
64
|
|
|
44
65
|
if (schema.additionalItems === true || schema.additionalItems === undefined) {
|
|
@@ -49,7 +70,13 @@ const stepType = {
|
|
|
49
70
|
return schema.additionalItems;
|
|
50
71
|
}
|
|
51
72
|
|
|
52
|
-
throw new Error(
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Invalid schema ${JSON.stringify(schema, null, 4)} for ${JSON.stringify(
|
|
75
|
+
data,
|
|
76
|
+
null,
|
|
77
|
+
4
|
|
78
|
+
)}`
|
|
79
|
+
);
|
|
53
80
|
}
|
|
54
81
|
|
|
55
82
|
if (schema.additionalItems !== false && data[key]) {
|
|
@@ -61,14 +88,19 @@ const stepType = {
|
|
|
61
88
|
return new Error(`Invalid array schema for ${key} at ${pointer}`) as JSONError;
|
|
62
89
|
},
|
|
63
90
|
|
|
64
|
-
object: (
|
|
65
|
-
|
|
91
|
+
object: (
|
|
92
|
+
core: Core,
|
|
93
|
+
key: string | number,
|
|
94
|
+
schema: JSONSchema,
|
|
95
|
+
data: any,
|
|
96
|
+
pointer: JSONPointer
|
|
97
|
+
): JSONSchema | JSONError => {
|
|
66
98
|
if (Array.isArray(schema.oneOf)) {
|
|
67
99
|
// update current schema
|
|
68
100
|
const oneOfSchema = core.resolveOneOf(data, schema, pointer);
|
|
69
101
|
// resolveOneOf does currently not apply merge with base schema
|
|
70
102
|
schema = merge(schema, oneOfSchema);
|
|
71
|
-
if (schema
|
|
103
|
+
if (isJSONError(schema)) {
|
|
72
104
|
return schema;
|
|
73
105
|
}
|
|
74
106
|
}
|
|
@@ -76,7 +108,7 @@ const stepType = {
|
|
|
76
108
|
if (Array.isArray(schema.anyOf)) {
|
|
77
109
|
// update current schema
|
|
78
110
|
schema = core.resolveAnyOf(data, schema, pointer);
|
|
79
|
-
if (schema
|
|
111
|
+
if (isJSONError(schema)) {
|
|
80
112
|
return schema;
|
|
81
113
|
}
|
|
82
114
|
}
|
|
@@ -84,7 +116,7 @@ const stepType = {
|
|
|
84
116
|
if (Array.isArray(schema.allOf)) {
|
|
85
117
|
// update current schema
|
|
86
118
|
schema = core.resolveAllOf(data, schema, pointer);
|
|
87
|
-
if (schema
|
|
119
|
+
if (isJSONError(schema)) {
|
|
88
120
|
return schema;
|
|
89
121
|
}
|
|
90
122
|
}
|
|
@@ -96,7 +128,7 @@ const stepType = {
|
|
|
96
128
|
// @todo patternProperties also validate properties
|
|
97
129
|
|
|
98
130
|
targetSchema = core.resolveRef(schema.properties[key]);
|
|
99
|
-
if (targetSchema
|
|
131
|
+
if (isJSONError(targetSchema)) {
|
|
100
132
|
return targetSchema;
|
|
101
133
|
}
|
|
102
134
|
|
|
@@ -104,9 +136,17 @@ const stepType = {
|
|
|
104
136
|
if (targetSchema && Array.isArray(targetSchema.oneOf)) {
|
|
105
137
|
// @special case: this is a mix of a schema and optional definitions
|
|
106
138
|
// we resolve the schema here and add the original schema to `oneOfSchema`
|
|
107
|
-
let resolvedSchema = core.resolveOneOf(
|
|
139
|
+
let resolvedSchema = core.resolveOneOf(
|
|
140
|
+
data[key],
|
|
141
|
+
targetSchema,
|
|
142
|
+
`${pointer}/${key}`
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const oneOfIndex = targetSchema.oneOf.findIndex((s) => s === resolvedSchema);
|
|
146
|
+
|
|
108
147
|
resolvedSchema = JSON.parse(JSON.stringify(resolvedSchema));
|
|
109
148
|
resolvedSchema.variableSchema = true;
|
|
149
|
+
resolvedSchema.oneOfIndex = oneOfIndex;
|
|
110
150
|
resolvedSchema.oneOfSchema = targetSchema;
|
|
111
151
|
return resolvedSchema;
|
|
112
152
|
}
|
|
@@ -117,6 +157,24 @@ const stepType = {
|
|
|
117
157
|
}
|
|
118
158
|
}
|
|
119
159
|
|
|
160
|
+
// @draft <= 07
|
|
161
|
+
const { dependencies } = schema;
|
|
162
|
+
if (getTypeOf(dependencies) === "object") {
|
|
163
|
+
const dependentProperties = Object.keys(dependencies).filter(
|
|
164
|
+
(propertyName) =>
|
|
165
|
+
// data[propertyName] !== undefined &&
|
|
166
|
+
getTypeOf(dependencies[propertyName]) === "object"
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
for (let i = 0, l = dependentProperties.length; i < l; i += 1) {
|
|
170
|
+
const dependentProperty = dependentProperties[i];
|
|
171
|
+
const schema = step(core, key, dependencies[dependentProperty], data);
|
|
172
|
+
if (!isJSONError(schema)) {
|
|
173
|
+
return schema;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
120
178
|
// find matching property key
|
|
121
179
|
if (getTypeOf(schema.patternProperties) === "object") {
|
|
122
180
|
let regex;
|
|
@@ -134,14 +192,18 @@ const stepType = {
|
|
|
134
192
|
}
|
|
135
193
|
|
|
136
194
|
if (schema.additionalProperties === true) {
|
|
137
|
-
return createSchemaOf(data);
|
|
195
|
+
return createSchemaOf(data[key]);
|
|
138
196
|
}
|
|
139
197
|
|
|
140
|
-
return errors.unknownPropertyError({
|
|
198
|
+
return errors.unknownPropertyError({
|
|
199
|
+
property: key,
|
|
200
|
+
value: data,
|
|
201
|
+
// pointer: `${pointer}/${key}`,
|
|
202
|
+
pointer: `${pointer}`
|
|
203
|
+
});
|
|
141
204
|
}
|
|
142
205
|
};
|
|
143
206
|
|
|
144
|
-
|
|
145
207
|
/**
|
|
146
208
|
* Returns the json-schema of the given object property or array item.
|
|
147
209
|
* e.g. it steps by one key into the data
|
|
@@ -149,15 +211,34 @@ const stepType = {
|
|
|
149
211
|
* This helper determines the location of the property within the schema (additional properties, oneOf, ...) and
|
|
150
212
|
* returns the correct schema.
|
|
151
213
|
*
|
|
152
|
-
* @param core
|
|
153
|
-
* @param key
|
|
154
|
-
* @param schema
|
|
214
|
+
* @param core - validator
|
|
215
|
+
* @param key - property-name or array-index
|
|
216
|
+
* @param schema - json schema of current data
|
|
155
217
|
* @param data - parent of key
|
|
156
|
-
* @param [pointer]
|
|
218
|
+
* @param [pointer] - pointer to schema and data (parent of key)
|
|
157
219
|
* @return Schema or Error if failed resolving key
|
|
158
220
|
*/
|
|
159
|
-
export default function step(
|
|
160
|
-
|
|
221
|
+
export default function step(
|
|
222
|
+
core: Core,
|
|
223
|
+
key: string | number,
|
|
224
|
+
schema: JSONSchema,
|
|
225
|
+
data?: any,
|
|
226
|
+
pointer: JSONPointer = "#"
|
|
227
|
+
): JSONSchema | JSONError {
|
|
228
|
+
// @draft >= 4 ?
|
|
229
|
+
if (Array.isArray(schema.type)) {
|
|
230
|
+
const dataType = getTypeOf(data);
|
|
231
|
+
if (schema.type.includes(dataType)) {
|
|
232
|
+
return stepType[dataType](core, key, schema, data, pointer);
|
|
233
|
+
}
|
|
234
|
+
return core.errors.typeError({
|
|
235
|
+
value: data,
|
|
236
|
+
pointer,
|
|
237
|
+
expected: schema.type,
|
|
238
|
+
received: dataType
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
161
242
|
const expectedType = schema.type || getTypeOf(data);
|
|
162
243
|
if (stepType[expectedType]) {
|
|
163
244
|
return stepType[expectedType](core, key, schema, data, pointer);
|
package/lib/types.ts
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import Core from "./cores/CoreInterface";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
export type JSONSchema = { [p:string]: any };
|
|
3
|
+
export type JSONSchema = { [p: string]: any };
|
|
5
4
|
|
|
6
5
|
export type JSONPointer = string;
|
|
7
6
|
|
|
8
7
|
export type JSONError = {
|
|
9
|
-
type: "error"
|
|
8
|
+
type: "error";
|
|
10
9
|
name: string;
|
|
11
10
|
code: string;
|
|
12
11
|
message: string;
|
|
13
12
|
data?: { [p: string]: any };
|
|
14
13
|
[p: string]: any;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ts type guard for json error
|
|
18
|
+
* @returns true if passed type is a JSONError
|
|
19
|
+
*/
|
|
20
|
+
export function isJSONError(error): error is JSONError {
|
|
21
|
+
return error?.type === "error";
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
export interface JSONValidator {
|
|
18
|
-
(core: Core, schema: JSONSchema, value: any, pointer: JSONPointer):
|
|
25
|
+
(core: Core, schema: JSONSchema, value: any, pointer: JSONPointer):
|
|
26
|
+
| void
|
|
27
|
+
| undefined
|
|
28
|
+
| JSONError
|
|
29
|
+
| JSONError[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface JSONTypeValidator {
|
|
33
|
+
(core: Core, schema: JSONSchema, value: any, pointer: JSONPointer): Array<
|
|
34
|
+
void | undefined | JSONError | JSONError[]
|
|
35
|
+
>;
|
|
19
36
|
}
|
package/lib/utils/filter.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
+
import { isJSONError } from "../types";
|
|
2
|
+
|
|
1
3
|
export function isPromise(obj) {
|
|
2
4
|
return obj instanceof Promise;
|
|
3
5
|
}
|
|
4
6
|
|
|
5
|
-
export function isError(obj) {
|
|
6
|
-
return obj && obj.type === "error";
|
|
7
|
-
}
|
|
8
|
-
|
|
9
7
|
export function errorOrPromise(error) {
|
|
10
|
-
return
|
|
8
|
+
return isJSONError(error) || isPromise(error);
|
|
11
9
|
}
|
|
12
10
|
|
|
13
11
|
export function errorsOnly(error) {
|
|
14
|
-
return
|
|
12
|
+
return isJSONError(error);
|
|
15
13
|
}
|
package/lib/utils/merge.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import deepmerge from "deepmerge";
|
|
2
2
|
const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* returns a new json-schema, where properties are combined and arrays are replaced
|
|
6
|
+
*/
|
|
3
7
|
export default <T, K>(a: T, b: K): T & K => deepmerge(a, b, { arrayMerge: overwriteMerge });
|
package/lib/validate.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import getTypeOf from "./getTypeOf";
|
|
1
|
+
import getTypeOf, { JSType } from "./getTypeOf";
|
|
2
2
|
import { errorOrPromise } from "./utils/filter";
|
|
3
3
|
import flattenArray from "./utils/flattenArray";
|
|
4
|
-
import { JSONSchema, JSONPointer, JSONError } from "./types";
|
|
4
|
+
import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
|
|
5
5
|
import Core from "./cores/CoreInterface";
|
|
6
|
+
import equal from "fast-deep-equal";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let jsType = getTypeOf(value);
|
|
10
|
-
|
|
8
|
+
function getJsonSchemaType(value: unknown, expectedType: string | string[]): JSType | "integer" {
|
|
9
|
+
const jsType = getTypeOf(value);
|
|
11
10
|
if (
|
|
12
|
-
jsType === "number" &&
|
|
13
|
-
(
|
|
11
|
+
jsType === "number" &&
|
|
12
|
+
(expectedType === "integer" ||
|
|
13
|
+
(Array.isArray(expectedType) && expectedType.includes("integer")))
|
|
14
14
|
) {
|
|
15
|
-
|
|
15
|
+
return Number.isInteger(value) ? "integer" : "number";
|
|
16
16
|
}
|
|
17
17
|
return jsType;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
/**
|
|
22
21
|
* Validate data by a json schema
|
|
23
22
|
*
|
|
@@ -27,18 +26,49 @@ function getJsonSchemaType(value, expectedType) {
|
|
|
27
26
|
* @param [pointer] - json pointer pointing to value (used for error-messages only)
|
|
28
27
|
* @return list of errors or empty
|
|
29
28
|
*/
|
|
30
|
-
export default function validate(
|
|
31
|
-
|
|
29
|
+
export default function validate(
|
|
30
|
+
core: Core,
|
|
31
|
+
value: unknown,
|
|
32
|
+
schema: JSONSchema = core.rootSchema,
|
|
33
|
+
pointer: JSONPointer = "#"
|
|
34
|
+
): Array<JSONError> {
|
|
35
|
+
schema = core.resolveRef(schema);
|
|
36
|
+
|
|
37
|
+
// this is a high level v7 schema validation
|
|
38
|
+
if (getTypeOf(schema) === "boolean") {
|
|
39
|
+
if (schema) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return [core.errors.invalidDataError({ value, pointer })];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isJSONError(schema)) {
|
|
32
46
|
return [schema as JSONError];
|
|
33
47
|
}
|
|
34
48
|
|
|
35
|
-
|
|
49
|
+
// @draft >= 6 const
|
|
50
|
+
if (schema.const !== undefined) {
|
|
51
|
+
if (equal(schema.const, value)) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
return [core.errors.constError({ value, expected: schema.const, pointer })];
|
|
55
|
+
}
|
|
36
56
|
|
|
37
57
|
const receivedType = getJsonSchemaType(value, schema.type);
|
|
38
58
|
const expectedType = schema.type || receivedType;
|
|
39
59
|
|
|
40
|
-
if (
|
|
41
|
-
|
|
60
|
+
if (
|
|
61
|
+
receivedType !== expectedType &&
|
|
62
|
+
(!Array.isArray(expectedType) || !expectedType.includes(receivedType))
|
|
63
|
+
) {
|
|
64
|
+
return [
|
|
65
|
+
core.errors.typeError({
|
|
66
|
+
received: receivedType,
|
|
67
|
+
expected: expectedType,
|
|
68
|
+
value,
|
|
69
|
+
pointer
|
|
70
|
+
})
|
|
71
|
+
];
|
|
42
72
|
}
|
|
43
73
|
|
|
44
74
|
if (core.validateType[receivedType] == null) {
|
package/lib/validateAsync.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errorsOnly } from "./utils/filter";
|
|
2
2
|
import flattenArray from "./utils/flattenArray";
|
|
3
|
-
import { JSONSchema, JSONPointer, JSONError } from "./types";
|
|
3
|
+
import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types";
|
|
4
4
|
import Core from "./cores/CoreInterface";
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
function createErrorNotification(onError: OnError) {
|
|
8
7
|
return function notifyError(error) {
|
|
9
8
|
if (Array.isArray(error)) {
|
|
@@ -11,14 +10,13 @@ function createErrorNotification(onError: OnError) {
|
|
|
11
10
|
error.forEach(notifyError);
|
|
12
11
|
return error;
|
|
13
12
|
}
|
|
14
|
-
if (
|
|
13
|
+
if (isJSONError(error)) {
|
|
15
14
|
onError(error);
|
|
16
15
|
}
|
|
17
16
|
return error;
|
|
18
17
|
};
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
20
|
export interface OnError {
|
|
23
21
|
(error: JSONError): void;
|
|
24
22
|
}
|
|
@@ -27,7 +25,7 @@ export type Options = {
|
|
|
27
25
|
schema?: JSONSchema;
|
|
28
26
|
pointer?: JSONPointer;
|
|
29
27
|
onError?: OnError;
|
|
30
|
-
}
|
|
28
|
+
};
|
|
31
29
|
|
|
32
30
|
/**
|
|
33
31
|
* @async
|
|
@@ -41,7 +39,11 @@ export type Options = {
|
|
|
41
39
|
* @param options.onError - will be called for each error as soon as it is resolved
|
|
42
40
|
* @return list of errors or empty
|
|
43
41
|
*/
|
|
44
|
-
export default function validateAsync(
|
|
42
|
+
export default function validateAsync(
|
|
43
|
+
core: Core,
|
|
44
|
+
value: any,
|
|
45
|
+
options?: Options
|
|
46
|
+
): Promise<Array<JSONError>> {
|
|
45
47
|
const { schema, pointer, onError } = { schema: core.rootSchema, pointer: "#", ...options };
|
|
46
48
|
|
|
47
49
|
let errors: Array<JSONError> = core.validate(value, schema, pointer);
|
|
@@ -51,17 +53,16 @@ export default function validateAsync(core: Core, value: any, options?: Options)
|
|
|
51
53
|
for (let i = 0; i < errors.length; i += 1) {
|
|
52
54
|
if (errors[i] instanceof Promise) {
|
|
53
55
|
errors[i].then(notifyError);
|
|
54
|
-
} else if (
|
|
56
|
+
} else if (isJSONError(errors[i])) {
|
|
55
57
|
onError(errors[i]);
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
return Promise
|
|
61
|
-
.all(errors)
|
|
62
|
+
return Promise.all(errors)
|
|
62
63
|
.then(flattenArray)
|
|
63
|
-
.then(resolvedErrors => resolvedErrors.filter(errorsOnly))
|
|
64
|
-
.catch(e => {
|
|
64
|
+
.then((resolvedErrors) => resolvedErrors.filter(errorsOnly))
|
|
65
|
+
.catch((e) => {
|
|
65
66
|
console.log("Failed resolving promises", e.message);
|
|
66
67
|
console.log(e.stack);
|
|
67
68
|
throw e;
|
package/lib/validation/errors.ts
CHANGED
|
@@ -7,17 +7,30 @@ const errors: { [p: string]: CreateError } = {
|
|
|
7
7
|
additionalPropertiesError: createCustomError("AdditionalPropertiesError"),
|
|
8
8
|
anyOfError: createCustomError("AnyOfError"),
|
|
9
9
|
allOfError: createCustomError("AllOfError"),
|
|
10
|
+
constError: createCustomError("ConstError"),
|
|
11
|
+
containsError: createCustomError("ContainsError"),
|
|
12
|
+
containsArrayError: createCustomError("ContainsArrayError"),
|
|
13
|
+
containsAnyError: createCustomError("ContainsAnyError"),
|
|
10
14
|
enumError: createCustomError("EnumError"),
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
formatURLError: createCustomError("FormatURLError"),
|
|
16
|
+
formatURIError: createCustomError("FormatURIError"),
|
|
17
|
+
formatURIReferenceError: createCustomError("FormatURIReferenceError"),
|
|
18
|
+
formatURITemplateError: createCustomError("FormatURITemplateError"),
|
|
19
|
+
formatDateError: createCustomError("FormatDateaError"),
|
|
13
20
|
formatDateTimeError: createCustomError("FormatDateTimeError"),
|
|
14
21
|
formatEmailError: createCustomError("FormatEmailError"),
|
|
15
22
|
formatHostnameError: createCustomError("FormatHostnameError"),
|
|
16
23
|
formatIPV4Error: createCustomError("FormatIPV4Error"),
|
|
24
|
+
formatIPV4LeadingZeroError: createCustomError("FormatIPV4LeadingZeroError"),
|
|
17
25
|
formatIPV6Error: createCustomError("FormatIPV6Error"),
|
|
26
|
+
formatIPV6LeadingZeroError: createCustomError("FormatIPV6LeadingZeroError"),
|
|
27
|
+
formatJSONPointerError: createCustomError("FormatJSONPointerError"),
|
|
18
28
|
formatRegExError: createCustomError("FormatRegExError"),
|
|
29
|
+
formatTimeError: createCustomError("FormatTimeError"),
|
|
19
30
|
invalidSchemaError: createCustomError("InvalidSchemaError"),
|
|
31
|
+
invalidDataError: createCustomError("InvalidDataError"),
|
|
20
32
|
invalidTypeError: createCustomError("InvalidTypeError"),
|
|
33
|
+
invalidPropertyNameError: createCustomError("InvalidPropertyNameError"),
|
|
21
34
|
maximumError: createCustomError("MaximumError"),
|
|
22
35
|
maxItemsError: createCustomError("MaxItemsError"),
|
|
23
36
|
maxLengthError: createCustomError("MaxLengthError"),
|
package/lib/validation/format.ts
CHANGED
|
@@ -1,17 +1,50 @@
|
|
|
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
4
|
|
|
5
|
+
// referenced
|
|
6
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
7
|
+
|
|
5
8
|
// https://gist.github.com/marcelotmelo/b67f58a08bee6c2468f8
|
|
6
9
|
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]))$");
|
|
7
10
|
const isValidIPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
|
|
8
11
|
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;
|
|
9
12
|
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])?)*\.?$/;
|
|
13
|
+
const matchDate = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
14
|
+
const matchTime = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
|
|
15
|
+
const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
16
|
+
|
|
17
|
+
const isValidJSONPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
|
|
18
|
+
const isValidRelativeJSONPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
|
|
19
|
+
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;
|
|
20
|
+
// uri-template: https://tools.ietf.org/html/rfc6570
|
|
21
|
+
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;
|
|
10
22
|
|
|
11
23
|
|
|
12
24
|
// Default JSON-Schema formats: date-time, email, hostname, ipv4, ipv6, uri, uriref
|
|
13
25
|
export default {
|
|
14
26
|
|
|
27
|
+
date: (core, schema, value, pointer) => {
|
|
28
|
+
if (typeof value !== "string") {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
32
|
+
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
33
|
+
const matches = value.match(matchDate);
|
|
34
|
+
if (!matches) {
|
|
35
|
+
return errors.formatDateTimeError({ value, pointer });
|
|
36
|
+
}
|
|
37
|
+
const year = +matches[1];
|
|
38
|
+
const month = +matches[2];
|
|
39
|
+
const day = +matches[3];
|
|
40
|
+
// https://tools.ietf.org/html/rfc3339#appendix-C
|
|
41
|
+
const isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
42
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= (month == 2 && isLeapYear ? 29 : DAYS[month])) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return errors.formatDateError({ value, pointer });
|
|
46
|
+
},
|
|
47
|
+
|
|
15
48
|
"date-time": (core, schema, value, pointer) => {
|
|
16
49
|
if (typeof value !== "string") {
|
|
17
50
|
return undefined;
|
|
@@ -63,6 +96,10 @@ export default {
|
|
|
63
96
|
if (typeof value !== "string" || value === "") {
|
|
64
97
|
return undefined;
|
|
65
98
|
}
|
|
99
|
+
if (value && value[0] === "0") {
|
|
100
|
+
// leading zeroes should be rejected, as they are treated as octals
|
|
101
|
+
return errors.formatIPV4LeadingZeroError({ value, pointer });
|
|
102
|
+
}
|
|
66
103
|
if (value.length <= 15 && isValidIPV4.test(value)) {
|
|
67
104
|
return undefined;
|
|
68
105
|
}
|
|
@@ -73,27 +110,99 @@ export default {
|
|
|
73
110
|
if (typeof value !== "string" || value === "") {
|
|
74
111
|
return undefined;
|
|
75
112
|
}
|
|
113
|
+
if (value && value[0] === "0") {
|
|
114
|
+
// leading zeroes should be rejected, as they are treated as octals
|
|
115
|
+
return errors.formatIPV6LeadingZeroError({ value, pointer });
|
|
116
|
+
}
|
|
76
117
|
if (value.length <= 45 && isValidIPV6.test(value)) {
|
|
77
118
|
return undefined;
|
|
78
119
|
}
|
|
79
120
|
return errors.formatIPV6Error({ value, pointer });
|
|
80
121
|
},
|
|
81
122
|
|
|
123
|
+
"json-pointer": (core, schema, value, pointer) => {
|
|
124
|
+
if (typeof value !== "string" || value === "") {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
if (isValidJSONPointer.test(value)) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
return errors.formatJSONPointerError({ value, pointer });
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
"relative-json-pointer": (core, schema, value, pointer) => {
|
|
134
|
+
if (typeof value !== "string" || value === "") {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
if (isValidRelativeJSONPointer.test(value)) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return errors.formatJSONPointerError({ value, pointer });
|
|
141
|
+
},
|
|
142
|
+
|
|
82
143
|
regex: (core, schema, value, pointer) => {
|
|
83
144
|
if (typeof value === "string" && /\\Z$/.test(value) === false) {
|
|
145
|
+
try {
|
|
146
|
+
new RegExp(value);
|
|
147
|
+
return undefined;
|
|
148
|
+
} catch (e) {} // eslint-disable-line no-empty
|
|
149
|
+
|
|
150
|
+
return errors.formatRegExError({ value, pointer });
|
|
151
|
+
}
|
|
152
|
+
// v7 tests, ignore non-regex values
|
|
153
|
+
if (typeof value === "object" || typeof value === "number" || Array.isArray(value)) {
|
|
84
154
|
return undefined;
|
|
85
155
|
}
|
|
86
156
|
return errors.formatRegExError({ value, pointer });
|
|
87
157
|
},
|
|
88
158
|
|
|
89
|
-
|
|
159
|
+
time: (core, schema, value, pointer) => {
|
|
90
160
|
if (typeof value !== "string") {
|
|
91
161
|
return undefined;
|
|
92
162
|
}
|
|
93
|
-
|
|
163
|
+
// https://github.com/cfworker/cfworker/blob/main/packages/json-schema/src/format.ts
|
|
164
|
+
const matches = value.match(matchTime);
|
|
165
|
+
if (!matches) {
|
|
166
|
+
return errors.formatDateTimeError({ value, pointer });
|
|
167
|
+
}
|
|
168
|
+
const hour = +matches[1];
|
|
169
|
+
const minute = +matches[2];
|
|
170
|
+
const second = +matches[3];
|
|
171
|
+
const timeZone = !!matches[5];
|
|
172
|
+
if (((hour <= 23 && minute <= 59 && second <= 59) || (hour == 23 && minute == 59 && second == 60)) && timeZone) {
|
|
173
|
+
return undefined
|
|
174
|
+
}
|
|
175
|
+
return errors.formatTimeError({ value, pointer });
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
uri: (core, schema, value, pointer) => {
|
|
179
|
+
if (typeof value !== "string" || value === "") {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
if (validUrl.isUri(value)) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
return errors.formatURIError({ value, pointer });
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
"uri-reference": (core, schema, value, pointer) => {
|
|
189
|
+
if (typeof value !== "string" || value === "") {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
if (isValidURIRef.test(value)) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return errors.formatURIReferenceError({ value, pointer });
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
"uri-template": (core, schema, value, pointer) => {
|
|
199
|
+
if (typeof value !== "string" || value === "") {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
if (isValidURITemplate.test(value)) {
|
|
94
203
|
return undefined;
|
|
95
204
|
}
|
|
96
|
-
return errors.
|
|
205
|
+
return errors.formatURITemplateError({ value, pointer });
|
|
97
206
|
},
|
|
98
207
|
|
|
99
208
|
url: (core, schema, value, pointer) => {
|