@jetio/validator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1362 -0
- package/dist/cli.js +219 -0
- package/dist/compileSchema.d.ts +148 -0
- package/dist/compileSchema.js +2199 -0
- package/dist/compileSchema.js.map +1 -0
- package/dist/formats.d.ts +41 -0
- package/dist/formats.js +166 -0
- package/dist/formats.js.map +1 -0
- package/dist/index.cjs.js +6167 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +6148 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/jet-validator.d.ts +88 -0
- package/dist/jet-validator.js +983 -0
- package/dist/jet-validator.js.map +1 -0
- package/dist/resolver.d.ts +348 -0
- package/dist/resolver.js +2459 -0
- package/dist/resolver.js.map +1 -0
- package/dist/scripts/load-metaschemas.d.ts +1 -0
- package/dist/scripts/metaschema-loader.d.ts +2 -0
- package/dist/src/compileSchema.d.ts +148 -0
- package/dist/src/formats.d.ts +41 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/jet-validator.d.ts +88 -0
- package/dist/src/resolver.d.ts +348 -0
- package/dist/src/types/format.d.ts +7 -0
- package/dist/src/types/keywords.d.ts +78 -0
- package/dist/src/types/schema.d.ts +123 -0
- package/dist/src/types/standalone.d.ts +4 -0
- package/dist/src/types/validation.d.ts +49 -0
- package/dist/src/utilities/index.d.ts +11 -0
- package/dist/src/utilities/schema.d.ts +10 -0
- package/dist/types/format.d.ts +7 -0
- package/dist/types/format.js +3 -0
- package/dist/types/format.js.map +1 -0
- package/dist/types/keywords.d.ts +78 -0
- package/dist/types/keywords.js +4 -0
- package/dist/types/keywords.js.map +1 -0
- package/dist/types/schema.d.ts +123 -0
- package/dist/types/schema.js +3 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/standalone.d.ts +4 -0
- package/dist/types/standalone.js +3 -0
- package/dist/types/standalone.js.map +1 -0
- package/dist/types/validation.d.ts +49 -0
- package/dist/types/validation.js +3 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utilities/index.d.ts +11 -0
- package/dist/utilities/index.js +146 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/schema.d.ts +10 -0
- package/dist/utilities/schema.js +232 -0
- package/dist/utilities/schema.js.map +1 -0
- package/dist/validator.umd.js +6196 -0
- package/package.json +79 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JetValidator = void 0;
|
|
4
|
+
const compileSchema_1 = require("./compileSchema");
|
|
5
|
+
const formats_1 = require("./formats");
|
|
6
|
+
const resolver_1 = require("./resolver");
|
|
7
|
+
const utilities_1 = require("./utilities");
|
|
8
|
+
const schema_1 = require("./utilities/schema");
|
|
9
|
+
class JetValidator {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.schemas = {};
|
|
12
|
+
this.metaSchemas = {};
|
|
13
|
+
this.customKeywords = new Map();
|
|
14
|
+
this.hasMacros = false;
|
|
15
|
+
this.counter = 0;
|
|
16
|
+
this.aliases = {
|
|
17
|
+
"draft-06": "https://json-schema.org/draft-06/schema",
|
|
18
|
+
"draft-07": "https://json-schema.org/draft-07/schema",
|
|
19
|
+
"draft/2019-09": "https://json-schema.org/draft/2019-09/schema",
|
|
20
|
+
"draft/2020-12": "https://json-schema.org/draft/2020-12/schema",
|
|
21
|
+
};
|
|
22
|
+
this.options = {
|
|
23
|
+
allErrors: options.allErrors ?? false,
|
|
24
|
+
inlineRefs: options.inlineRefs ?? true,
|
|
25
|
+
overwrittenFormats: options.overwrittenFormats ?? [],
|
|
26
|
+
verbose: options.verbose ?? false,
|
|
27
|
+
debug: options.debug ?? false,
|
|
28
|
+
logFunction: options.logFunction ?? false,
|
|
29
|
+
strict: options.strict ?? true,
|
|
30
|
+
metaSchema: options.metaSchema ?? "",
|
|
31
|
+
draft: options.draft ?? "draft2019-09",
|
|
32
|
+
validateFormats: options.validateFormats ?? true,
|
|
33
|
+
formatMode: options.formatMode ?? "full",
|
|
34
|
+
loopEnum: options.loopEnum ?? 200,
|
|
35
|
+
loopRequired: options.loopRequired ?? 200,
|
|
36
|
+
formats: options.formats ?? [],
|
|
37
|
+
loadSchema: options.loadSchema ??
|
|
38
|
+
(() => {
|
|
39
|
+
throw new Error("loadSchema not provided");
|
|
40
|
+
}),
|
|
41
|
+
allowFormatOverride: options.allowFormatOverride ?? false,
|
|
42
|
+
$data: options.$data ?? false,
|
|
43
|
+
removeAdditional: options.removeAdditional ?? false,
|
|
44
|
+
useDefaults: options.useDefaults ?? false,
|
|
45
|
+
coerceTypes: options.coerceTypes ?? false,
|
|
46
|
+
cache: options.cache ?? true,
|
|
47
|
+
strictSchema: options.strictSchema ?? false,
|
|
48
|
+
strictNumbers: options.strictNumbers ?? false,
|
|
49
|
+
strictRequired: options.strictRequired ?? false,
|
|
50
|
+
strictTypes: options.strictTypes ?? false,
|
|
51
|
+
async: options.async ?? false,
|
|
52
|
+
validateSchema: options.validateSchema ?? false,
|
|
53
|
+
addUsedSchema: options.addUsedSchema ?? true,
|
|
54
|
+
errorMessage: options.errorMessage ?? false,
|
|
55
|
+
};
|
|
56
|
+
if (this.options.formatMode !== false) {
|
|
57
|
+
this.formatValidators =
|
|
58
|
+
this.options.formatMode === "full"
|
|
59
|
+
? { ...formats_1.FULL_FORMAT_VALIDATORS }
|
|
60
|
+
: { ...formats_1.FAST_FORMAT_VALIDATORS };
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.formatValidators = {};
|
|
64
|
+
}
|
|
65
|
+
this.compilationCache = new Map();
|
|
66
|
+
}
|
|
67
|
+
//#region
|
|
68
|
+
addFormat(key, validator, options) {
|
|
69
|
+
const shouldOverride = options?.override ?? this.options.allowFormatOverride;
|
|
70
|
+
if (!shouldOverride && key in this.formatValidators)
|
|
71
|
+
throw Error(`Format "${key}" is already registered, call removeFormat("${key}") before Attempting to add.`);
|
|
72
|
+
this.formatValidators[key] = validator;
|
|
73
|
+
}
|
|
74
|
+
removeFormat(key) {
|
|
75
|
+
if (!(key in this.formatValidators)) {
|
|
76
|
+
throw new Error(`Format "${key}" is not registered.`);
|
|
77
|
+
}
|
|
78
|
+
delete this.formatValidators[key];
|
|
79
|
+
}
|
|
80
|
+
getFormat(format) {
|
|
81
|
+
return this.formatValidators[format];
|
|
82
|
+
}
|
|
83
|
+
testFormat(value, format) {
|
|
84
|
+
const validator = this.formatValidators[format];
|
|
85
|
+
if (!validator)
|
|
86
|
+
return true;
|
|
87
|
+
if (validator instanceof RegExp) {
|
|
88
|
+
return validator.test(value);
|
|
89
|
+
}
|
|
90
|
+
else if (typeof validator === "function") {
|
|
91
|
+
return validator(value);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const validate = validator.validate;
|
|
95
|
+
if (validate instanceof RegExp) {
|
|
96
|
+
return validate.test(value);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return validate(value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
isFormatRegistered(key) {
|
|
104
|
+
return key in this.formatValidators;
|
|
105
|
+
}
|
|
106
|
+
getRegisteredFormats() {
|
|
107
|
+
return Object.keys(this.formatValidators);
|
|
108
|
+
}
|
|
109
|
+
validateFormat(value, format) {
|
|
110
|
+
const isValid = this.testFormat(value, format);
|
|
111
|
+
return isValid
|
|
112
|
+
? { valid: true }
|
|
113
|
+
: {
|
|
114
|
+
valid: false,
|
|
115
|
+
errors: { message: `Failed to validate format '${format}'` },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
getAllFormats() {
|
|
119
|
+
return { ...this.formatValidators };
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region
|
|
123
|
+
addKeyword(definition) {
|
|
124
|
+
if (schema_1.baseSchemaKeys.has(definition.keyword)) {
|
|
125
|
+
throw new Error(`Keyword "${definition.keyword}" is a predefined keyword and cannot be registered.`);
|
|
126
|
+
}
|
|
127
|
+
this.validateKeywordDefinition(definition);
|
|
128
|
+
this.customKeywords.set(definition.keyword, definition);
|
|
129
|
+
if ("macro" in definition) {
|
|
130
|
+
this.hasMacros = true;
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
removeKeyword(keyword) {
|
|
135
|
+
this.customKeywords.delete(keyword);
|
|
136
|
+
this.hasMacros = false;
|
|
137
|
+
for (const def of this.customKeywords.values()) {
|
|
138
|
+
if ("macro" in def) {
|
|
139
|
+
this.hasMacros = true;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
hasMacroKeywords() {
|
|
146
|
+
return this.hasMacros;
|
|
147
|
+
}
|
|
148
|
+
getKeyword(keyword) {
|
|
149
|
+
return this.customKeywords.get(keyword);
|
|
150
|
+
}
|
|
151
|
+
validateKeywordDefinition(def) {
|
|
152
|
+
const approaches = [];
|
|
153
|
+
if ("macro" in def && def.macro)
|
|
154
|
+
approaches.push(def.macro);
|
|
155
|
+
if ("compile" in def && def.compile)
|
|
156
|
+
approaches.push(def.compile);
|
|
157
|
+
if ("validate" in def && def.validate)
|
|
158
|
+
approaches.push(def.validate);
|
|
159
|
+
if ("code" in def && def.code)
|
|
160
|
+
approaches.push(def.code);
|
|
161
|
+
if (approaches.length === 0) {
|
|
162
|
+
throw new Error(`Keyword "${def.keyword}" must have at least one of: macro, compile, validate, or code`);
|
|
163
|
+
}
|
|
164
|
+
if (approaches.length > 1) {
|
|
165
|
+
throw new Error(`Keyword "${def.keyword}" can only have ONE of: macro, compile, validate, or code`);
|
|
166
|
+
}
|
|
167
|
+
if (def.schemaType) {
|
|
168
|
+
const validTypes = [
|
|
169
|
+
"string",
|
|
170
|
+
"number",
|
|
171
|
+
"boolean",
|
|
172
|
+
"array",
|
|
173
|
+
"object",
|
|
174
|
+
"null",
|
|
175
|
+
];
|
|
176
|
+
const types = Array.isArray(def.schemaType)
|
|
177
|
+
? def.schemaType
|
|
178
|
+
: [def.schemaType];
|
|
179
|
+
for (const type of types) {
|
|
180
|
+
if (!validTypes.includes(type)) {
|
|
181
|
+
throw new Error(`Invalid schemaType "${type}" for keyword "${def.keyword}"`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (def.type) {
|
|
186
|
+
const validTypes = [
|
|
187
|
+
"string",
|
|
188
|
+
"number",
|
|
189
|
+
"integer",
|
|
190
|
+
"boolean",
|
|
191
|
+
"array",
|
|
192
|
+
"object",
|
|
193
|
+
"null",
|
|
194
|
+
];
|
|
195
|
+
const types = Array.isArray(def.type)
|
|
196
|
+
? def.type
|
|
197
|
+
: [def.type];
|
|
198
|
+
for (const type of types) {
|
|
199
|
+
if (!validTypes.includes(type)) {
|
|
200
|
+
throw new Error(`Invalid type "${type}" for keyword "${def.keyword}"`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (def.metaSchema) {
|
|
205
|
+
if (typeof def.metaSchema !== "object" ||
|
|
206
|
+
def.metaSchema === null) {
|
|
207
|
+
throw new Error(`metaSchema for keyword "${def.keyword}" must be an object`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
isKeywordAdded(key) {
|
|
212
|
+
return this.customKeywords.has(key);
|
|
213
|
+
}
|
|
214
|
+
getAddedKeywords() {
|
|
215
|
+
return Object.keys(this.customKeywords);
|
|
216
|
+
}
|
|
217
|
+
clearKeywords() {
|
|
218
|
+
this.customKeywords.clear();
|
|
219
|
+
}
|
|
220
|
+
getAllKeywords() {
|
|
221
|
+
return this.customKeywords;
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region
|
|
225
|
+
addSchema(schema, id) {
|
|
226
|
+
const key = id || schema.$id;
|
|
227
|
+
if (!key)
|
|
228
|
+
throw Error("Attempting to register a schema that has no defined id.");
|
|
229
|
+
schema.$id = key;
|
|
230
|
+
this.schemas[key] = structuredClone(schema);
|
|
231
|
+
}
|
|
232
|
+
getSchema(key) {
|
|
233
|
+
return structuredClone(this.schemas[key]);
|
|
234
|
+
}
|
|
235
|
+
getCompiledSchema(key, config) {
|
|
236
|
+
if (this.schemas[key] !== undefined) {
|
|
237
|
+
return this.compile(this.schemas[key], config);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
throw Error(`Schema ${key} not found in registry.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async getCompiledSchemaAsync(key, config) {
|
|
244
|
+
if (this.schemas[key] !== undefined) {
|
|
245
|
+
return await this.compileAsync(this.schemas[key], config);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
throw Error(`Schema ${key} not found in registry.`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
isSchemaAdded(key) {
|
|
252
|
+
return key in this.schemas;
|
|
253
|
+
}
|
|
254
|
+
getAddedSchemas() {
|
|
255
|
+
return Object.keys(this.schemas);
|
|
256
|
+
}
|
|
257
|
+
removeSchema(pattern) {
|
|
258
|
+
if (pattern === undefined) {
|
|
259
|
+
this.schemas = {};
|
|
260
|
+
if (this.options.cache) {
|
|
261
|
+
this.compilationCache.clear();
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (typeof pattern === "string") {
|
|
266
|
+
if (!(pattern in this.schemas)) {
|
|
267
|
+
throw new Error(`Schema "${pattern}" is not registered.`);
|
|
268
|
+
}
|
|
269
|
+
delete this.schemas[pattern];
|
|
270
|
+
if (this.options.cache) {
|
|
271
|
+
this.compilationCache.delete(pattern);
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (pattern instanceof RegExp) {
|
|
276
|
+
const keys = Object.keys(this.schemas);
|
|
277
|
+
let removed = 0;
|
|
278
|
+
for (const key of keys) {
|
|
279
|
+
if (pattern.test(key)) {
|
|
280
|
+
delete this.schemas[key];
|
|
281
|
+
if (this.options.cache) {
|
|
282
|
+
this.compilationCache.delete(key);
|
|
283
|
+
}
|
|
284
|
+
removed++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (removed === 0) {
|
|
288
|
+
console.warn(`No schemas matched pattern: ${pattern}`);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (typeof pattern === "object") {
|
|
293
|
+
const keys = Object.keys(this.schemas);
|
|
294
|
+
let found = false;
|
|
295
|
+
for (const key of keys) {
|
|
296
|
+
if (this.schemas[key] === pattern) {
|
|
297
|
+
delete this.schemas[key];
|
|
298
|
+
if (this.options.cache) {
|
|
299
|
+
this.compilationCache.delete(key);
|
|
300
|
+
}
|
|
301
|
+
found = true;
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (!found) {
|
|
306
|
+
throw new Error("Schema object not found in registry");
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
throw new Error("Invalid pattern type for removeSchema");
|
|
311
|
+
}
|
|
312
|
+
clearSchemas() {
|
|
313
|
+
this.schemas = {};
|
|
314
|
+
}
|
|
315
|
+
getAllSchemas() {
|
|
316
|
+
return { ...this.schemas };
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region
|
|
320
|
+
validate(schema, data, config) {
|
|
321
|
+
let finalSchema;
|
|
322
|
+
if (typeof schema === "object" || typeof schema === "boolean") {
|
|
323
|
+
finalSchema = schema;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
finalSchema = this.schemas[schema];
|
|
327
|
+
}
|
|
328
|
+
if (finalSchema !== undefined) {
|
|
329
|
+
let validator;
|
|
330
|
+
if (typeof finalSchema !== "boolean") {
|
|
331
|
+
const func = typeof schema === "string"
|
|
332
|
+
? this.compilationCache.get(schema)
|
|
333
|
+
: this.compilationCache.get(finalSchema.$id ?? finalSchema);
|
|
334
|
+
if (func)
|
|
335
|
+
validator = func;
|
|
336
|
+
}
|
|
337
|
+
if (!validator) {
|
|
338
|
+
validator = this.compile(finalSchema, config);
|
|
339
|
+
}
|
|
340
|
+
const valid = validator(data);
|
|
341
|
+
return { valid, errors: validator.errors };
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
throw Error(`Schema ${schema} was not found in registry.`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async validateAsync(schema, data, config) {
|
|
348
|
+
let finalSchema;
|
|
349
|
+
if (typeof schema === "object" || typeof schema === "boolean") {
|
|
350
|
+
finalSchema = schema;
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
finalSchema = this.schemas[schema];
|
|
354
|
+
}
|
|
355
|
+
if (finalSchema !== undefined) {
|
|
356
|
+
let validator;
|
|
357
|
+
if (typeof finalSchema !== "boolean") {
|
|
358
|
+
const func = typeof schema === "string"
|
|
359
|
+
? this.compilationCache.get(schema)
|
|
360
|
+
: this.compilationCache.get(finalSchema.$id ?? finalSchema);
|
|
361
|
+
if (func)
|
|
362
|
+
validator = func;
|
|
363
|
+
}
|
|
364
|
+
if (!validator) {
|
|
365
|
+
validator = await this.compileAsync(finalSchema, config);
|
|
366
|
+
}
|
|
367
|
+
const valid = await validator(data);
|
|
368
|
+
return { valid, errors: validator.errors };
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
throw Error(`Schema ${schema} was not found in registry.`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region
|
|
376
|
+
getMetaSchema($schema, options) {
|
|
377
|
+
let metaSchemaId = options?.metaSchema;
|
|
378
|
+
if (!metaSchemaId && $schema) {
|
|
379
|
+
metaSchemaId = $schema;
|
|
380
|
+
}
|
|
381
|
+
if (!metaSchemaId) {
|
|
382
|
+
metaSchemaId =
|
|
383
|
+
this.options.metaSchema || "https://json-schema.org/draft-07/schema";
|
|
384
|
+
}
|
|
385
|
+
const finalId = this.aliases[metaSchemaId] ?? metaSchemaId;
|
|
386
|
+
let metaSchema = this.metaSchemas[finalId];
|
|
387
|
+
if (!metaSchema) {
|
|
388
|
+
if (options?.strictSchema) {
|
|
389
|
+
throw new Error(`Meta-schema "${metaSchemaId}" is not loaded.\n` +
|
|
390
|
+
`Load it using: loadDraft07(jetValidator) or loadAllMetaSchemas(jetValidator)\n` +
|
|
391
|
+
`Or disable validation: new JetValidator({ validateSchema: false })`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return { metaSchema, metaSchemaId };
|
|
395
|
+
}
|
|
396
|
+
validateSchemaSync(schema, options) {
|
|
397
|
+
const { metaSchema } = this.getMetaSchema(schema.$schema, options);
|
|
398
|
+
if (!metaSchema)
|
|
399
|
+
return { valid: false, errors: [{ message: "metaSchema not found" }] };
|
|
400
|
+
const validator = this.compile(metaSchema, {
|
|
401
|
+
...options,
|
|
402
|
+
logFunction: true,
|
|
403
|
+
validateSchema: false,
|
|
404
|
+
});
|
|
405
|
+
const result = validator(schema);
|
|
406
|
+
if (!result && options?.strictSchema) {
|
|
407
|
+
console.log(validator.errors);
|
|
408
|
+
throw Error();
|
|
409
|
+
}
|
|
410
|
+
if (!result)
|
|
411
|
+
validator.errors[0]["metaSchemaError"] = true;
|
|
412
|
+
return { valid: result, errors: validator.errors };
|
|
413
|
+
}
|
|
414
|
+
async validateSchemaAsync(schema, options) {
|
|
415
|
+
let metaSchema;
|
|
416
|
+
let { metaSchema: mSchema, metaSchemaId } = this.getMetaSchema(schema.$schema, options);
|
|
417
|
+
if (mSchema) {
|
|
418
|
+
metaSchema = mSchema;
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
metaSchema = await this.options.loadSchema(metaSchemaId);
|
|
422
|
+
}
|
|
423
|
+
const validator = await this.compileAsync(metaSchema, {
|
|
424
|
+
...options,
|
|
425
|
+
validateSchema: false,
|
|
426
|
+
async: true,
|
|
427
|
+
});
|
|
428
|
+
const result = await validator(schema);
|
|
429
|
+
if (!result && options?.strictSchema) {
|
|
430
|
+
console.log(validator.errors);
|
|
431
|
+
throw Error();
|
|
432
|
+
}
|
|
433
|
+
if (!result)
|
|
434
|
+
validator.errors[0]["metaSchemaError"] = true;
|
|
435
|
+
return { valid: result, errors: validator.errors };
|
|
436
|
+
}
|
|
437
|
+
addMetaSchema(schema, key) {
|
|
438
|
+
let Key = key || schema.$id;
|
|
439
|
+
if (!Key) {
|
|
440
|
+
throw new Error("Meta-schema must have an $id or explicit key");
|
|
441
|
+
}
|
|
442
|
+
const schemaKey = this.aliases[Key] ?? Key;
|
|
443
|
+
if (!(schemaKey in this.metaSchemas)) {
|
|
444
|
+
this.metaSchemas[schemaKey] = structuredClone(schema);
|
|
445
|
+
}
|
|
446
|
+
return this;
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
clearRegistries() {
|
|
450
|
+
this.schemas = {};
|
|
451
|
+
this.formatValidators = {};
|
|
452
|
+
this.clearKeywords();
|
|
453
|
+
if (this.options.cache) {
|
|
454
|
+
this.compilationCache.clear();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
compileResolved(resolvedSchema, mainSchema, refables, allFormats, allKeywords, config, compileContext) {
|
|
458
|
+
const includesItemsRef = compileContext.hasUnevaluatedItems;
|
|
459
|
+
const includesPropRef = compileContext.hasUnevaluatedProperties;
|
|
460
|
+
const fconfig = {
|
|
461
|
+
...config,
|
|
462
|
+
};
|
|
463
|
+
if (typeof resolvedSchema === "boolean")
|
|
464
|
+
fconfig.allErrors = false;
|
|
465
|
+
const compiler = new compileSchema_1.Compiler(refables, mainSchema, fconfig, this, allKeywords, compileContext, false);
|
|
466
|
+
let source;
|
|
467
|
+
if (compileContext.hasRootReference) {
|
|
468
|
+
source = compiler.compileSchema(resolvedSchema, {
|
|
469
|
+
schema: `\${path.schema}`,
|
|
470
|
+
data: "${path.data}",
|
|
471
|
+
$data: "",
|
|
472
|
+
}, {
|
|
473
|
+
parentHasUnevaluatedProperties: includesPropRef,
|
|
474
|
+
parentUnevaluatedPropVar: includesPropRef
|
|
475
|
+
? "evaluatedProperties"
|
|
476
|
+
: undefined,
|
|
477
|
+
parentHasUnevaluatedItems: includesItemsRef,
|
|
478
|
+
parentUnevaluatedItemVar: includesItemsRef
|
|
479
|
+
? "evaluatedItems"
|
|
480
|
+
: undefined,
|
|
481
|
+
isSubschema: true,
|
|
482
|
+
}, "rootData");
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
source = compiler.compileSchema(resolvedSchema, undefined, undefined, "rootData");
|
|
486
|
+
}
|
|
487
|
+
const keywords = compiler.getCompiledKeywords();
|
|
488
|
+
const formatValidators = {};
|
|
489
|
+
const customKeywords = new Map(keywords.compiledKeywords);
|
|
490
|
+
for (const keywordDef of keywords.validateKeywords) {
|
|
491
|
+
const validate = this.customKeywords.get(keywordDef)?.validate;
|
|
492
|
+
if (validate)
|
|
493
|
+
customKeywords.set(keywordDef, validate);
|
|
494
|
+
}
|
|
495
|
+
if (typeof resolvedSchema !== "boolean") {
|
|
496
|
+
if (allFormats.size > 0) {
|
|
497
|
+
for (const validatorKey of allFormats) {
|
|
498
|
+
const validator = this.formatValidators[validatorKey];
|
|
499
|
+
if (validator) {
|
|
500
|
+
if (typeof validator === "function" ||
|
|
501
|
+
validator instanceof RegExp) {
|
|
502
|
+
formatValidators[validatorKey] = validator;
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
formatValidators[validatorKey] = validator.validate;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const asyncPrefix = config.async ? "async " : "";
|
|
512
|
+
let functionDeclaration = "validate(rootData";
|
|
513
|
+
if (compileContext.hasRootReference) {
|
|
514
|
+
if (includesItemsRef || includesPropRef) {
|
|
515
|
+
if (includesPropRef)
|
|
516
|
+
functionDeclaration = functionDeclaration + ",evaluatedProperties";
|
|
517
|
+
if (includesItemsRef)
|
|
518
|
+
functionDeclaration = functionDeclaration + ",evaluatedItems";
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (compileContext.hasRootReference)
|
|
522
|
+
functionDeclaration = functionDeclaration + ",path";
|
|
523
|
+
functionDeclaration = functionDeclaration + ")";
|
|
524
|
+
const finalSource = `
|
|
525
|
+
${compiler.hoistedFunctions.join("")}
|
|
526
|
+
${asyncPrefix}function ${functionDeclaration}{${compileContext.hasRootReference
|
|
527
|
+
? 'if (!path) {path = { schema: "#", data: "" };}'
|
|
528
|
+
: ""}${source}${fconfig.allErrors
|
|
529
|
+
? `validate.errors = allErrors; return allErrors.length == 0`
|
|
530
|
+
: "return true"};} return validate;
|
|
531
|
+
`;
|
|
532
|
+
if (config.logFunction) {
|
|
533
|
+
console.log(finalSource);
|
|
534
|
+
}
|
|
535
|
+
const regexParams = [];
|
|
536
|
+
const regexArgs = [];
|
|
537
|
+
if (compiler.regexCache.size > 0) {
|
|
538
|
+
for (const [key, value] of compiler.regexCache.entries()) {
|
|
539
|
+
regexParams.push(value);
|
|
540
|
+
regexArgs.push(new RegExp(key));
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return new Function("formatValidators", "deepEqual", "canonicalStringify", "customKeywords", "len_of", ...regexParams, finalSource)(formatValidators, utilities_1.deepEqual, utilities_1.canonicalStringify, customKeywords, utilities_1.len_of, ...regexArgs);
|
|
544
|
+
}
|
|
545
|
+
compile(fschema, config) {
|
|
546
|
+
const schema = typeof fschema === "boolean" ? fschema : fschema;
|
|
547
|
+
const finalConfig = {
|
|
548
|
+
...this.options,
|
|
549
|
+
...config,
|
|
550
|
+
};
|
|
551
|
+
if (typeof schema === "object" &&
|
|
552
|
+
finalConfig.validateSchema &&
|
|
553
|
+
(finalConfig.metaSchema || schema.$schema)) {
|
|
554
|
+
const result = this.validateSchemaSync(schema, {
|
|
555
|
+
metaSchema: finalConfig?.metaSchema,
|
|
556
|
+
});
|
|
557
|
+
if (!result.valid) {
|
|
558
|
+
const validator = (data) => result.valid;
|
|
559
|
+
validator.errors = result.errors || [];
|
|
560
|
+
return validator;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (finalConfig.cache && typeof schema !== "boolean") {
|
|
564
|
+
if (this.compilationCache.has(schema?.$id) ||
|
|
565
|
+
this.compilationCache.has(schema)) {
|
|
566
|
+
return (this.compilationCache.get(schema?.$id) ??
|
|
567
|
+
this.compilationCache.get(schema));
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const resolver = new resolver_1.SchemaResolver(this, finalConfig);
|
|
571
|
+
const resolved = resolver.resolveSync(schema);
|
|
572
|
+
const validator = this.compileResolved(resolved.schema, schema, resolved.refables, resolved.allFormats, resolved.keywords, finalConfig, resolved.compileContext);
|
|
573
|
+
if (finalConfig.cache && typeof schema === "object" && schema !== null) {
|
|
574
|
+
const schem = schema;
|
|
575
|
+
this.compilationCache.set(schem.$id ?? schem.id ?? schema, validator);
|
|
576
|
+
}
|
|
577
|
+
return validator;
|
|
578
|
+
}
|
|
579
|
+
async compileAsync(fschema, config) {
|
|
580
|
+
const schema = typeof fschema === "boolean" ? fschema : fschema;
|
|
581
|
+
const finalConfig = {
|
|
582
|
+
...this.options,
|
|
583
|
+
...config,
|
|
584
|
+
};
|
|
585
|
+
if (typeof schema === "object" &&
|
|
586
|
+
finalConfig.validateSchema &&
|
|
587
|
+
(finalConfig.metaSchema || schema.$schema)) {
|
|
588
|
+
const result = await this.validateSchemaAsync(schema, {
|
|
589
|
+
metaSchema: finalConfig?.metaSchema,
|
|
590
|
+
});
|
|
591
|
+
if (!result.valid) {
|
|
592
|
+
const validator = (data) => result.valid;
|
|
593
|
+
validator.errors = result.errors || [];
|
|
594
|
+
return validator;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (finalConfig.cache && typeof schema !== "boolean") {
|
|
598
|
+
if (this.compilationCache.has(schema?.$id) ||
|
|
599
|
+
this.compilationCache.has(schema)) {
|
|
600
|
+
return (this.compilationCache.get(schema?.$id) ??
|
|
601
|
+
this.compilationCache.get(schema));
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const resolver = new resolver_1.SchemaResolver(this, finalConfig);
|
|
605
|
+
const resolved = await resolver.resolveAsync(schema, finalConfig.loadSchema);
|
|
606
|
+
const validator = this.compileResolved(resolved.schema, schema, resolved.refables, resolved.allFormats, resolved.keywords, finalConfig, resolved.compileContext);
|
|
607
|
+
if (finalConfig.cache && typeof schema === "object") {
|
|
608
|
+
this.compilationCache.set(schema, validator);
|
|
609
|
+
}
|
|
610
|
+
return validator;
|
|
611
|
+
}
|
|
612
|
+
logErrors(errors, indent = 0) {
|
|
613
|
+
const spacer = " ".repeat(indent); // Create indentation
|
|
614
|
+
if (Array.isArray(errors)) {
|
|
615
|
+
// If the input is an array (like subErrors), iterate through each error
|
|
616
|
+
errors.forEach((err) => this.logErrors(err, indent));
|
|
617
|
+
}
|
|
618
|
+
else if (errors && typeof errors === "object") {
|
|
619
|
+
// If the input is a single error object
|
|
620
|
+
console.log(`${spacer}❌ Validation Failed: ${errors.message || "Unknown error"}`);
|
|
621
|
+
if (errors.dataPath) {
|
|
622
|
+
console.log(`${spacer} - Data Path: ${errors.dataPath}`);
|
|
623
|
+
}
|
|
624
|
+
if (errors.schemaPath) {
|
|
625
|
+
console.log(`${spacer} - Schema Path: ${errors.schemaPath}`);
|
|
626
|
+
}
|
|
627
|
+
if (errors.rule) {
|
|
628
|
+
console.log(`${spacer} - Rule: ${errors.rule}`);
|
|
629
|
+
}
|
|
630
|
+
if (errors.expected) {
|
|
631
|
+
console.log(`${spacer} - Expected: ${errors.expected}`);
|
|
632
|
+
}
|
|
633
|
+
if (errors.subErrors) {
|
|
634
|
+
console.log(`${spacer} - Sub-errors:`);
|
|
635
|
+
this.logErrors(errors.subErrors, indent + 1); // Recursive call for nested errors
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
getFieldFromPath(dataPath) {
|
|
640
|
+
if (!dataPath || dataPath === "/")
|
|
641
|
+
return "";
|
|
642
|
+
const segments = dataPath.split("/").filter(Boolean);
|
|
643
|
+
return segments[segments.length - 1];
|
|
644
|
+
}
|
|
645
|
+
getFullFieldPath(dataPath) {
|
|
646
|
+
if (!dataPath || dataPath === "/")
|
|
647
|
+
return "";
|
|
648
|
+
return dataPath
|
|
649
|
+
.slice(1)
|
|
650
|
+
.replace(/\/(\d+)/g, "[$1]")
|
|
651
|
+
.replace(/\//g, ".");
|
|
652
|
+
}
|
|
653
|
+
getFieldErrors(errors) {
|
|
654
|
+
const byField = {};
|
|
655
|
+
for (const error of errors) {
|
|
656
|
+
const field = error.dataPath || "/";
|
|
657
|
+
if (!byField[field])
|
|
658
|
+
byField[field] = [];
|
|
659
|
+
byField[field].push(error.message);
|
|
660
|
+
}
|
|
661
|
+
return byField;
|
|
662
|
+
}
|
|
663
|
+
errorsText(errors, options) {
|
|
664
|
+
const sep = options?.separator ?? ", ";
|
|
665
|
+
const dataVar = options?.dataVar ?? "data";
|
|
666
|
+
return errors
|
|
667
|
+
.map((e) => {
|
|
668
|
+
const path = e.dataPath || "/";
|
|
669
|
+
const fullPath = path === "/" ? dataVar : `${dataVar}${path.replace(/\//g, ".")}`;
|
|
670
|
+
return `${fullPath}: ${e.message}`;
|
|
671
|
+
})
|
|
672
|
+
.join(sep);
|
|
673
|
+
}
|
|
674
|
+
generateStandalone(schema, sconfig) {
|
|
675
|
+
const code = [];
|
|
676
|
+
const formatImports = [];
|
|
677
|
+
const config = { ...this.options, ...sconfig };
|
|
678
|
+
let generatedFunctionName;
|
|
679
|
+
if (this.counter === 0) {
|
|
680
|
+
generatedFunctionName = "validate" + this.counter;
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
generatedFunctionName = "validate" + this.counter++;
|
|
684
|
+
}
|
|
685
|
+
const resolver = new resolver_1.SchemaResolver(this, config);
|
|
686
|
+
resolver.rootFunctionName = generatedFunctionName;
|
|
687
|
+
const resolved = resolver.resolveSync(schema);
|
|
688
|
+
const includesItemsRef = resolved.compileContext.hasUnevaluatedItems;
|
|
689
|
+
const includesPropRef = resolved.compileContext.hasUnevaluatedProperties;
|
|
690
|
+
const has$Data = resolved.compileContext.uses$Data;
|
|
691
|
+
const compiler = new compileSchema_1.Compiler(resolved.refables, schema, { ...config }, this, resolved.keywords, resolved.compileContext);
|
|
692
|
+
compiler.mainFunctionName = generatedFunctionName;
|
|
693
|
+
let source;
|
|
694
|
+
if (resolved.compileContext.hasRootReference) {
|
|
695
|
+
source = compiler.compileSchema(resolved.schema, {
|
|
696
|
+
schema: `\${path.schema}`,
|
|
697
|
+
data: "${path.data}",
|
|
698
|
+
$data: "",
|
|
699
|
+
}, {
|
|
700
|
+
parentHasUnevaluatedProperties: includesPropRef,
|
|
701
|
+
parentUnevaluatedPropVar: includesPropRef
|
|
702
|
+
? "evaluatedProperties"
|
|
703
|
+
: undefined,
|
|
704
|
+
parentHasUnevaluatedItems: includesItemsRef,
|
|
705
|
+
parentUnevaluatedItemVar: includesItemsRef
|
|
706
|
+
? "evaluatedItems"
|
|
707
|
+
: undefined,
|
|
708
|
+
isSubschema: true,
|
|
709
|
+
}, "rootData");
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
source = compiler.compileSchema(resolved.schema, undefined, undefined, "rootData");
|
|
713
|
+
}
|
|
714
|
+
const keywords = compiler.getCompiledKeywords();
|
|
715
|
+
if (keywords.hasCompileKeyword) {
|
|
716
|
+
code.push(`const compilerOptions = ${JSON.stringify(config)};\n`);
|
|
717
|
+
code.push(`const mainRootSchema = ${JSON.stringify(schema)};\n`);
|
|
718
|
+
}
|
|
719
|
+
const inlinedFormats = new Set();
|
|
720
|
+
if (has$Data) {
|
|
721
|
+
this.inlineAllConfiguredFormats(code, config, inlinedFormats);
|
|
722
|
+
this.createFormatObject(code, config, inlinedFormats);
|
|
723
|
+
}
|
|
724
|
+
else if (resolved.allFormats.size > 0) {
|
|
725
|
+
this.inlineUsedFormats(code, resolved.allFormats, config, formatImports, inlinedFormats);
|
|
726
|
+
}
|
|
727
|
+
for (const keywordDef of keywords.validateKeywords) {
|
|
728
|
+
const validate = this.customKeywords.get(keywordDef)?.validate;
|
|
729
|
+
if (validate) {
|
|
730
|
+
code.push(`const ${keywordDef} = ${validate.toString()};\n`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (compiler.needslen_of)
|
|
734
|
+
code.push(utilities_1.len_of.toString() + ";");
|
|
735
|
+
if (compiler.needsStringify)
|
|
736
|
+
code.push(utilities_1.canonicalStringify.toString() + ";");
|
|
737
|
+
if (compiler.needsDeepEqual)
|
|
738
|
+
code.push(utilities_1.deepEqual.toString() + ";");
|
|
739
|
+
const asyncPrefix = config.async ? "async " : "";
|
|
740
|
+
let functionDeclaration = "validate(rootData";
|
|
741
|
+
if (resolved.compileContext.hasRootReference) {
|
|
742
|
+
if (includesItemsRef || includesPropRef) {
|
|
743
|
+
if (includesPropRef)
|
|
744
|
+
functionDeclaration += ",evaluatedProperties";
|
|
745
|
+
if (includesItemsRef)
|
|
746
|
+
functionDeclaration += ",evaluatedItems";
|
|
747
|
+
}
|
|
748
|
+
functionDeclaration += ",path";
|
|
749
|
+
}
|
|
750
|
+
functionDeclaration += ")";
|
|
751
|
+
let regexDeclaration = "";
|
|
752
|
+
if (compiler.regexCache.size > 0) {
|
|
753
|
+
for (const [key, value] of compiler.regexCache.entries()) {
|
|
754
|
+
regexDeclaration =
|
|
755
|
+
regexDeclaration +
|
|
756
|
+
`const ${value} = new RegExp(${JSON.stringify(key)});\n`;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
code.push(compiler.hoistedFunctions.join(""));
|
|
760
|
+
const finalSource = `
|
|
761
|
+
${asyncPrefix}function ${functionDeclaration} {
|
|
762
|
+
${regexDeclaration}${resolved.compileContext.hasRootReference
|
|
763
|
+
? '\n if (!path) { path = { schema: "#", data: "" }; }'
|
|
764
|
+
: ""}
|
|
765
|
+
${source}
|
|
766
|
+
${this.options.allErrors
|
|
767
|
+
? `validate.errors = allErrors; return allErrors.length == 0`
|
|
768
|
+
: " return true"}
|
|
769
|
+
}
|
|
770
|
+
`;
|
|
771
|
+
code.push(finalSource);
|
|
772
|
+
let formatSetup;
|
|
773
|
+
if (formatImports.length > 0) {
|
|
774
|
+
formatSetup = this.generateFormatSetup(formatImports);
|
|
775
|
+
}
|
|
776
|
+
return {
|
|
777
|
+
code: code.join("\n"),
|
|
778
|
+
functionName: generatedFunctionName,
|
|
779
|
+
formatSetup,
|
|
780
|
+
imports: formatImports,
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
inlineUsedFormats(code, usedFormats, config, formatImports, inlinedFormats) {
|
|
784
|
+
const overwrittenFormats = config.overwrittenFormats || [];
|
|
785
|
+
code.push("// Format validators\n");
|
|
786
|
+
for (const formatName of usedFormats) {
|
|
787
|
+
if (inlinedFormats.has(formatName))
|
|
788
|
+
continue;
|
|
789
|
+
const validator = this.formatValidators[formatName];
|
|
790
|
+
if (!validator) {
|
|
791
|
+
formatImports.push(formatName);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
const isOverwritten = overwrittenFormats.includes(formatName);
|
|
795
|
+
if (typeof validator === "function" || validator instanceof RegExp) {
|
|
796
|
+
this.resolveFormats(validator, code, formatName, inlinedFormats, isOverwritten, formatImports);
|
|
797
|
+
}
|
|
798
|
+
else if (typeof validator === "object" && "validate" in validator) {
|
|
799
|
+
this.resolveFormats(validator.validate, code, formatName, inlinedFormats, isOverwritten, formatImports);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
code.push("\n");
|
|
803
|
+
}
|
|
804
|
+
resolveFormats(validator, code, formatName, inlinedFormats, isOverwritten, formatImports) {
|
|
805
|
+
if (validator instanceof RegExp) {
|
|
806
|
+
const safeName = this.getSafeFormatName(formatName);
|
|
807
|
+
code.push(`const ${safeName} = new RegExp(${JSON.stringify(validator.source)}, '${validator.flags}');\n`);
|
|
808
|
+
inlinedFormats.add(formatName);
|
|
809
|
+
}
|
|
810
|
+
else if (typeof validator === "function") {
|
|
811
|
+
if (isOverwritten) {
|
|
812
|
+
const fnString = validator.toString();
|
|
813
|
+
if (this.isSelfContained(fnString)) {
|
|
814
|
+
code.push(`const ${this.getSafeFormatName(formatName)} = ${fnString};\n`);
|
|
815
|
+
inlinedFormats.add(formatName);
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
formatImports.push(formatName);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
const needsExternalDeps = this.formatNeedsExternalDeps(formatName);
|
|
823
|
+
if (needsExternalDeps) {
|
|
824
|
+
this.inlineFormatWithDeps(code, formatName, inlinedFormats);
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
if (validator.name) {
|
|
828
|
+
code.push(`${validator.toString()};\n`);
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
code.push(`const ${this.getSafeFormatName(formatName)} = ${validator.toString()};\n`);
|
|
832
|
+
}
|
|
833
|
+
inlinedFormats.add(formatName);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
inlineAllConfiguredFormats(code, config, inlinedFormats) {
|
|
839
|
+
const configuredFormats = config.formats ?? [];
|
|
840
|
+
code.push("// Format validators (all configured for $data support)\n");
|
|
841
|
+
if (configuredFormats?.length > 0) {
|
|
842
|
+
for (const formatName of configuredFormats) {
|
|
843
|
+
if (inlinedFormats.has(formatName))
|
|
844
|
+
continue;
|
|
845
|
+
const validator = this.formatValidators[formatName];
|
|
846
|
+
if (validator) {
|
|
847
|
+
if (validator instanceof RegExp || typeof validator === "function") {
|
|
848
|
+
this.resolve$DataFormat(validator, formatName, inlinedFormats, code);
|
|
849
|
+
}
|
|
850
|
+
else if (typeof validator === "object" && "validate" in validator) {
|
|
851
|
+
this.resolve$DataFormat(validator.validate, formatName, inlinedFormats, code);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (config.formatMode === "fast" || config.formatMode === "full") {
|
|
857
|
+
const validators = this.formatValidators;
|
|
858
|
+
for (const [formatName, validator] of Object.entries(validators)) {
|
|
859
|
+
if (inlinedFormats.has(formatName))
|
|
860
|
+
continue;
|
|
861
|
+
if (validator instanceof RegExp || typeof validator === "function") {
|
|
862
|
+
this.resolve$DataFormat(validator, formatName, inlinedFormats, code);
|
|
863
|
+
}
|
|
864
|
+
else if (typeof validator === "object" && "validate" in validator) {
|
|
865
|
+
this.resolve$DataFormat(validator.validate, formatName, inlinedFormats, code);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
code.push("\n");
|
|
870
|
+
}
|
|
871
|
+
resolve$DataFormat(validator, formatName, inlinedFormats, code) {
|
|
872
|
+
if (validator instanceof RegExp) {
|
|
873
|
+
const safeName = this.getSafeFormatName(formatName);
|
|
874
|
+
code.push(`const ${safeName} = new RegExp(${JSON.stringify(validator.source)}, '${validator.flags}');\n`);
|
|
875
|
+
inlinedFormats.add(formatName);
|
|
876
|
+
}
|
|
877
|
+
else if (typeof validator === "function") {
|
|
878
|
+
this.inlineFormatWithDeps(code, formatName, inlinedFormats);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
createFormatObject(code, config, inlinedFormats) {
|
|
882
|
+
code.push("// Format object for $data access\n");
|
|
883
|
+
code.push("const formatValidators = {\n");
|
|
884
|
+
const formatsToMap = [];
|
|
885
|
+
if (config.formats && config.formats.length > 0) {
|
|
886
|
+
formatsToMap.push(...config.formats);
|
|
887
|
+
}
|
|
888
|
+
else if (config.formatMode === "fast" || config.formatMode === "full") {
|
|
889
|
+
const validators = config.formatMode === "fast"
|
|
890
|
+
? formats_1.FAST_FORMAT_VALIDATORS
|
|
891
|
+
: formats_1.FULL_FORMAT_VALIDATORS;
|
|
892
|
+
formatsToMap.push(...Object.keys(validators));
|
|
893
|
+
}
|
|
894
|
+
for (const formatName of formatsToMap) {
|
|
895
|
+
if (inlinedFormats.has(formatName)) {
|
|
896
|
+
const safeName = this.getSafeFormatName(formatName);
|
|
897
|
+
code.push(` "${formatName}": ${safeName},\n`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
code.push("};\n\n");
|
|
901
|
+
}
|
|
902
|
+
getSafeFormatName(formatName) {
|
|
903
|
+
return "format_" + formatName.replace(/[^a-zA-Z0-9]/g, "_");
|
|
904
|
+
}
|
|
905
|
+
formatNeedsExternalDeps(formatName) {
|
|
906
|
+
const formatsWithDeps = new Set(["date-time", "iso-date-time", "time"]);
|
|
907
|
+
return formatsWithDeps.has(formatName);
|
|
908
|
+
}
|
|
909
|
+
inlineFormatWithDeps(code, formatName, inlinedFormats) {
|
|
910
|
+
const validator = this.formatValidators[formatName];
|
|
911
|
+
if (!validator || typeof validator !== "function")
|
|
912
|
+
return;
|
|
913
|
+
if (inlinedFormats.has(formatName))
|
|
914
|
+
return;
|
|
915
|
+
switch (formatName) {
|
|
916
|
+
case "date-time":
|
|
917
|
+
code.push(`// date-time format with dependencies\n`);
|
|
918
|
+
if (!inlinedFormats.has("date")) {
|
|
919
|
+
code.push(this.serializeFormatFunction("date"));
|
|
920
|
+
inlinedFormats.add("date");
|
|
921
|
+
}
|
|
922
|
+
if (!inlinedFormats.has("time")) {
|
|
923
|
+
code.push(this.serializeFormatFunction("time"));
|
|
924
|
+
inlinedFormats.add("time");
|
|
925
|
+
}
|
|
926
|
+
code.push(`${validator.toString()};\n`);
|
|
927
|
+
inlinedFormats.add(formatName);
|
|
928
|
+
break;
|
|
929
|
+
case "iso-date-time":
|
|
930
|
+
code.push(`// iso-date-time format with dependencies\n`);
|
|
931
|
+
if (!inlinedFormats.has("date")) {
|
|
932
|
+
code.push(this.serializeFormatFunction("date"));
|
|
933
|
+
inlinedFormats.add("date");
|
|
934
|
+
}
|
|
935
|
+
if (!inlinedFormats.has("iso-time")) {
|
|
936
|
+
code.push(this.serializeFormatFunction("iso-time"));
|
|
937
|
+
inlinedFormats.add("iso-time");
|
|
938
|
+
}
|
|
939
|
+
code.push(`${validator.toString()};\n`);
|
|
940
|
+
inlinedFormats.add(formatName);
|
|
941
|
+
break;
|
|
942
|
+
case "time":
|
|
943
|
+
code.push(`// time format with dependencies\n`);
|
|
944
|
+
code.push(`${validator.toString()};\n`);
|
|
945
|
+
inlinedFormats.add(formatName);
|
|
946
|
+
break;
|
|
947
|
+
default:
|
|
948
|
+
code.push(`${validator.toString()};\n`);
|
|
949
|
+
inlinedFormats.add(formatName);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
serializeFormatFunction(formatName) {
|
|
953
|
+
const validator = this.formatValidators[formatName];
|
|
954
|
+
if (!validator)
|
|
955
|
+
return "";
|
|
956
|
+
if (validator instanceof RegExp) {
|
|
957
|
+
const safeName = this.getSafeFormatName(formatName);
|
|
958
|
+
return `const ${safeName} = new RegExp(${JSON.stringify(validator.source)}, '${validator.flags}');\n`;
|
|
959
|
+
}
|
|
960
|
+
if (typeof validator === "function") {
|
|
961
|
+
return `${validator.toString()};\n`;
|
|
962
|
+
}
|
|
963
|
+
return "";
|
|
964
|
+
}
|
|
965
|
+
isSelfContained(fnString) {
|
|
966
|
+
const externalPatterns = [/\bimport\s+/, /\brequire\(/, /\bfetch\(/];
|
|
967
|
+
return !externalPatterns.some((pattern) => pattern.test(fnString));
|
|
968
|
+
}
|
|
969
|
+
generateFormatSetup(formatImports) {
|
|
970
|
+
const lines = [
|
|
971
|
+
"// Format validators that need to be provided",
|
|
972
|
+
"// Import these and pass them to the validator\n",
|
|
973
|
+
"const formatValidators = {",
|
|
974
|
+
];
|
|
975
|
+
for (const format of formatImports) {
|
|
976
|
+
lines.push(` ${format}: /* import your ${format} validator */,`);
|
|
977
|
+
}
|
|
978
|
+
lines.push("};\n");
|
|
979
|
+
return lines.join("\n");
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
exports.JetValidator = JetValidator;
|
|
983
|
+
//# sourceMappingURL=jet-validator.js.map
|