z-schema 12.1.1 → 12.3.1
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 +4 -5
- package/cjs/{index.js → index.cjs} +696 -687
- 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} +43 -36
- 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} +7 -12
- package/dist/{json-validation.mjs → json-validation.js} +143 -127
- package/dist/{report.d.mts → report.d.ts} +7 -8
- package/dist/{report.mjs → report.js} +28 -31
- package/dist/{schema-cache.d.mts → schema-cache.d.ts} +4 -4
- 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} +95 -77
- 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.mjs → hostname.js} +19 -22
- package/dist/utils/{json.mjs → json.js} +11 -7
- package/dist/utils/{schema-regex.mjs → schema-regex.js} +5 -5
- package/dist/utils/{time.mjs → time.js} +5 -5
- 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} +4 -4
- package/dist/validation/{shared.mjs → shared.js} +12 -11
- package/dist/validation/{string.mjs → string.js} +32 -32
- 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} +4 -4
- 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} +37 -47
- package/package.json +25 -23
- package/src/errors.ts +1 -2
- package/src/format-validators.ts +139 -59
- 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 +27 -27
- 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 +5 -5
- 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 +29 -39
- 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 +723 -697
- package/umd/ZSchema.min.js +2 -2
- package/umd/package.json +3 -0
- 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
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
import { deepClone } from "./utils/clone.
|
|
2
|
-
import { jsonSymbol, schemaSymbol } from "./utils/symbols.
|
|
3
|
-
import { defaultOptions } from "./z-schema-options.
|
|
4
|
-
import { SchemaCache, prepareRemoteSchema } from "./schema-cache.
|
|
5
|
-
import "./z-schema-versions.
|
|
6
|
-
import { getRegisteredFormats, registerFormat, unregisterFormat } from "./format-validators.
|
|
7
|
-
import { getSchemaReader, setSchemaReader } from "./z-schema-reader.
|
|
8
|
-
import { FACTORY_TOKEN, ZSchemaBase } from "./z-schema-base.
|
|
1
|
+
import { deepClone } from "./utils/clone.js";
|
|
2
|
+
import { jsonSymbol, schemaSymbol } from "./utils/symbols.js";
|
|
3
|
+
import { defaultOptions } from "./z-schema-options.js";
|
|
4
|
+
import { SchemaCache, prepareRemoteSchema } from "./schema-cache.js";
|
|
5
|
+
import "./z-schema-versions.js";
|
|
6
|
+
import { getRegisteredFormats, registerFormat, unregisterFormat } from "./format-validators.js";
|
|
7
|
+
import { getSchemaReader, setSchemaReader } from "./z-schema-reader.js";
|
|
8
|
+
import { FACTORY_TOKEN, ZSchemaBase } from "./z-schema-base.js";
|
|
9
9
|
//#region src/z-schema.ts
|
|
10
10
|
var ZSchema = class ZSchema extends ZSchemaBase {
|
|
11
|
-
/** @internal Use ZSchema.create() instead. */
|
|
12
|
-
constructor(options, token) {
|
|
13
|
-
super(options, token);
|
|
14
|
-
}
|
|
15
11
|
/**
|
|
16
12
|
* Register a global format validator available to all instances.
|
|
17
13
|
* @param name - The format name (e.g. `'email'`, `'date'`).
|
|
18
14
|
* @param validatorFunction - A sync or async function `(value: unknown) => boolean | Promise<boolean>`.
|
|
19
15
|
*/
|
|
20
16
|
static registerFormat(name, validatorFunction) {
|
|
21
|
-
|
|
17
|
+
registerFormat(name, validatorFunction);
|
|
22
18
|
}
|
|
23
19
|
/**
|
|
24
20
|
* Remove a globally registered format validator.
|
|
25
21
|
* @param name - The format name to unregister.
|
|
26
22
|
*/
|
|
27
23
|
static unregisterFormat(name) {
|
|
28
|
-
|
|
24
|
+
unregisterFormat(name);
|
|
29
25
|
}
|
|
30
26
|
/** Returns the names of all globally registered format validators. */
|
|
31
27
|
static getRegisteredFormats() {
|
|
@@ -54,7 +50,7 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
54
50
|
* @param schemaReader - A function `(uri: string) => JsonSchema | undefined`, or `undefined` to clear.
|
|
55
51
|
*/
|
|
56
52
|
static setSchemaReader(schemaReader) {
|
|
57
|
-
|
|
53
|
+
setSchemaReader(schemaReader);
|
|
58
54
|
}
|
|
59
55
|
static schemaSymbol = schemaSymbol;
|
|
60
56
|
static jsonSymbol = jsonSymbol;
|
|
@@ -90,10 +86,10 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
90
86
|
try {
|
|
91
87
|
this._validate(json, schema, options ?? {});
|
|
92
88
|
return { valid: true };
|
|
93
|
-
} catch (
|
|
89
|
+
} catch (error) {
|
|
94
90
|
return {
|
|
95
91
|
valid: false,
|
|
96
|
-
err
|
|
92
|
+
err: error
|
|
97
93
|
};
|
|
98
94
|
}
|
|
99
95
|
}
|
|
@@ -108,9 +104,12 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
108
104
|
validateAsync(json, schema, options) {
|
|
109
105
|
return new Promise((resolve, reject) => {
|
|
110
106
|
try {
|
|
111
|
-
this._validate(json, schema, options || {}, (err, valid) =>
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
this._validate(json, schema, options || {}, (err, valid) => {
|
|
108
|
+
if (err || !valid) reject(err ?? /* @__PURE__ */ new Error("Validation failed"));
|
|
109
|
+
else resolve(valid);
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
reject(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
114
113
|
}
|
|
115
114
|
});
|
|
116
115
|
}
|
|
@@ -131,10 +130,10 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
131
130
|
err
|
|
132
131
|
});
|
|
133
132
|
});
|
|
134
|
-
} catch (
|
|
133
|
+
} catch (error) {
|
|
135
134
|
resolve({
|
|
136
135
|
valid: false,
|
|
137
|
-
err
|
|
136
|
+
err: error
|
|
138
137
|
});
|
|
139
138
|
}
|
|
140
139
|
});
|
|
@@ -157,10 +156,10 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
157
156
|
try {
|
|
158
157
|
this._validateSchema(schemaOrArr);
|
|
159
158
|
return { valid: true };
|
|
160
|
-
} catch (
|
|
159
|
+
} catch (error) {
|
|
161
160
|
return {
|
|
162
161
|
valid: false,
|
|
163
|
-
err
|
|
162
|
+
err: error
|
|
164
163
|
};
|
|
165
164
|
}
|
|
166
165
|
}
|
|
@@ -170,10 +169,6 @@ var ZSchema = class ZSchema extends ZSchemaBase {
|
|
|
170
169
|
* Created via `ZSchema.create({ safe: true })`.
|
|
171
170
|
*/
|
|
172
171
|
var ZSchemaSafe = class extends ZSchemaBase {
|
|
173
|
-
/** @internal Use ZSchema.create() instead. */
|
|
174
|
-
constructor(options, token) {
|
|
175
|
-
super(options, token);
|
|
176
|
-
}
|
|
177
172
|
/**
|
|
178
173
|
* Validate JSON data against a schema.
|
|
179
174
|
* @param json - The data to validate.
|
|
@@ -185,10 +180,10 @@ var ZSchemaSafe = class extends ZSchemaBase {
|
|
|
185
180
|
try {
|
|
186
181
|
this._validate(json, schema, options);
|
|
187
182
|
return { valid: true };
|
|
188
|
-
} catch (
|
|
183
|
+
} catch (error) {
|
|
189
184
|
return {
|
|
190
185
|
valid: false,
|
|
191
|
-
err
|
|
186
|
+
err: error
|
|
192
187
|
};
|
|
193
188
|
}
|
|
194
189
|
}
|
|
@@ -201,10 +196,10 @@ var ZSchemaSafe = class extends ZSchemaBase {
|
|
|
201
196
|
try {
|
|
202
197
|
this._validateSchema(schemaOrArr);
|
|
203
198
|
return { valid: true };
|
|
204
|
-
} catch (
|
|
199
|
+
} catch (error) {
|
|
205
200
|
return {
|
|
206
201
|
valid: false,
|
|
207
|
-
err
|
|
202
|
+
err: error
|
|
208
203
|
};
|
|
209
204
|
}
|
|
210
205
|
}
|
|
@@ -214,10 +209,6 @@ var ZSchemaSafe = class extends ZSchemaBase {
|
|
|
214
209
|
* Created via `ZSchema.create({ async: true })`.
|
|
215
210
|
*/
|
|
216
211
|
var ZSchemaAsync = class extends ZSchemaBase {
|
|
217
|
-
/** @internal Use ZSchema.create() instead. */
|
|
218
|
-
constructor(options, token) {
|
|
219
|
-
super(options, token);
|
|
220
|
-
}
|
|
221
212
|
/**
|
|
222
213
|
* Validate JSON data against a schema asynchronously.
|
|
223
214
|
* @param json - The data to validate.
|
|
@@ -229,9 +220,12 @@ var ZSchemaAsync = class extends ZSchemaBase {
|
|
|
229
220
|
validate(json, schema, options = {}) {
|
|
230
221
|
return new Promise((resolve, reject) => {
|
|
231
222
|
try {
|
|
232
|
-
this._validate(json, schema, options, (err, valid) =>
|
|
233
|
-
|
|
234
|
-
|
|
223
|
+
this._validate(json, schema, options, (err, valid) => {
|
|
224
|
+
if (err || !valid) reject(err ?? /* @__PURE__ */ new Error("Validation failed"));
|
|
225
|
+
else resolve(valid);
|
|
226
|
+
});
|
|
227
|
+
} catch (error) {
|
|
228
|
+
reject(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
235
229
|
}
|
|
236
230
|
});
|
|
237
231
|
}
|
|
@@ -250,10 +244,6 @@ var ZSchemaAsync = class extends ZSchemaBase {
|
|
|
250
244
|
* Created via `ZSchema.create({ async: true, safe: true })`.
|
|
251
245
|
*/
|
|
252
246
|
var ZSchemaAsyncSafe = class extends ZSchemaBase {
|
|
253
|
-
/** @internal Use ZSchema.create() instead. */
|
|
254
|
-
constructor(options, token) {
|
|
255
|
-
super(options, token);
|
|
256
|
-
}
|
|
257
247
|
/**
|
|
258
248
|
* Validate JSON data against a schema asynchronously.
|
|
259
249
|
* The promise always resolves (never rejects).
|
|
@@ -271,10 +261,10 @@ var ZSchemaAsyncSafe = class extends ZSchemaBase {
|
|
|
271
261
|
err
|
|
272
262
|
});
|
|
273
263
|
});
|
|
274
|
-
} catch (
|
|
264
|
+
} catch (error) {
|
|
275
265
|
resolve({
|
|
276
266
|
valid: false,
|
|
277
|
-
err
|
|
267
|
+
err: error
|
|
278
268
|
});
|
|
279
269
|
}
|
|
280
270
|
});
|
|
@@ -288,10 +278,10 @@ var ZSchemaAsyncSafe = class extends ZSchemaBase {
|
|
|
288
278
|
try {
|
|
289
279
|
this._validateSchema(schemaOrArr);
|
|
290
280
|
return { valid: true };
|
|
291
|
-
} catch (
|
|
281
|
+
} catch (error) {
|
|
292
282
|
return {
|
|
293
283
|
valid: false,
|
|
294
|
-
err
|
|
284
|
+
err: error
|
|
295
285
|
};
|
|
296
286
|
}
|
|
297
287
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "z-schema",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.1",
|
|
4
4
|
"description": "Fast, lightweight JSON Schema validator for Node.js and browsers — full support for draft-04, draft-06, draft-07, draft-2019-09, and draft-2020-12 (latest)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"draft-04",
|
|
@@ -39,32 +39,30 @@
|
|
|
39
39
|
"LICENSE",
|
|
40
40
|
"README.md"
|
|
41
41
|
],
|
|
42
|
+
"type": "module",
|
|
42
43
|
"sideEffects": [
|
|
43
|
-
"./dist/z-schema-versions.
|
|
44
|
+
"./dist/z-schema-versions.js"
|
|
44
45
|
],
|
|
45
46
|
"exports": {
|
|
46
47
|
".": {
|
|
47
48
|
"import": {
|
|
48
|
-
"types": "./dist/index.d.
|
|
49
|
-
"default": "./dist/index.
|
|
49
|
+
"types": "./dist/index.d.ts",
|
|
50
|
+
"default": "./dist/index.js"
|
|
50
51
|
},
|
|
51
52
|
"require": {
|
|
52
|
-
"types": "./cjs/index.d.
|
|
53
|
-
"default": "./cjs/index.
|
|
53
|
+
"types": "./cjs/index.d.cts",
|
|
54
|
+
"default": "./cjs/index.cjs"
|
|
54
55
|
},
|
|
55
|
-
"default": "./dist/index.
|
|
56
|
+
"default": "./dist/index.js"
|
|
56
57
|
}
|
|
57
58
|
},
|
|
58
59
|
"scripts": {
|
|
59
|
-
"prepare": "
|
|
60
|
-
"pre-commit": "npx lint-staged",
|
|
60
|
+
"prepare": "lefthook install",
|
|
61
61
|
"pre-push": "npm run build && npm run build:tests",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"lint": "oxlint --fix",
|
|
65
|
-
"lint:check": "oxlint",
|
|
62
|
+
"check": "ultracite check",
|
|
63
|
+
"fix": "ultracite fix",
|
|
66
64
|
"clean": "rimraf ./cjs && rimraf ./dist && rimraf ./umd && rimraf --glob \"./src/schemas/*.json\"",
|
|
67
|
-
"build": "npm run copy:schemas && tsdown",
|
|
65
|
+
"build": "npm run copy:schemas && tsdown && node ./scripts/write-build-markers.mjs",
|
|
68
66
|
"build:watch": "npm run copy:schemas && tsdown --watch",
|
|
69
67
|
"build:tests": "tsc --noEmit --project test/tsconfig.json",
|
|
70
68
|
"copy:schemas": "node ./scripts/copy-schemas.mts",
|
|
@@ -87,22 +85,26 @@
|
|
|
87
85
|
"@types/node": "^25.2.0",
|
|
88
86
|
"@types/punycode": "^2.1.4",
|
|
89
87
|
"@types/validator": "^13.15.10",
|
|
90
|
-
"@vitest/browser-playwright": "^4.0
|
|
91
|
-
"@vitest/coverage-istanbul": "^4.0
|
|
92
|
-
"@vitest/coverage-v8": "^4.0
|
|
93
|
-
"
|
|
94
|
-
"lint-staged": "^16.2.7",
|
|
88
|
+
"@vitest/browser-playwright": "^4.1.0",
|
|
89
|
+
"@vitest/coverage-istanbul": "^4.1.0",
|
|
90
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
91
|
+
"lefthook": "^2.1.9",
|
|
95
92
|
"lodash.isequal": "^4.5.0",
|
|
96
|
-
"oxfmt": "^0.
|
|
93
|
+
"oxfmt": "^0.51.0",
|
|
97
94
|
"oxlint": "^1.56.0",
|
|
95
|
+
"oxlint-tsgolint": "^0.23.0",
|
|
98
96
|
"rimraf": "^6.1.2",
|
|
99
|
-
"tsdown": "^0.
|
|
100
|
-
"typescript": "^
|
|
101
|
-
"
|
|
97
|
+
"tsdown": "^0.22.0",
|
|
98
|
+
"typescript": "^6.0.3",
|
|
99
|
+
"ultracite": "^7.8.1",
|
|
100
|
+
"vitest": "^4.1.0"
|
|
102
101
|
},
|
|
103
102
|
"optionalDependencies": {
|
|
104
103
|
"commander": "^14.0.3"
|
|
105
104
|
},
|
|
105
|
+
"overrides": {
|
|
106
|
+
"vite": ">=7.3.2"
|
|
107
|
+
},
|
|
106
108
|
"engines": {
|
|
107
109
|
"node": ">=22.0.0"
|
|
108
110
|
},
|
package/src/errors.ts
CHANGED
|
@@ -76,12 +76,11 @@ export const Errors = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export class ValidateError extends Error {
|
|
79
|
-
name: string;
|
|
79
|
+
override readonly name: string = 'ValidateError';
|
|
80
80
|
details?: SchemaErrorDetail[];
|
|
81
81
|
|
|
82
82
|
constructor(message: string, details?: SchemaErrorDetail[]) {
|
|
83
83
|
super(message);
|
|
84
|
-
this.name = 'z-schema validation error';
|
|
85
84
|
this.details = details;
|
|
86
85
|
}
|
|
87
86
|
}
|
package/src/format-validators.ts
CHANGED
|
@@ -18,9 +18,9 @@ const dateValidator: FormatValidatorFn = (date: unknown) => {
|
|
|
18
18
|
if (matches === null) {
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
21
|
-
const year = parseInt(matches[1], 10);
|
|
22
|
-
const month = parseInt(matches[2], 10);
|
|
23
|
-
const day = parseInt(matches[3], 10);
|
|
21
|
+
const year = Number.parseInt(matches[1], 10);
|
|
22
|
+
const month = Number.parseInt(matches[2], 10);
|
|
23
|
+
const day = Number.parseInt(matches[3], 10);
|
|
24
24
|
return isValidRfc3339Date(year, month, day);
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -29,20 +29,23 @@ const dateTimeValidator: FormatValidatorFn = (dateTime: unknown) => {
|
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
31
31
|
// date-time from http://tools.ietf.org/html/rfc3339#section-5.6
|
|
32
|
-
|
|
33
|
-
if (
|
|
32
|
+
let tIdx = dateTime.indexOf('T');
|
|
33
|
+
if (tIdx === -1) {
|
|
34
|
+
tIdx = dateTime.indexOf('t');
|
|
35
|
+
}
|
|
36
|
+
if (tIdx === -1) {
|
|
34
37
|
return false;
|
|
35
38
|
}
|
|
36
|
-
const datePart =
|
|
37
|
-
const timePart =
|
|
39
|
+
const datePart = dateTime.slice(0, tIdx);
|
|
40
|
+
const timePart = dateTime.slice(tIdx + 1);
|
|
38
41
|
// Check date
|
|
39
42
|
const dateMatches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(datePart);
|
|
40
43
|
if (dateMatches === null) {
|
|
41
44
|
return false;
|
|
42
45
|
}
|
|
43
|
-
const year = parseInt(dateMatches[1], 10);
|
|
44
|
-
const month = parseInt(dateMatches[2], 10);
|
|
45
|
-
const day = parseInt(dateMatches[3], 10);
|
|
46
|
+
const year = Number.parseInt(dateMatches[1], 10);
|
|
47
|
+
const month = Number.parseInt(dateMatches[2], 10);
|
|
48
|
+
const day = Number.parseInt(dateMatches[3], 10);
|
|
46
49
|
if (!isValidRfc3339Date(year, month, day)) {
|
|
47
50
|
return false;
|
|
48
51
|
}
|
|
@@ -96,12 +99,13 @@ const ipv6Validator: FormatValidatorFn = (ipv6: unknown) => {
|
|
|
96
99
|
return isIPModule.default(ipv6, 6);
|
|
97
100
|
};
|
|
98
101
|
|
|
102
|
+
const INVALID_REGEX_ESCAPES = new Set(['a']);
|
|
103
|
+
|
|
99
104
|
const regexValidator: FormatValidatorFn = (input: unknown) => {
|
|
100
105
|
if (typeof input !== 'string') {
|
|
101
106
|
return true;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
const invalidEscapes = new Set(['a']);
|
|
105
109
|
for (let idx = 0; idx < input.length; idx++) {
|
|
106
110
|
if (input[idx] !== '\\') {
|
|
107
111
|
continue;
|
|
@@ -113,15 +117,19 @@ const regexValidator: FormatValidatorFn = (input: unknown) => {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
const escaped = input[idx];
|
|
116
|
-
if (
|
|
120
|
+
if (INVALID_REGEX_ESCAPES.has(escaped)) {
|
|
117
121
|
return false;
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
try {
|
|
122
|
-
|
|
126
|
+
// Constructed purely to detect an invalid pattern (throws SyntaxError). The
|
|
127
|
+
// result is intentionally unused; `new` is required by new-for-builtins and
|
|
128
|
+
// `void`/call-form trip no-void/new-for-builtins, so no-new is disabled here.
|
|
129
|
+
// oxlint-disable-next-line no-new
|
|
130
|
+
new RegExp(input);
|
|
123
131
|
return true;
|
|
124
|
-
} catch
|
|
132
|
+
} catch {
|
|
125
133
|
return false;
|
|
126
134
|
}
|
|
127
135
|
};
|
|
@@ -132,7 +140,7 @@ const durationValidator: FormatValidatorFn = (input: unknown) => {
|
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
// eslint-disable-next-line no-control-regex
|
|
135
|
-
if (!/^P[\
|
|
143
|
+
if (!/^P[\u0000-\u007F]*$/.test(input)) {
|
|
136
144
|
return false;
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -163,7 +171,7 @@ const durationValidator: FormatValidatorFn = (input: unknown) => {
|
|
|
163
171
|
return false;
|
|
164
172
|
}
|
|
165
173
|
|
|
166
|
-
const hasDateComponent =
|
|
174
|
+
const hasDateComponent = datePart.length > 0;
|
|
167
175
|
let hasTimeComponent = false;
|
|
168
176
|
|
|
169
177
|
if (timePart !== undefined) {
|
|
@@ -193,43 +201,55 @@ const uuidValidator: FormatValidatorFn = (input: unknown) => {
|
|
|
193
201
|
|
|
194
202
|
const strictUriValidator: FormatValidatorFn = (uri: unknown) => typeof uri !== 'string' || isURLModule.default(uri);
|
|
195
203
|
|
|
204
|
+
const isHexChar = (c: number) => (c >= 48 && c <= 57) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102);
|
|
205
|
+
|
|
196
206
|
const hasValidPercentEncoding = (str: string): boolean => {
|
|
197
207
|
for (let i = 0; i < str.length; i++) {
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
if (
|
|
209
|
+
str[i] === '%' &&
|
|
210
|
+
(i + 2 >= str.length || !isHexChar(str.charCodeAt(i + 1)) || !isHexChar(str.charCodeAt(i + 2)))
|
|
211
|
+
) {
|
|
212
|
+
return false;
|
|
202
213
|
}
|
|
203
214
|
}
|
|
204
215
|
return true;
|
|
205
216
|
};
|
|
206
217
|
|
|
207
|
-
const uriValidator: FormatValidatorFn =
|
|
208
|
-
if (typeof uri !== 'string')
|
|
218
|
+
const uriValidator: FormatValidatorFn = (uri: unknown) => {
|
|
219
|
+
if (typeof uri !== 'string') {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
209
222
|
// eslint-disable-next-line no-control-regex
|
|
210
|
-
if (/[^\
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
if (/[^\u0000-\u007F]/.test(uri)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (!hasValidPercentEncoding(uri)) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/([^/?#]*)/.exec(uri);
|
|
213
230
|
if (match) {
|
|
214
231
|
const authority = match[2];
|
|
215
232
|
const atIndex = authority.indexOf('@');
|
|
216
233
|
if (atIndex > 0) {
|
|
217
|
-
|
|
234
|
+
// userinfo is a string; prefer-set-has misfires here — two String#includes
|
|
235
|
+
// calls are faster than building a Set or a regex for this membership check.
|
|
236
|
+
// oxlint-disable-next-line unicorn/prefer-set-has
|
|
237
|
+
const userinfo = authority.slice(0, atIndex);
|
|
218
238
|
if (userinfo.includes('[') || userinfo.includes(']')) {
|
|
219
239
|
return false;
|
|
220
240
|
}
|
|
221
241
|
}
|
|
222
242
|
// Validate port: must be numeric
|
|
223
|
-
let hostPort = atIndex
|
|
243
|
+
let hostPort = atIndex === -1 ? authority : authority.slice(atIndex + 1);
|
|
224
244
|
if (hostPort.startsWith('[')) {
|
|
225
245
|
const bracketEnd = hostPort.indexOf(']');
|
|
226
|
-
if (bracketEnd
|
|
227
|
-
hostPort = hostPort.
|
|
246
|
+
if (bracketEnd !== -1) {
|
|
247
|
+
hostPort = hostPort.slice(bracketEnd + 1);
|
|
228
248
|
}
|
|
229
249
|
}
|
|
230
250
|
const colonIndex = hostPort.lastIndexOf(':');
|
|
231
|
-
if (colonIndex
|
|
232
|
-
const port = hostPort.
|
|
251
|
+
if (colonIndex !== -1) {
|
|
252
|
+
const port = hostPort.slice(colonIndex + 1);
|
|
233
253
|
if (port.length > 0 && !/^\d+$/.test(port)) {
|
|
234
254
|
return false;
|
|
235
255
|
}
|
|
@@ -239,15 +259,21 @@ const uriValidator: FormatValidatorFn = function (uri: unknown) {
|
|
|
239
259
|
};
|
|
240
260
|
|
|
241
261
|
const uriReferenceValidator: FormatValidatorFn = (uri: unknown) => {
|
|
242
|
-
if (typeof uri !== 'string')
|
|
262
|
+
if (typeof uri !== 'string') {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
243
265
|
// eslint-disable-next-line no-control-regex
|
|
244
|
-
if (/[^\
|
|
266
|
+
if (/[^\u0000-\u007F]/.test(uri)) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
245
269
|
// URI-reference allows relative URIs
|
|
246
270
|
return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/.test(uri);
|
|
247
271
|
};
|
|
248
272
|
|
|
249
273
|
const uriTemplateValidator: FormatValidatorFn = (uri: unknown) => {
|
|
250
|
-
if (typeof uri !== 'string')
|
|
274
|
+
if (typeof uri !== 'string') {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
251
277
|
// URI template allows braces for expressions.
|
|
252
278
|
if (!/^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^`| ]*$/.test(uri)) {
|
|
253
279
|
return false;
|
|
@@ -286,66 +312,100 @@ const hasValidTildeEscapes = (segment: string): boolean => {
|
|
|
286
312
|
};
|
|
287
313
|
|
|
288
314
|
const jsonPointerValidator: FormatValidatorFn = (pointer: unknown) => {
|
|
289
|
-
if (typeof pointer !== 'string')
|
|
315
|
+
if (typeof pointer !== 'string') {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
290
318
|
// JSON Pointer: empty, or a sequence of '/'-prefixed reference tokens.
|
|
291
319
|
// In each token, '~' must be escaped as '~0' or '~1'.
|
|
292
|
-
if (pointer === '')
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
320
|
+
if (pointer === '') {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
if (!/^(?:\/[^/]*)+$/.test(pointer)) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
const tokens = pointer.split('/');
|
|
327
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
328
|
+
if (!hasValidTildeEscapes(tokens[i])) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
297
331
|
}
|
|
298
332
|
return true;
|
|
299
333
|
};
|
|
300
334
|
|
|
301
335
|
const relativeJsonPointerValidator: FormatValidatorFn = (pointer: unknown) => {
|
|
302
|
-
if (typeof pointer !== 'string')
|
|
336
|
+
if (typeof pointer !== 'string') {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
303
339
|
// Relative JSON Pointer: non-negative integer prefix (no leading zeros unless zero),
|
|
304
340
|
// followed by either '#', a JSON Pointer, or nothing.
|
|
305
|
-
const match =
|
|
306
|
-
if (!match)
|
|
341
|
+
const match = /^(0|[1-9]\d*)(.*)$/.exec(pointer);
|
|
342
|
+
if (!match) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
307
345
|
const suffix = match[2];
|
|
308
|
-
if (suffix === '' || suffix === '#')
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
346
|
+
if (suffix === '' || suffix === '#') {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
if (!suffix.startsWith('/')) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
if (!/^(?:\/[^/]*)+$/.test(suffix)) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const tokens = suffix.split('/');
|
|
356
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
357
|
+
if (!hasValidTildeEscapes(tokens[i])) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
314
360
|
}
|
|
315
361
|
return true;
|
|
316
362
|
};
|
|
317
363
|
|
|
318
364
|
const timeValidator: FormatValidatorFn = (time: unknown) => {
|
|
319
|
-
if (typeof time !== 'string')
|
|
365
|
+
if (typeof time !== 'string') {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
320
368
|
return parseRfc3339Time(time) !== null;
|
|
321
369
|
};
|
|
322
370
|
|
|
323
371
|
const idnEmailValidator: FormatValidatorFn = (email: unknown) => {
|
|
324
|
-
if (typeof email !== 'string')
|
|
372
|
+
if (typeof email !== 'string') {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
325
375
|
// Simple email check, allowing international chars
|
|
326
376
|
return /^[^\s@]+@[^\s@]+$/.test(email);
|
|
327
377
|
};
|
|
328
378
|
|
|
329
379
|
const idnHostnameValidator: FormatValidatorFn = (hostname: unknown) => {
|
|
330
|
-
if (typeof hostname !== 'string')
|
|
380
|
+
if (typeof hostname !== 'string') {
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
331
383
|
return isValidIdnHostname(hostname);
|
|
332
384
|
};
|
|
333
385
|
|
|
334
386
|
const iriValidator: FormatValidatorFn = (iri: unknown) => {
|
|
335
|
-
if (typeof iri !== 'string')
|
|
387
|
+
if (typeof iri !== 'string') {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
336
390
|
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:[^"\\<>^{}^`| ]*$/u.test(iri)) {
|
|
337
391
|
return false;
|
|
338
392
|
}
|
|
339
393
|
try {
|
|
394
|
+
// Constructed purely to detect an unparseable IRI (throws). Avoids URL.canParse,
|
|
395
|
+
// which is unavailable in older browsers the UMD build still targets; no-new is
|
|
396
|
+
// disabled here as the construction is intentional and the result is unused.
|
|
397
|
+
// oxlint-disable-next-line no-new
|
|
340
398
|
new URL(iri);
|
|
341
399
|
return true;
|
|
342
|
-
} catch
|
|
400
|
+
} catch {
|
|
343
401
|
return false;
|
|
344
402
|
}
|
|
345
403
|
};
|
|
346
404
|
|
|
347
405
|
const iriReferenceValidator: FormatValidatorFn = (iriReference: unknown) => {
|
|
348
|
-
if (typeof iriReference !== 'string')
|
|
406
|
+
if (typeof iriReference !== 'string') {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
349
409
|
return /^([a-zA-Z][a-zA-Z0-9+.-]*:)?[^"\\<>^{}^`| ]*$/u.test(iriReference);
|
|
350
410
|
};
|
|
351
411
|
|
|
@@ -385,10 +445,24 @@ export function getFormatValidators(options?: FormatValidatorsOptions): Record<s
|
|
|
385
445
|
...inbuiltValidators,
|
|
386
446
|
...(options?.strictUris ? { uri: strictUriValidator } : {}),
|
|
387
447
|
...customValidators,
|
|
388
|
-
...
|
|
448
|
+
...options?.customFormats,
|
|
389
449
|
};
|
|
390
450
|
}
|
|
391
451
|
|
|
452
|
+
export function resolveFormatValidator(name: string, options?: FormatValidatorsOptions): FormatValidatorFn | undefined {
|
|
453
|
+
const custom = options?.customFormats;
|
|
454
|
+
if (custom && Object.hasOwn(custom, name)) {
|
|
455
|
+
return custom[name] as FormatValidatorFn | undefined;
|
|
456
|
+
}
|
|
457
|
+
if (Object.hasOwn(customValidators, name)) {
|
|
458
|
+
return customValidators[name];
|
|
459
|
+
}
|
|
460
|
+
if (options?.strictUris && name === 'uri') {
|
|
461
|
+
return strictUriValidator;
|
|
462
|
+
}
|
|
463
|
+
return inbuiltValidators[name];
|
|
464
|
+
}
|
|
465
|
+
|
|
392
466
|
export function registerFormat(name: string, validatorFunction: FormatValidatorFn) {
|
|
393
467
|
customValidators[name] = validatorFunction;
|
|
394
468
|
}
|
|
@@ -411,10 +485,16 @@ export function isFormatSupported(name: string, customFormats?: Record<string, F
|
|
|
411
485
|
if (customFormats) {
|
|
412
486
|
const custom = customFormats[name];
|
|
413
487
|
// Explicitly null means unregistered at instance level
|
|
414
|
-
if (custom === null)
|
|
415
|
-
|
|
488
|
+
if (custom === null) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
if (custom != null) {
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (name in customValidators) {
|
|
496
|
+
return customValidators[name] != null;
|
|
416
497
|
}
|
|
417
|
-
if (name in customValidators) return customValidators[name] != null;
|
|
418
498
|
return name in inbuiltValidators;
|
|
419
499
|
}
|
|
420
500
|
|