z-schema 12.2.0 → 12.4.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/README.md +2 -2
- package/bin/z-schema +1 -1
- package/cjs/{index.js → index.cjs} +838 -707
- package/cjs/{index.d.ts → index.d.cts} +47 -26
- package/dist/{errors.d.mts → errors.d.ts} +2 -2
- package/dist/{errors.mjs → errors.js} +1 -2
- package/dist/{format-validators.mjs → format-validators.js} +60 -43
- package/dist/{index.d.mts → index.d.ts} +9 -9
- package/dist/{index.mjs → index.js} +3 -3
- package/dist/{json-schema-versions.d.mts → json-schema-versions.d.ts} +34 -3
- package/dist/{json-schema.d.mts → json-schema.d.ts} +7 -7
- package/dist/{json-schema.mjs → json-schema.js} +6 -5
- package/dist/{json-validation.mjs → json-validation.js} +144 -127
- package/dist/{report.d.mts → report.d.ts} +8 -9
- package/dist/{report.mjs → report.js} +28 -31
- package/dist/{schema-cache.d.mts → schema-cache.d.ts} +5 -5
- package/dist/{schema-cache.mjs → schema-cache.js} +10 -11
- package/dist/{schema-compiler.d.mts → schema-compiler.d.ts} +4 -4
- package/dist/{schema-compiler.mjs → schema-compiler.js} +101 -79
- package/dist/{schema-validator.d.mts → schema-validator.d.ts} +5 -5
- package/dist/{schema-validator.mjs → schema-validator.js} +138 -166
- package/dist/utils/{array.mjs → array.js} +4 -3
- package/dist/utils/{base64.mjs → base64.js} +3 -2
- package/dist/utils/{clone.mjs → clone.js} +18 -20
- package/dist/utils/hostname.js +176 -0
- package/dist/utils/{json.mjs → json.js} +13 -8
- package/dist/utils/{schema-regex.mjs → schema-regex.js} +5 -5
- package/dist/utils/{time.mjs → time.js} +8 -8
- package/dist/utils/unicode.js +22 -0
- package/dist/utils/{what-is.mjs → what-is.js} +1 -2
- package/dist/validation/{array.mjs → array.js} +18 -20
- package/dist/validation/{combinators.mjs → combinators.js} +16 -16
- package/dist/validation/{numeric.mjs → numeric.js} +11 -11
- package/dist/validation/{object.mjs → object.js} +35 -34
- package/dist/validation/{ref.mjs → ref.js} +5 -5
- package/dist/validation/{shared.mjs → shared.js} +15 -12
- package/dist/validation/{string.mjs → string.js} +35 -35
- package/dist/validation/type.js +34 -0
- package/dist/{z-schema-base.d.mts → z-schema-base.d.ts} +11 -12
- package/dist/{z-schema-base.mjs → z-schema-base.js} +45 -40
- package/dist/{z-schema-options.d.mts → z-schema-options.d.ts} +3 -3
- package/dist/{z-schema-options.mjs → z-schema-options.js} +5 -5
- package/dist/{z-schema-reader.d.mts → z-schema-reader.d.ts} +1 -1
- package/dist/{z-schema-versions.mjs → z-schema-versions.js} +21 -21
- package/dist/{z-schema.d.mts → z-schema.d.ts} +5 -13
- package/dist/{z-schema.mjs → z-schema.js} +38 -48
- package/package.json +24 -25
- package/src/errors.ts +1 -2
- package/src/format-validators.ts +173 -67
- package/src/json-schema-versions.ts +56 -2
- package/src/json-schema.ts +10 -9
- package/src/json-validation.ts +189 -146
- package/src/report.ts +37 -49
- package/src/schema-cache.ts +13 -13
- package/src/schema-compiler.ts +170 -117
- package/src/schema-validator.ts +239 -238
- package/src/utils/array.ts +9 -6
- package/src/utils/base64.ts +13 -2
- package/src/utils/clone.ts +28 -30
- package/src/utils/date.ts +6 -3
- package/src/utils/hostname.ts +257 -31
- package/src/utils/json.ts +16 -9
- package/src/utils/properties.ts +2 -2
- package/src/utils/schema-regex.ts +4 -4
- package/src/utils/time.ts +8 -7
- package/src/utils/unicode.ts +12 -5
- package/src/utils/what-is.ts +1 -5
- package/src/validation/array.ts +24 -22
- package/src/validation/combinators.ts +14 -14
- package/src/validation/numeric.ts +14 -28
- package/src/validation/object.ts +32 -36
- package/src/validation/ref.ts +5 -6
- package/src/validation/shared.ts +22 -21
- package/src/validation/string.ts +36 -42
- package/src/validation/type.ts +17 -17
- package/src/z-schema-base.ts +49 -38
- package/src/z-schema-options.ts +4 -3
- package/src/z-schema.ts +35 -45
- package/umd/ZSchema.js +853 -715
- package/umd/ZSchema.min.js +2 -2
- package/umd/package.json +3 -0
- package/dist/utils/hostname.mjs +0 -75
- package/dist/utils/unicode.mjs +0 -12
- package/dist/validation/type.mjs +0 -32
- /package/dist/{format-validators.d.mts → format-validators.d.ts} +0 -0
- /package/dist/{json-schema-versions.mjs → json-schema-versions.js} +0 -0
- /package/dist/schemas/{draft-04-schema.mjs → draft-04-schema.js} +0 -0
- /package/dist/schemas/{draft-06-schema.mjs → draft-06-schema.js} +0 -0
- /package/dist/schemas/{draft-07-schema.mjs → draft-07-schema.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-applicator.mjs → draft-2019-09-meta-applicator.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-content.mjs → draft-2019-09-meta-content.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-core.mjs → draft-2019-09-meta-core.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-format.mjs → draft-2019-09-meta-format.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-meta-data.mjs → draft-2019-09-meta-meta-data.js} +0 -0
- /package/dist/schemas/{draft-2019-09-meta-validation.mjs → draft-2019-09-meta-validation.js} +0 -0
- /package/dist/schemas/{draft-2019-09-schema.mjs → draft-2019-09-schema.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-applicator.mjs → draft-2020-12-meta-applicator.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-content.mjs → draft-2020-12-meta-content.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-core.mjs → draft-2020-12-meta-core.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-format-annotation.mjs → draft-2020-12-meta-format-annotation.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-format-assertion.mjs → draft-2020-12-meta-format-assertion.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-meta-data.mjs → draft-2020-12-meta-meta-data.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-unevaluated.mjs → draft-2020-12-meta-unevaluated.js} +0 -0
- /package/dist/schemas/{draft-2020-12-meta-validation.mjs → draft-2020-12-meta-validation.js} +0 -0
- /package/dist/schemas/{draft-2020-12-schema.mjs → draft-2020-12-schema.js} +0 -0
- /package/dist/utils/{constants.mjs → constants.js} +0 -0
- /package/dist/utils/{date.mjs → date.js} +0 -0
- /package/dist/utils/{properties.mjs → properties.js} +0 -0
- /package/dist/utils/{symbols.mjs → symbols.js} +0 -0
- /package/dist/utils/{uri.mjs → uri.js} +0 -0
- /package/dist/{z-schema-reader.mjs → z-schema-reader.js} +0 -0
package/src/validation/shared.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ErrorCode } from '../errors.js';
|
|
1
2
|
import type { JsonSchema, JsonSchemaAll, JsonSchemaInternal, JsonSchemaVersion } from '../json-schema-versions.js';
|
|
2
3
|
// JsonSchemaAll is retained for the VALIDATION_VOCAB_KEYWORDS Set key type.
|
|
3
4
|
import type { Report } from '../report.js';
|
|
@@ -9,22 +10,17 @@ import { isObject } from '../utils/what-is.js';
|
|
|
9
10
|
// Shared types
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
11
12
|
|
|
12
|
-
export type JsonValidatorFn = (
|
|
13
|
+
export type JsonValidatorFn = (ctx: ZSchemaBase, report: Report, schema: JsonSchema, json: unknown) => void;
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Draft / vocabulary helpers
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
|
|
18
|
-
export const shouldSkipValidate =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
!errors.some(function (err: any) {
|
|
24
|
-
return options.includeErrors!.includes(err);
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
};
|
|
19
|
+
export const shouldSkipValidate = (options: ValidateOptions, errors: readonly ErrorCode[]) =>
|
|
20
|
+
options &&
|
|
21
|
+
Array.isArray(options.includeErrors) &&
|
|
22
|
+
options.includeErrors.length > 0 &&
|
|
23
|
+
!errors.some((err) => options.includeErrors!.includes(err));
|
|
28
24
|
|
|
29
25
|
export const supportsDependentKeywords = (
|
|
30
26
|
schema: JsonSchemaInternal,
|
|
@@ -89,12 +85,12 @@ export const isValidationVocabularyEnabled = (
|
|
|
89
85
|
return true;
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
const vocabulary = metaSchema.$vocabulary
|
|
88
|
+
const vocabulary = metaSchema.$vocabulary;
|
|
93
89
|
const has2019 = Object.hasOwn(vocabulary, VOCAB_VALIDATION_2019_09);
|
|
94
90
|
const has2020 = Object.hasOwn(vocabulary, VOCAB_VALIDATION_2020_12);
|
|
95
91
|
|
|
96
92
|
if (has2019 || has2020) {
|
|
97
|
-
return vocabulary[VOCAB_VALIDATION_2019_09]
|
|
93
|
+
return vocabulary[VOCAB_VALIDATION_2019_09] || vocabulary[VOCAB_VALIDATION_2020_12];
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
return false;
|
|
@@ -124,16 +120,16 @@ export const isFormatAssertionVocabEnabled = (
|
|
|
124
120
|
return false; // no vocabulary info, default to annotation-only for modern drafts
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
const vocabulary = metaSchema.$vocabulary
|
|
123
|
+
const vocabulary = metaSchema.$vocabulary;
|
|
128
124
|
|
|
129
125
|
// For draft 2020-12, only the format-assertion vocabulary enables format as assertion
|
|
130
126
|
if (Object.hasOwn(vocabulary, VOCAB_FORMAT_ASSERTION_2020_12)) {
|
|
131
|
-
return vocabulary[VOCAB_FORMAT_ASSERTION_2020_12]
|
|
127
|
+
return vocabulary[VOCAB_FORMAT_ASSERTION_2020_12];
|
|
132
128
|
}
|
|
133
129
|
|
|
134
130
|
// For draft 2019-09, check if the format vocabulary is enabled (true)
|
|
135
131
|
if (Object.hasOwn(vocabulary, VOCAB_FORMAT_2019_09)) {
|
|
136
|
-
return vocabulary[VOCAB_FORMAT_2019_09]
|
|
132
|
+
return vocabulary[VOCAB_FORMAT_2019_09];
|
|
137
133
|
}
|
|
138
134
|
|
|
139
135
|
return false; // default to annotation-only for modern drafts
|
|
@@ -170,17 +166,22 @@ export function getCachedValidationResult(report: Report, schema: unknown, json:
|
|
|
170
166
|
*/
|
|
171
167
|
export function deferOrRunSync(report: Report, subReports: Report[], decisionFn: () => void): void {
|
|
172
168
|
const asyncTasksBefore = report.asyncTasks.length;
|
|
173
|
-
for (
|
|
174
|
-
|
|
169
|
+
for (let i = 0; i < subReports.length; i++) {
|
|
170
|
+
const tasks = subReports[i].asyncTasks;
|
|
171
|
+
for (let j = 0; j < tasks.length; j++) {
|
|
172
|
+
report.asyncTasks.push(tasks[j]);
|
|
173
|
+
}
|
|
175
174
|
}
|
|
176
175
|
const hasAsyncTasks = report.asyncTasks.length > asyncTasksBefore;
|
|
177
176
|
|
|
178
177
|
if (hasAsyncTasks) {
|
|
179
178
|
report.addAsyncTaskWithPath(
|
|
180
|
-
(callback) => {
|
|
181
|
-
setTimeout(() =>
|
|
179
|
+
(callback: (result: unknown) => void) => {
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
callback(null);
|
|
182
|
+
}, 0);
|
|
182
183
|
},
|
|
183
|
-
[]
|
|
184
|
+
[],
|
|
184
185
|
() => {
|
|
185
186
|
decisionFn();
|
|
186
187
|
}
|
package/src/validation/string.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { JsonSchemaInternal } from '../json-schema-versions.js';
|
|
|
2
2
|
import type { Report } from '../report.js';
|
|
3
3
|
import type { ZSchemaBase } from '../z-schema-base.js';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { resolveFormatValidator } from '../format-validators.js';
|
|
6
6
|
import { decodeBase64, isValidBase64 } from '../utils/base64.js';
|
|
7
7
|
import { compileSchemaRegex } from '../utils/schema-regex.js';
|
|
8
8
|
import { unicodeLength } from '../utils/unicode.js';
|
|
@@ -13,9 +13,9 @@ import { isFormatAssertionVocabEnabled, shouldSkipValidate } from './shared.js';
|
|
|
13
13
|
// minLength
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
|
|
16
|
-
export function minLengthValidator(
|
|
16
|
+
export function minLengthValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
17
17
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.2.2
|
|
18
|
-
if (shouldSkipValidate(
|
|
18
|
+
if (shouldSkipValidate(ctx.validateOptions, ['MIN_LENGTH'])) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
if (typeof json !== 'string') {
|
|
@@ -30,9 +30,9 @@ export function minLengthValidator(this: ZSchemaBase, report: Report, schema: Js
|
|
|
30
30
|
// maxLength
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
32
|
|
|
33
|
-
export function maxLengthValidator(
|
|
33
|
+
export function maxLengthValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
34
34
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1.2
|
|
35
|
-
if (shouldSkipValidate(
|
|
35
|
+
if (shouldSkipValidate(ctx.validateOptions, ['MAX_LENGTH'])) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
if (typeof json !== 'string') {
|
|
@@ -47,9 +47,9 @@ export function maxLengthValidator(this: ZSchemaBase, report: Report, schema: Js
|
|
|
47
47
|
// pattern
|
|
48
48
|
// ---------------------------------------------------------------------------
|
|
49
49
|
|
|
50
|
-
export function patternValidator(
|
|
50
|
+
export function patternValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
51
51
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.3.2
|
|
52
|
-
if (shouldSkipValidate(
|
|
52
|
+
if (shouldSkipValidate(ctx.validateOptions, ['PATTERN'])) {
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
if (typeof json !== 'string') {
|
|
@@ -70,27 +70,24 @@ export function patternValidator(this: ZSchemaBase, report: Report, schema: Json
|
|
|
70
70
|
// format
|
|
71
71
|
// ---------------------------------------------------------------------------
|
|
72
72
|
|
|
73
|
-
export function formatValidator(
|
|
73
|
+
export function formatValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
74
74
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.2
|
|
75
|
-
if (
|
|
75
|
+
if (ctx.options.formatAssertions === false) {
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// When formatAssertions is explicitly true, respect the meta-schema vocabulary:
|
|
80
80
|
// for draft 2019-09/2020-12, format is annotation-only unless the format-assertion
|
|
81
81
|
// vocabulary is enabled in the meta-schema.
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
82
|
+
if (ctx.options.formatAssertions === true && !isFormatAssertionVocabEnabled(schema, report, ctx.options.version)) {
|
|
83
|
+
return;
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
const isModernDraft =
|
|
86
|
+
const isModernDraft = ctx.options.version === 'draft2019-09' || ctx.options.version === 'draft2020-12';
|
|
89
87
|
|
|
90
|
-
const
|
|
91
|
-
const formatValidatorFn = formatValidators[schema.format!];
|
|
88
|
+
const formatValidatorFn = resolveFormatValidator(schema.format!, ctx.options);
|
|
92
89
|
if (typeof formatValidatorFn === 'function') {
|
|
93
|
-
if (shouldSkipValidate(
|
|
90
|
+
if (shouldSkipValidate(ctx.validateOptions, ['INVALID_FORMAT'])) {
|
|
94
91
|
return;
|
|
95
92
|
}
|
|
96
93
|
if (report.hasError('INVALID_TYPE', [schema.type, whatIs(json)])) {
|
|
@@ -98,40 +95,42 @@ export function formatValidator(this: ZSchemaBase, report: Report, schema: JsonS
|
|
|
98
95
|
}
|
|
99
96
|
if (formatValidatorFn.length === 2) {
|
|
100
97
|
// callback-based async
|
|
101
|
-
report.addAsyncTaskWithPath(formatValidatorFn, [json],
|
|
98
|
+
report.addAsyncTaskWithPath(formatValidatorFn, [json], (result) => {
|
|
102
99
|
if (result !== true) {
|
|
103
100
|
report.addError('INVALID_FORMAT', [schema.format!, JSON.stringify(json)], undefined, schema, 'format');
|
|
104
101
|
}
|
|
105
102
|
});
|
|
106
103
|
} else {
|
|
107
|
-
const result = formatValidatorFn.call(
|
|
104
|
+
const result = formatValidatorFn.call(ctx, json);
|
|
108
105
|
if (result instanceof Promise) {
|
|
109
106
|
// Promise-based async
|
|
110
107
|
const promiseResult = result;
|
|
111
108
|
report.addAsyncTaskWithPath(
|
|
112
|
-
async (callback) => {
|
|
109
|
+
async (callback: (result: unknown) => void) => {
|
|
110
|
+
let resolved: unknown = false;
|
|
113
111
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
callback(false);
|
|
112
|
+
resolved = await promiseResult;
|
|
113
|
+
} catch {
|
|
114
|
+
// A rejected validator promise is treated as an invalid result (false).
|
|
118
115
|
}
|
|
116
|
+
// Terminal callback of the async task; `return callback(...)` would trip
|
|
117
|
+
// no-confusing-void-expression since the callback returns void.
|
|
118
|
+
// oxlint-disable-next-line node/callback-return
|
|
119
|
+
callback(resolved);
|
|
119
120
|
},
|
|
120
|
-
[]
|
|
121
|
-
|
|
121
|
+
[],
|
|
122
|
+
(resolvedResult) => {
|
|
122
123
|
if (resolvedResult !== true) {
|
|
123
124
|
report.addError('INVALID_FORMAT', [schema.format!, JSON.stringify(json)], undefined, schema, 'format');
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
);
|
|
127
|
-
} else {
|
|
128
|
+
} else if (!result) {
|
|
128
129
|
// sync
|
|
129
|
-
|
|
130
|
-
report.addError('INVALID_FORMAT', [schema.format!, JSON.stringify(json)], undefined, schema, 'format');
|
|
131
|
-
}
|
|
130
|
+
report.addError('INVALID_FORMAT', [schema.format!, JSON.stringify(json)], undefined, schema, 'format');
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
|
-
} else if (
|
|
133
|
+
} else if (ctx.options.ignoreUnknownFormats !== true && !isModernDraft) {
|
|
135
134
|
report.addError('UNKNOWN_FORMAT', [schema.format!], undefined, schema, 'format');
|
|
136
135
|
}
|
|
137
136
|
}
|
|
@@ -140,15 +139,15 @@ export function formatValidator(this: ZSchemaBase, report: Report, schema: JsonS
|
|
|
140
139
|
// contentEncoding
|
|
141
140
|
// ---------------------------------------------------------------------------
|
|
142
141
|
|
|
143
|
-
export function contentEncodingValidator(
|
|
144
|
-
if (
|
|
142
|
+
export function contentEncodingValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
143
|
+
if (ctx.options.version !== 'draft-07') {
|
|
145
144
|
return;
|
|
146
145
|
}
|
|
147
146
|
if (typeof json !== 'string') {
|
|
148
147
|
return;
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
const contentEncoding = schema
|
|
150
|
+
const { contentEncoding } = schema;
|
|
152
151
|
if (contentEncoding !== 'base64') {
|
|
153
152
|
return;
|
|
154
153
|
}
|
|
@@ -168,20 +167,15 @@ export function contentEncodingValidator(this: ZSchemaBase, report: Report, sche
|
|
|
168
167
|
// contentMediaType
|
|
169
168
|
// ---------------------------------------------------------------------------
|
|
170
169
|
|
|
171
|
-
export function contentMediaTypeValidator(
|
|
172
|
-
|
|
173
|
-
report: Report,
|
|
174
|
-
schema: JsonSchemaInternal,
|
|
175
|
-
json: unknown
|
|
176
|
-
) {
|
|
177
|
-
if (this.options.version !== 'draft-07') {
|
|
170
|
+
export function contentMediaTypeValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
171
|
+
if (ctx.options.version !== 'draft-07') {
|
|
178
172
|
return;
|
|
179
173
|
}
|
|
180
174
|
if (typeof json !== 'string') {
|
|
181
175
|
return;
|
|
182
176
|
}
|
|
183
177
|
|
|
184
|
-
const contentMediaType = schema
|
|
178
|
+
const { contentMediaType } = schema;
|
|
185
179
|
if (contentMediaType !== 'application/json') {
|
|
186
180
|
return;
|
|
187
181
|
}
|
package/src/validation/type.ts
CHANGED
|
@@ -10,9 +10,9 @@ import { shouldSkipValidate } from './shared.js';
|
|
|
10
10
|
// type
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
|
-
export function typeValidator(
|
|
13
|
+
export function typeValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
14
14
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2
|
|
15
|
-
if (shouldSkipValidate(
|
|
15
|
+
if (shouldSkipValidate(ctx.validateOptions, ['INVALID_TYPE'])) {
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
const jsonType = whatIs(json);
|
|
@@ -20,10 +20,8 @@ export function typeValidator(this: ZSchemaBase, report: Report, schema: JsonSch
|
|
|
20
20
|
if (jsonType !== schema.type && (jsonType !== 'integer' || schema.type !== 'number')) {
|
|
21
21
|
report.addError('INVALID_TYPE', [schema.type, jsonType], undefined, schema, 'type');
|
|
22
22
|
}
|
|
23
|
-
} else {
|
|
24
|
-
|
|
25
|
-
report.addError('INVALID_TYPE', [JSON.stringify(schema.type), jsonType], undefined, schema, 'type');
|
|
26
|
-
}
|
|
23
|
+
} else if (!schema.type!.includes(jsonType) && (jsonType !== 'integer' || !schema.type!.includes('number'))) {
|
|
24
|
+
report.addError('INVALID_TYPE', [JSON.stringify(schema.type), jsonType], undefined, schema, 'type');
|
|
27
25
|
}
|
|
28
26
|
}
|
|
29
27
|
|
|
@@ -31,25 +29,27 @@ export function typeValidator(this: ZSchemaBase, report: Report, schema: JsonSch
|
|
|
31
29
|
// enum
|
|
32
30
|
// ---------------------------------------------------------------------------
|
|
33
31
|
|
|
34
|
-
export function enumValidator(
|
|
32
|
+
export function enumValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
35
33
|
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.1.2
|
|
36
|
-
if (shouldSkipValidate(
|
|
34
|
+
if (shouldSkipValidate(ctx.validateOptions, ['ENUM_CASE_MISMATCH', 'ENUM_MISMATCH'])) {
|
|
37
35
|
return;
|
|
38
36
|
}
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
const eqOpts = { maxDepth: ctx.options.maxRecursionDepth };
|
|
38
|
+
const eqOptsCI = { caseInsensitiveComparison: true, maxDepth: ctx.options.maxRecursionDepth };
|
|
39
|
+
let caseInsensitiveMatch = false,
|
|
40
|
+
match = false;
|
|
41
41
|
for (const enumVal of schema.enum!) {
|
|
42
|
-
if (areEqual(json, enumVal,
|
|
42
|
+
if (areEqual(json, enumVal, eqOpts)) {
|
|
43
43
|
match = true;
|
|
44
44
|
break;
|
|
45
|
-
} else if (areEqual(json, enumVal,
|
|
45
|
+
} else if (areEqual(json, enumVal, eqOptsCI)) {
|
|
46
46
|
caseInsensitiveMatch = true;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (match
|
|
50
|
+
if (!match) {
|
|
51
51
|
const error =
|
|
52
|
-
caseInsensitiveMatch &&
|
|
52
|
+
caseInsensitiveMatch && ctx.options.enumCaseInsensitiveComparison ? 'ENUM_CASE_MISMATCH' : 'ENUM_MISMATCH';
|
|
53
53
|
report.addError(error, [JSON.stringify(json)], undefined, schema, 'enum');
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -58,9 +58,9 @@ export function enumValidator(this: ZSchemaBase, report: Report, schema: JsonSch
|
|
|
58
58
|
// const
|
|
59
59
|
// ---------------------------------------------------------------------------
|
|
60
60
|
|
|
61
|
-
export function constValidator(
|
|
61
|
+
export function constValidator(ctx: ZSchemaBase, report: Report, schema: JsonSchemaInternal, json: unknown) {
|
|
62
62
|
const constValue = schema.const;
|
|
63
|
-
if (areEqual(json, constValue, { maxDepth:
|
|
64
|
-
report.addError('CONST', [JSON.stringify(constValue)], undefined, schema
|
|
63
|
+
if (!areEqual(json, constValue, { maxDepth: ctx.options.maxRecursionDepth })) {
|
|
64
|
+
report.addError('CONST', [JSON.stringify(constValue)], undefined, schema);
|
|
65
65
|
}
|
|
66
66
|
}
|
package/src/z-schema-base.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ValidateError } from './errors.js';
|
|
1
|
+
import type { Errors, ValidateError } from './errors.js';
|
|
2
2
|
import type { FormatValidatorFn } from './format-validators.js';
|
|
3
3
|
import type { JsonSchema, JsonSchemaInternal, JsonSchemaVersion } from './json-schema-versions.js';
|
|
4
4
|
import type { SchemaErrorDetail } from './report.js';
|
|
5
5
|
import type { ZSchemaOptions } from './z-schema-options.js';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { getValidateError } from './errors.js';
|
|
8
8
|
import { getSupportedFormats } from './format-validators.js';
|
|
9
9
|
import { VERSION_SCHEMA_URL_MAPPING } from './json-schema-versions.js';
|
|
10
10
|
import { isInternalKey } from './json-schema.js';
|
|
@@ -26,7 +26,10 @@ export interface ValidateOptions {
|
|
|
26
26
|
excludeErrors?: Array<keyof typeof Errors>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export
|
|
29
|
+
export interface ValidateResponse {
|
|
30
|
+
valid: boolean;
|
|
31
|
+
err?: ValidateError;
|
|
32
|
+
}
|
|
30
33
|
|
|
31
34
|
export type ValidateCallback = (err: ValidateResponse['err'], valid: ValidateResponse['valid']) => void;
|
|
32
35
|
|
|
@@ -62,7 +65,7 @@ export class ZSchemaBase {
|
|
|
62
65
|
* create a circular dependency).
|
|
63
66
|
*/
|
|
64
67
|
_jsonValidate(report: Report, schema: boolean | JsonSchemaInternal, json: unknown): boolean {
|
|
65
|
-
return validateJson
|
|
68
|
+
return validateJson(this, report, schema, json);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
getDefaultSchemaId(): string {
|
|
@@ -73,8 +76,7 @@ export class ZSchemaBase {
|
|
|
73
76
|
|
|
74
77
|
_validate(json: unknown, schema: JsonSchema | string, options: ValidateOptions, callback: ValidateCallback): void;
|
|
75
78
|
_validate(json: unknown, schema: JsonSchema | string, callback: ValidateCallback): void;
|
|
76
|
-
_validate(json: unknown, schema: JsonSchema | string, options
|
|
77
|
-
_validate(json: unknown, schema: JsonSchema | string): true;
|
|
79
|
+
_validate(json: unknown, schema: JsonSchema | string, options?: ValidateOptions): true;
|
|
78
80
|
_validate(
|
|
79
81
|
json: unknown,
|
|
80
82
|
schema: JsonSchema | string,
|
|
@@ -93,10 +95,10 @@ export class ZSchemaBase {
|
|
|
93
95
|
|
|
94
96
|
if (typeof schema !== 'string' && typeof schema !== 'boolean' && !isObject(schema)) {
|
|
95
97
|
const e = new Error(
|
|
96
|
-
|
|
98
|
+
`Invalid .validate call - schema must be a string or object but ${whatIs(schema)} was passed!`
|
|
97
99
|
);
|
|
98
100
|
if (callback) {
|
|
99
|
-
setTimeout(
|
|
101
|
+
setTimeout(() => {
|
|
100
102
|
callback(e, false);
|
|
101
103
|
}, 0);
|
|
102
104
|
return;
|
|
@@ -113,9 +115,9 @@ export class ZSchemaBase {
|
|
|
113
115
|
const schemaName = schema;
|
|
114
116
|
_schema = this.scache.getSchema(report, schemaName)!;
|
|
115
117
|
if (!_schema) {
|
|
116
|
-
const e = new Error(
|
|
118
|
+
const e = new Error(`Schema with id '${schemaName}' wasn't found in the validator cache!`);
|
|
117
119
|
if (callback) {
|
|
118
|
-
setTimeout(
|
|
120
|
+
setTimeout(() => {
|
|
119
121
|
callback(e, false);
|
|
120
122
|
}, 0);
|
|
121
123
|
return;
|
|
@@ -144,11 +146,11 @@ export class ZSchemaBase {
|
|
|
144
146
|
|
|
145
147
|
if (options.schemaPath) {
|
|
146
148
|
report.rootSchema = _schema;
|
|
147
|
-
_schema = get(_schema, options.schemaPath);
|
|
149
|
+
_schema = get(_schema, options.schemaPath) as JsonSchemaInternal;
|
|
148
150
|
if (!_schema) {
|
|
149
|
-
const e = new Error(
|
|
151
|
+
const e = new Error(`Schema path '${options.schemaPath}' wasn't found in the schema!`);
|
|
150
152
|
if (callback) {
|
|
151
|
-
setTimeout(
|
|
153
|
+
setTimeout(() => {
|
|
152
154
|
callback(e, false);
|
|
153
155
|
}, 0);
|
|
154
156
|
return;
|
|
@@ -158,7 +160,7 @@ export class ZSchemaBase {
|
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
if (!foundError) {
|
|
161
|
-
validateJson
|
|
163
|
+
validateJson(this, report, _schema, json);
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
if (callback) {
|
|
@@ -187,13 +189,13 @@ export class ZSchemaBase {
|
|
|
187
189
|
const report = new Report(this.options);
|
|
188
190
|
|
|
189
191
|
if (Array.isArray(schemaOrArr)) {
|
|
190
|
-
const arr = this.scache.getSchema(report, schemaOrArr)
|
|
192
|
+
const arr = this.scache.getSchema(report, schemaOrArr);
|
|
191
193
|
const compiled = this.sc.compileSchema(report, arr);
|
|
192
194
|
if (compiled) {
|
|
193
195
|
this.sv.validateSchema(report, arr);
|
|
194
196
|
}
|
|
195
197
|
} else {
|
|
196
|
-
const schema = this.scache.getSchema(report, schemaOrArr)
|
|
198
|
+
const schema = this.scache.getSchema(report, schemaOrArr);
|
|
197
199
|
const compiled = this.sc.compileSchema(report, schema);
|
|
198
200
|
if (compiled) {
|
|
199
201
|
this.sv.validateSchema(report, schema);
|
|
@@ -256,11 +258,13 @@ export class ZSchemaBase {
|
|
|
256
258
|
* @returns An array of unresolvable reference URIs.
|
|
257
259
|
*/
|
|
258
260
|
getMissingReferences(err: ValidateError): string[] {
|
|
259
|
-
if (!err)
|
|
261
|
+
if (!err) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
260
264
|
const details = err.details || [];
|
|
261
265
|
const missingRefs: string[] = [];
|
|
262
|
-
function collect(
|
|
263
|
-
for (const detail of
|
|
266
|
+
function collect(items: SchemaErrorDetail[]) {
|
|
267
|
+
for (const detail of items) {
|
|
264
268
|
if (detail.code === 'UNRESOLVABLE_REFERENCE' || detail.code === 'SCHEMA_NOT_REACHABLE') {
|
|
265
269
|
missingRefs.push(detail.params[0] as string);
|
|
266
270
|
}
|
|
@@ -281,9 +285,11 @@ export class ZSchemaBase {
|
|
|
281
285
|
getMissingRemoteReferences(err: ValidateError) {
|
|
282
286
|
const missingReferences = this.getMissingReferences(err);
|
|
283
287
|
const missingRemoteReferences: string[] = [];
|
|
288
|
+
const seen = new Set<string>();
|
|
284
289
|
for (const ref of missingReferences) {
|
|
285
290
|
const remoteReference = getRemotePath(ref);
|
|
286
|
-
if (remoteReference && !
|
|
291
|
+
if (remoteReference && !seen.has(remoteReference)) {
|
|
292
|
+
seen.add(remoteReference);
|
|
287
293
|
missingRemoteReferences.push(remoteReference);
|
|
288
294
|
}
|
|
289
295
|
}
|
|
@@ -299,41 +305,46 @@ export class ZSchemaBase {
|
|
|
299
305
|
getResolvedSchema(schemaId: string): JsonSchema | undefined {
|
|
300
306
|
const report = new Report(this.options);
|
|
301
307
|
const schema = this.scache.getSchemaByUri(report, schemaId);
|
|
302
|
-
if (!schema)
|
|
308
|
+
if (!schema) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
303
311
|
|
|
304
312
|
const clonedSchema = deepClone(schema, this.options.maxRecursionDepth);
|
|
305
313
|
|
|
306
314
|
const visited = new WeakSet<object>();
|
|
307
315
|
|
|
308
316
|
// clean-up the schema and resolve references
|
|
309
|
-
const cleanup =
|
|
310
|
-
|
|
311
|
-
const typeOf = whatIs(schema);
|
|
317
|
+
const cleanup = (node: unknown) => {
|
|
318
|
+
const typeOf = whatIs(node);
|
|
312
319
|
if (typeOf !== 'object' && typeOf !== 'array') {
|
|
313
320
|
return;
|
|
314
321
|
}
|
|
315
322
|
|
|
316
|
-
if (visited.has(
|
|
323
|
+
if (visited.has(node as object)) {
|
|
317
324
|
return;
|
|
318
325
|
}
|
|
319
326
|
|
|
320
|
-
visited.add(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
delete
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
visited.add(node as object);
|
|
328
|
+
|
|
329
|
+
const schemaNode = node as JsonSchemaInternal;
|
|
330
|
+
if (schemaNode.$ref && schemaNode.__$refResolved) {
|
|
331
|
+
const from = schemaNode.__$refResolved;
|
|
332
|
+
const to = schemaNode;
|
|
333
|
+
delete schemaNode.$ref;
|
|
334
|
+
delete schemaNode.__$refResolved;
|
|
335
|
+
for (const key in from) {
|
|
336
|
+
if (Object.hasOwn(from, key)) {
|
|
337
|
+
copyProp(from, to, key);
|
|
338
|
+
}
|
|
329
339
|
}
|
|
330
340
|
}
|
|
331
|
-
|
|
332
|
-
|
|
341
|
+
const record = node as Record<string, unknown>;
|
|
342
|
+
for (const key in record) {
|
|
343
|
+
if (Object.hasOwn(record, key)) {
|
|
333
344
|
if (isInternalKey(key)) {
|
|
334
|
-
delete
|
|
345
|
+
delete record[key];
|
|
335
346
|
} else {
|
|
336
|
-
cleanup(
|
|
347
|
+
cleanup(record[key]);
|
|
337
348
|
}
|
|
338
349
|
}
|
|
339
350
|
}
|
package/src/z-schema-options.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { FormatValidatorFn } from './format-validators.js';
|
|
2
|
+
import type { JsonSchemaVersion } from './json-schema-versions.js';
|
|
2
3
|
import type { Report } from './report.js';
|
|
3
4
|
|
|
4
|
-
import { CURRENT_DEFAULT_SCHEMA_VERSION
|
|
5
|
+
import { CURRENT_DEFAULT_SCHEMA_VERSION } from './json-schema-versions.js';
|
|
5
6
|
import { shallowClone } from './utils/clone.js';
|
|
6
7
|
import { DEFAULT_MAX_RECURSION_DEPTH, MAX_ASYNC_TIMEOUT } from './utils/constants.js';
|
|
7
8
|
|
|
@@ -103,7 +104,7 @@ export const normalizeOptions = (options?: ZSchemaOptions) => {
|
|
|
103
104
|
// check that the options are correctly configured
|
|
104
105
|
for (const key of keys) {
|
|
105
106
|
if (defaultOptions[key] === undefined) {
|
|
106
|
-
throw new Error(
|
|
107
|
+
throw new Error(`Unexpected option passed to constructor: ${key}`);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -111,7 +112,7 @@ export const normalizeOptions = (options?: ZSchemaOptions) => {
|
|
|
111
112
|
keys = Object.keys(defaultOptions) as Array<keyof ZSchemaOptions>;
|
|
112
113
|
for (const key of keys) {
|
|
113
114
|
if (options[key] === undefined) {
|
|
114
|
-
(options as
|
|
115
|
+
(options as Record<string, unknown>)[key] = shallowClone(defaultOptions[key]);
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|