@amritk/generate-validators 0.4.2 → 0.5.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/dist/generators/build-schema.d.ts +4 -2
- package/dist/generators/build-schema.d.ts.map +1 -0
- package/dist/generators/build-schema.js +61 -0
- package/dist/generators/collect-validator-imports.d.ts +1 -0
- package/dist/generators/collect-validator-imports.d.ts.map +1 -0
- package/dist/generators/collect-validator-imports.js +99 -0
- package/dist/generators/generate-files.d.ts +1 -0
- package/dist/generators/generate-files.d.ts.map +1 -0
- package/dist/generators/generate-files.js +47 -0
- package/dist/generators/generate-validator-function.d.ts +1 -0
- package/dist/generators/generate-validator-function.d.ts.map +1 -0
- package/dist/generators/generate-validator-function.js +405 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1468
- package/package.json +6 -6
- package/src/generators/build-schema.ts +15 -80
package/dist/index.js
CHANGED
|
@@ -1,1468 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var isSchemaObject = (schema) => {
|
|
3
|
-
return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
|
|
4
|
-
};
|
|
5
|
-
var buildDynamicRefMap = (rootSchema) => {
|
|
6
|
-
const map = {};
|
|
7
|
-
if (!isSchemaObject(rootSchema) || !("$defs" in rootSchema)) {
|
|
8
|
-
return map;
|
|
9
|
-
}
|
|
10
|
-
const defs = rootSchema.$defs;
|
|
11
|
-
for (const [key, value] of Object.entries(defs)) {
|
|
12
|
-
if (typeof value === "object" && value !== null && "$dynamicAnchor" in value && typeof value["$dynamicAnchor"] === "string") {
|
|
13
|
-
const anchor = value["$dynamicAnchor"];
|
|
14
|
-
map[`#${anchor}`] = `#/$defs/${key}`;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return map;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// ../helpers/dist/extract-refs.js
|
|
21
|
-
var isResolvableRef = (ref) => {
|
|
22
|
-
if (ref === "#")
|
|
23
|
-
return false;
|
|
24
|
-
if (ref.startsWith("#"))
|
|
25
|
-
return true;
|
|
26
|
-
if (ref.startsWith("http://") || ref.startsWith("https://"))
|
|
27
|
-
return true;
|
|
28
|
-
return false;
|
|
29
|
-
};
|
|
30
|
-
var extractRefs = (schema) => {
|
|
31
|
-
const refs = new Set;
|
|
32
|
-
const traverse = (obj) => {
|
|
33
|
-
if (typeof obj !== "object" || obj === null) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (Array.isArray(obj)) {
|
|
37
|
-
for (const item of obj) {
|
|
38
|
-
traverse(item);
|
|
39
|
-
}
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const record = obj;
|
|
43
|
-
if ("$ref" in record && typeof record["$ref"] === "string" && isResolvableRef(record["$ref"])) {
|
|
44
|
-
refs.add(record["$ref"]);
|
|
45
|
-
}
|
|
46
|
-
for (const key in record) {
|
|
47
|
-
traverse(record[key]);
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
traverse(schema);
|
|
51
|
-
return refs;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// ../helpers/dist/ref-to-filename.js
|
|
55
|
-
var toKebabCase = (value) => value.replace(/OAuth/g, "Oauth").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
56
|
-
var uriRefToFilename = (uri) => {
|
|
57
|
-
const hashIndex = uri.indexOf("#");
|
|
58
|
-
const baseUri = hashIndex === -1 ? uri : uri.slice(0, hashIndex);
|
|
59
|
-
const fragment = hashIndex === -1 ? "" : uri.slice(hashIndex + 1);
|
|
60
|
-
const withoutProtocol = baseUri.replace(/^https?:\/\/[^/]+\//, "");
|
|
61
|
-
const withoutExt = withoutProtocol.replace(/\.json$/, "");
|
|
62
|
-
const rawSegments = withoutExt.split("/");
|
|
63
|
-
const SKIP_KEYS = new Set(["definitions", "$defs"]);
|
|
64
|
-
const segments = [];
|
|
65
|
-
for (let i = 0;i < rawSegments.length; i++) {
|
|
66
|
-
const s = rawSegments[i];
|
|
67
|
-
if (SKIP_KEYS.has(s))
|
|
68
|
-
continue;
|
|
69
|
-
const prevRaw = rawSegments[i - 1];
|
|
70
|
-
if (/^\d+\.\d+/.test(s) && prevRaw !== undefined && SKIP_KEYS.has(prevRaw))
|
|
71
|
-
continue;
|
|
72
|
-
segments.push(s);
|
|
73
|
-
}
|
|
74
|
-
const baseName = segments.map((s) => toKebabCase(s).replace(/\./g, "-")).join("-");
|
|
75
|
-
if (!fragment)
|
|
76
|
-
return baseName;
|
|
77
|
-
const fragSegments = fragment.split("/").filter((s) => s && !SKIP_KEYS.has(s) && s !== "properties");
|
|
78
|
-
const fragLast = fragSegments[fragSegments.length - 1];
|
|
79
|
-
if (!fragLast)
|
|
80
|
-
return baseName;
|
|
81
|
-
return `${baseName}-${toKebabCase(fragLast)}`;
|
|
82
|
-
};
|
|
83
|
-
var refToFilename = (ref) => {
|
|
84
|
-
if (ref.startsWith("http://") || ref.startsWith("https://")) {
|
|
85
|
-
return uriRefToFilename(ref);
|
|
86
|
-
}
|
|
87
|
-
const segments = ref.split("/");
|
|
88
|
-
let filename = segments[segments.length - 1];
|
|
89
|
-
if (/[A-Z]/.test(filename)) {
|
|
90
|
-
filename = toKebabCase(filename);
|
|
91
|
-
}
|
|
92
|
-
return filename;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// ../helpers/dist/ref-to-name.js
|
|
96
|
-
var toKebabCase2 = (value) => value.replace(/OAuth/g, "Oauth").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
97
|
-
var uriRefToFilename2 = (uri) => {
|
|
98
|
-
const hashIndex = uri.indexOf("#");
|
|
99
|
-
const baseUri = hashIndex === -1 ? uri : uri.slice(0, hashIndex);
|
|
100
|
-
const fragment = hashIndex === -1 ? "" : uri.slice(hashIndex + 1);
|
|
101
|
-
const withoutProtocol = baseUri.replace(/^https?:\/\/[^/]+\//, "");
|
|
102
|
-
const withoutExt = withoutProtocol.replace(/\.json$/, "");
|
|
103
|
-
const rawSegments = withoutExt.split("/");
|
|
104
|
-
const SKIP_KEYS = new Set(["definitions", "$defs"]);
|
|
105
|
-
const segments = [];
|
|
106
|
-
for (let i = 0;i < rawSegments.length; i++) {
|
|
107
|
-
const s = rawSegments[i];
|
|
108
|
-
if (SKIP_KEYS.has(s))
|
|
109
|
-
continue;
|
|
110
|
-
const prevRaw = rawSegments[i - 1];
|
|
111
|
-
if (/^\d+\.\d+/.test(s) && prevRaw !== undefined && SKIP_KEYS.has(prevRaw))
|
|
112
|
-
continue;
|
|
113
|
-
segments.push(s);
|
|
114
|
-
}
|
|
115
|
-
const baseName = segments.map((s) => toKebabCase2(s).replace(/\./g, "-")).join("-");
|
|
116
|
-
if (!fragment)
|
|
117
|
-
return baseName;
|
|
118
|
-
const fragSegments = fragment.split("/").filter((s) => s && !SKIP_KEYS.has(s) && s !== "properties");
|
|
119
|
-
const fragLast = fragSegments[fragSegments.length - 1];
|
|
120
|
-
if (!fragLast)
|
|
121
|
-
return baseName;
|
|
122
|
-
return `${baseName}-${toKebabCase2(fragLast)}`;
|
|
123
|
-
};
|
|
124
|
-
var refToFilename2 = (ref) => {
|
|
125
|
-
if (ref.startsWith("http://") || ref.startsWith("https://")) {
|
|
126
|
-
return uriRefToFilename2(ref);
|
|
127
|
-
}
|
|
128
|
-
const segments = ref.split("/");
|
|
129
|
-
let filename = segments[segments.length - 1];
|
|
130
|
-
if (/[A-Z]/.test(filename)) {
|
|
131
|
-
filename = toKebabCase2(filename);
|
|
132
|
-
}
|
|
133
|
-
return filename;
|
|
134
|
-
};
|
|
135
|
-
var kebabToPascal = (kebab, suffix) => {
|
|
136
|
-
const words = kebab.split("-");
|
|
137
|
-
let pascalCase = "";
|
|
138
|
-
for (const word of words) {
|
|
139
|
-
pascalCase += word.charAt(0).toUpperCase() + word.slice(1);
|
|
140
|
-
}
|
|
141
|
-
return pascalCase + suffix;
|
|
142
|
-
};
|
|
143
|
-
var refToName = (ref, suffix = "") => kebabToPascal(refToFilename2(ref), suffix);
|
|
144
|
-
|
|
145
|
-
// ../helpers/dist/resolve-dynamic-refs.js
|
|
146
|
-
var resolveDynamicRefs = (schema, dynamicRefMap) => {
|
|
147
|
-
if (typeof schema !== "object" || schema === null) {
|
|
148
|
-
return schema;
|
|
149
|
-
}
|
|
150
|
-
if (Object.keys(dynamicRefMap).length === 0) {
|
|
151
|
-
return schema;
|
|
152
|
-
}
|
|
153
|
-
const clone = JSON.parse(JSON.stringify(schema));
|
|
154
|
-
const walk = (obj) => {
|
|
155
|
-
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const record = obj;
|
|
159
|
-
if ("$dynamicRef" in record && typeof record["$dynamicRef"] === "string") {
|
|
160
|
-
const resolved = dynamicRefMap[record["$dynamicRef"]];
|
|
161
|
-
if (resolved) {
|
|
162
|
-
record["$ref"] = resolved;
|
|
163
|
-
delete record["$dynamicRef"];
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
for (const key in record) {
|
|
167
|
-
walk(record[key]);
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
walk(clone);
|
|
171
|
-
return clone;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// ../helpers/dist/resolve-ref.js
|
|
175
|
-
var navigatePointer = (pointer, schema) => {
|
|
176
|
-
const parts = pointer.split("/").filter(Boolean);
|
|
177
|
-
let current = schema;
|
|
178
|
-
for (const part of parts) {
|
|
179
|
-
const decodedPart = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
180
|
-
if (current && typeof current === "object" && decodedPart in current) {
|
|
181
|
-
const next = current[decodedPart];
|
|
182
|
-
if (typeof next === "object" && next !== null) {
|
|
183
|
-
current = next;
|
|
184
|
-
} else {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return current;
|
|
192
|
-
};
|
|
193
|
-
var resolveRef = (ref, rootSchema) => {
|
|
194
|
-
if (ref.startsWith("#")) {
|
|
195
|
-
return navigatePointer(ref.slice(1), rootSchema);
|
|
196
|
-
}
|
|
197
|
-
const hashIndex = ref.indexOf("#");
|
|
198
|
-
const baseUri = hashIndex === -1 ? ref : ref.slice(0, hashIndex);
|
|
199
|
-
const rawFragment = hashIndex === -1 ? "" : ref.slice(hashIndex + 1);
|
|
200
|
-
const fragment = rawFragment === "" || rawFragment === "/" ? "" : rawFragment;
|
|
201
|
-
const defs = rootSchema["$defs"];
|
|
202
|
-
if (typeof defs !== "object" || defs === null)
|
|
203
|
-
return;
|
|
204
|
-
const defsRecord = defs;
|
|
205
|
-
const base = defsRecord[baseUri];
|
|
206
|
-
if (typeof base !== "object" || base === null)
|
|
207
|
-
return;
|
|
208
|
-
if (!fragment)
|
|
209
|
-
return base;
|
|
210
|
-
const normalizedFragment = fragment.replace(/^\/definitions\//, "/$defs/");
|
|
211
|
-
return navigatePointer(normalizedFragment, base);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// ../helpers/dist/upgrade-draft07-schema.js
|
|
215
|
-
var toKebabCase3 = (value) => value.replace(/OAuth/g, "Oauth").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
216
|
-
var uriRefToFilename3 = (uri) => {
|
|
217
|
-
const hashIndex = uri.indexOf("#");
|
|
218
|
-
const baseUri = hashIndex === -1 ? uri : uri.slice(0, hashIndex);
|
|
219
|
-
const fragment = hashIndex === -1 ? "" : uri.slice(hashIndex + 1);
|
|
220
|
-
const withoutProtocol = baseUri.replace(/^https?:\/\/[^/]+\//, "");
|
|
221
|
-
const withoutExt = withoutProtocol.replace(/\.json$/, "");
|
|
222
|
-
const rawSegments = withoutExt.split("/");
|
|
223
|
-
const SKIP_KEYS = new Set(["definitions", "$defs"]);
|
|
224
|
-
const segments = [];
|
|
225
|
-
for (let i = 0;i < rawSegments.length; i++) {
|
|
226
|
-
const s = rawSegments[i];
|
|
227
|
-
if (SKIP_KEYS.has(s))
|
|
228
|
-
continue;
|
|
229
|
-
const prevRaw = rawSegments[i - 1];
|
|
230
|
-
if (/^\d+\.\d+/.test(s) && prevRaw !== undefined && SKIP_KEYS.has(prevRaw))
|
|
231
|
-
continue;
|
|
232
|
-
segments.push(s);
|
|
233
|
-
}
|
|
234
|
-
const baseName = segments.map((s) => toKebabCase3(s).replace(/\./g, "-")).join("-");
|
|
235
|
-
if (!fragment)
|
|
236
|
-
return baseName;
|
|
237
|
-
const fragSegments = fragment.split("/").filter((s) => s && !SKIP_KEYS.has(s) && s !== "properties");
|
|
238
|
-
const fragLast = fragSegments[fragSegments.length - 1];
|
|
239
|
-
if (!fragLast)
|
|
240
|
-
return baseName;
|
|
241
|
-
return `${baseName}-${toKebabCase3(fragLast)}`;
|
|
242
|
-
};
|
|
243
|
-
var refToFilename3 = (ref) => {
|
|
244
|
-
if (ref.startsWith("http://") || ref.startsWith("https://")) {
|
|
245
|
-
return uriRefToFilename3(ref);
|
|
246
|
-
}
|
|
247
|
-
const segments = ref.split("/");
|
|
248
|
-
let filename = segments[segments.length - 1];
|
|
249
|
-
if (/[A-Z]/.test(filename)) {
|
|
250
|
-
filename = toKebabCase3(filename);
|
|
251
|
-
}
|
|
252
|
-
return filename;
|
|
253
|
-
};
|
|
254
|
-
var isDraft07Schema = (schema) => typeof schema["$schema"] === "string" && schema["$schema"].includes("draft-07");
|
|
255
|
-
var rewriteRefs = (obj, refMap, selfRef) => {
|
|
256
|
-
if (typeof obj !== "object" || obj === null)
|
|
257
|
-
return obj;
|
|
258
|
-
if (Array.isArray(obj))
|
|
259
|
-
return obj.map((item) => rewriteRefs(item, refMap, selfRef));
|
|
260
|
-
const record = obj;
|
|
261
|
-
const result = {};
|
|
262
|
-
for (const [key, value] of Object.entries(record)) {
|
|
263
|
-
if (key === "$ref" && typeof value === "string") {
|
|
264
|
-
if (refMap.has(value)) {
|
|
265
|
-
result[key] = refMap.get(value);
|
|
266
|
-
} else if (value === "#" && selfRef) {
|
|
267
|
-
result[key] = selfRef;
|
|
268
|
-
} else {
|
|
269
|
-
result[key] = value;
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
result[key] = rewriteRefs(value, refMap, selfRef);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return result;
|
|
276
|
-
};
|
|
277
|
-
var hoistNestedDefs = (defs) => {
|
|
278
|
-
const hoisted = {};
|
|
279
|
-
for (const [parentName, parentSchema] of Object.entries(defs)) {
|
|
280
|
-
if (typeof parentSchema !== "object" || parentSchema === null) {
|
|
281
|
-
hoisted[parentName] = parentSchema;
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const parentObj = parentSchema;
|
|
285
|
-
const nestedDefs = parentObj["$defs"];
|
|
286
|
-
if (!nestedDefs || typeof nestedDefs !== "object") {
|
|
287
|
-
hoisted[parentName] = parentSchema;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
const parentPrefix = parentName.startsWith("http://") || parentName.startsWith("https://") ? refToFilename3(parentName) : parentName;
|
|
291
|
-
const localToHoisted = new Map;
|
|
292
|
-
for (const localName of Object.keys(nestedDefs)) {
|
|
293
|
-
const hoistedName = `${parentPrefix}-${toKebabCase3(localName)}`;
|
|
294
|
-
localToHoisted.set(`#/$defs/${localName}`, `#/$defs/${hoistedName}`);
|
|
295
|
-
}
|
|
296
|
-
const selfRef = `#/$defs/${parentPrefix}`;
|
|
297
|
-
const rewrittenParent = rewriteRefs(parentObj, localToHoisted, selfRef);
|
|
298
|
-
hoisted[parentName] = rewrittenParent;
|
|
299
|
-
for (const [localName, localSchema] of Object.entries(nestedDefs)) {
|
|
300
|
-
const hoistedName = `${parentPrefix}-${toKebabCase3(localName)}`;
|
|
301
|
-
hoisted[hoistedName] = rewriteRefs(localSchema, localToHoisted, selfRef);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return hoisted;
|
|
305
|
-
};
|
|
306
|
-
var upgradeDraft07Schema = (schema) => {
|
|
307
|
-
if (!isDraft07Schema(schema))
|
|
308
|
-
return schema;
|
|
309
|
-
const { definitions, $schema: _, ...rest } = schema;
|
|
310
|
-
const rawDefs = definitions ?? {};
|
|
311
|
-
const renamedDefs = {};
|
|
312
|
-
for (const [key, value] of Object.entries(rawDefs)) {
|
|
313
|
-
renamedDefs[key] = renameNestedDefs(value);
|
|
314
|
-
}
|
|
315
|
-
const hoistedDefs = hoistNestedDefs(renamedDefs);
|
|
316
|
-
for (const key of Object.keys(hoistedDefs)) {
|
|
317
|
-
if (key.startsWith("http://") || key.startsWith("https://")) {
|
|
318
|
-
const shortName = refToFilename3(key);
|
|
319
|
-
if (shortName && !(shortName in hoistedDefs)) {
|
|
320
|
-
hoistedDefs[shortName] = hoistedDefs[key];
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return {
|
|
325
|
-
...rest,
|
|
326
|
-
$defs: hoistedDefs
|
|
327
|
-
};
|
|
328
|
-
};
|
|
329
|
-
var renameNestedDefs = (obj) => {
|
|
330
|
-
if (typeof obj !== "object" || obj === null)
|
|
331
|
-
return obj;
|
|
332
|
-
if (Array.isArray(obj))
|
|
333
|
-
return obj.map(renameNestedDefs);
|
|
334
|
-
const record = obj;
|
|
335
|
-
const result = {};
|
|
336
|
-
for (const [key, value] of Object.entries(record)) {
|
|
337
|
-
if (key === "$ref" && typeof value === "string" && value.startsWith("#/definitions/")) {
|
|
338
|
-
result[key] = value.replace("#/definitions/", "#/$defs/");
|
|
339
|
-
} else {
|
|
340
|
-
const outKey = key === "definitions" ? "$defs" : key;
|
|
341
|
-
result[outKey] = renameNestedDefs(value);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return result;
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
// ../helpers/dist/generate-type-definition.js
|
|
348
|
-
var isSchemaObject2 = (schema) => {
|
|
349
|
-
return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
|
|
350
|
-
};
|
|
351
|
-
var isObjectSchema = (schema) => {
|
|
352
|
-
return isSchemaObject2(schema) && (("type" in schema) && schema.type === "object" || ("properties" in schema));
|
|
353
|
-
};
|
|
354
|
-
var MJST_EXTENSION_KEY = "x-mjst";
|
|
355
|
-
var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
356
|
-
var SUPPORTED_PRIMITIVES = new Set(["bigint"]);
|
|
357
|
-
var SAFE_BRAND = /^[\w$ -]+$/;
|
|
358
|
-
var readExtensionString = (schema, field) => {
|
|
359
|
-
if (!isSchemaObject2(schema))
|
|
360
|
-
return;
|
|
361
|
-
const extension = schema[MJST_EXTENSION_KEY];
|
|
362
|
-
if (typeof extension !== "object" || extension === null)
|
|
363
|
-
return;
|
|
364
|
-
const value = extension[field];
|
|
365
|
-
return typeof value === "string" ? value : undefined;
|
|
366
|
-
};
|
|
367
|
-
var getMjstInstanceOf = (schema) => {
|
|
368
|
-
const instanceOf = readExtensionString(schema, "instanceOf");
|
|
369
|
-
return instanceOf !== undefined && IDENTIFIER.test(instanceOf) ? instanceOf : undefined;
|
|
370
|
-
};
|
|
371
|
-
var getMjstPrimitive = (schema) => {
|
|
372
|
-
const primitive = readExtensionString(schema, "primitive");
|
|
373
|
-
return primitive !== undefined && SUPPORTED_PRIMITIVES.has(primitive) ? primitive : undefined;
|
|
374
|
-
};
|
|
375
|
-
var getMjstBrand = (schema) => {
|
|
376
|
-
const brand = readExtensionString(schema, "brand");
|
|
377
|
-
return brand !== undefined && SAFE_BRAND.test(brand) ? brand : undefined;
|
|
378
|
-
};
|
|
379
|
-
var toKebabCase4 = (value) => value.replace(/OAuth/g, "Oauth").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
380
|
-
var uriRefToFilename4 = (uri) => {
|
|
381
|
-
const hashIndex = uri.indexOf("#");
|
|
382
|
-
const baseUri = hashIndex === -1 ? uri : uri.slice(0, hashIndex);
|
|
383
|
-
const fragment = hashIndex === -1 ? "" : uri.slice(hashIndex + 1);
|
|
384
|
-
const withoutProtocol = baseUri.replace(/^https?:\/\/[^/]+\//, "");
|
|
385
|
-
const withoutExt = withoutProtocol.replace(/\.json$/, "");
|
|
386
|
-
const rawSegments = withoutExt.split("/");
|
|
387
|
-
const SKIP_KEYS = new Set(["definitions", "$defs"]);
|
|
388
|
-
const segments = [];
|
|
389
|
-
for (let i = 0;i < rawSegments.length; i++) {
|
|
390
|
-
const s = rawSegments[i];
|
|
391
|
-
if (SKIP_KEYS.has(s))
|
|
392
|
-
continue;
|
|
393
|
-
const prevRaw = rawSegments[i - 1];
|
|
394
|
-
if (/^\d+\.\d+/.test(s) && prevRaw !== undefined && SKIP_KEYS.has(prevRaw))
|
|
395
|
-
continue;
|
|
396
|
-
segments.push(s);
|
|
397
|
-
}
|
|
398
|
-
const baseName = segments.map((s) => toKebabCase4(s).replace(/\./g, "-")).join("-");
|
|
399
|
-
if (!fragment)
|
|
400
|
-
return baseName;
|
|
401
|
-
const fragSegments = fragment.split("/").filter((s) => s && !SKIP_KEYS.has(s) && s !== "properties");
|
|
402
|
-
const fragLast = fragSegments[fragSegments.length - 1];
|
|
403
|
-
if (!fragLast)
|
|
404
|
-
return baseName;
|
|
405
|
-
return `${baseName}-${toKebabCase4(fragLast)}`;
|
|
406
|
-
};
|
|
407
|
-
var refToFilename4 = (ref) => {
|
|
408
|
-
if (ref.startsWith("http://") || ref.startsWith("https://")) {
|
|
409
|
-
return uriRefToFilename4(ref);
|
|
410
|
-
}
|
|
411
|
-
const segments = ref.split("/");
|
|
412
|
-
let filename = segments[segments.length - 1];
|
|
413
|
-
if (/[A-Z]/.test(filename)) {
|
|
414
|
-
filename = toKebabCase4(filename);
|
|
415
|
-
}
|
|
416
|
-
return filename;
|
|
417
|
-
};
|
|
418
|
-
var kebabToPascal2 = (kebab, suffix) => {
|
|
419
|
-
const words = kebab.split("-");
|
|
420
|
-
let pascalCase = "";
|
|
421
|
-
for (const word of words) {
|
|
422
|
-
pascalCase += word.charAt(0).toUpperCase() + word.slice(1);
|
|
423
|
-
}
|
|
424
|
-
return pascalCase + suffix;
|
|
425
|
-
};
|
|
426
|
-
var refToName2 = (ref, suffix = "") => kebabToPascal2(refToFilename4(ref), suffix);
|
|
427
|
-
var JS_IDENTIFIER = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
428
|
-
var safeKey = (key) => {
|
|
429
|
-
if (JS_IDENTIFIER.test(key)) {
|
|
430
|
-
return key;
|
|
431
|
-
}
|
|
432
|
-
return `'${key}'`;
|
|
433
|
-
};
|
|
434
|
-
var getConditionalObjectSchema = (schema) => {
|
|
435
|
-
if (!isSchemaObject2(schema)) {
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
if (!("if" in schema) || !("then" in schema)) {
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
const ifSchema = schema.if;
|
|
442
|
-
const thenSchema = schema.then;
|
|
443
|
-
if (!isSchemaObject2(ifSchema) || !isSchemaObject2(thenSchema)) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
const ifProperties = ifSchema.properties;
|
|
447
|
-
const thenProperties = thenSchema.properties;
|
|
448
|
-
const hasIfProperties = ifProperties && typeof ifProperties === "object";
|
|
449
|
-
const hasThenProperties = thenProperties && typeof thenProperties === "object";
|
|
450
|
-
if (!hasIfProperties && !hasThenProperties) {
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
const properties = {
|
|
454
|
-
...hasIfProperties ? ifProperties : {},
|
|
455
|
-
...hasThenProperties ? thenProperties : {}
|
|
456
|
-
};
|
|
457
|
-
const required = new Set;
|
|
458
|
-
if (Array.isArray(ifSchema.required)) {
|
|
459
|
-
for (const key of ifSchema.required) {
|
|
460
|
-
required.add(key);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
if (hasIfProperties) {
|
|
464
|
-
for (const key in ifProperties) {
|
|
465
|
-
required.add(key);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (Array.isArray(thenSchema.required)) {
|
|
469
|
-
for (const key of thenSchema.required) {
|
|
470
|
-
required.add(key);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
if (hasThenProperties) {
|
|
474
|
-
for (const key in thenProperties) {
|
|
475
|
-
required.add(key);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const thenRef = typeof thenSchema.$ref === "string" ? thenSchema.$ref : null;
|
|
479
|
-
return {
|
|
480
|
-
schema: {
|
|
481
|
-
type: "object",
|
|
482
|
-
properties,
|
|
483
|
-
...required.size > 0 ? { required: Array.from(required) } : {}
|
|
484
|
-
},
|
|
485
|
-
thenRef
|
|
486
|
-
};
|
|
487
|
-
};
|
|
488
|
-
var isObjectLikeSchema = (schema) => {
|
|
489
|
-
if (!isSchemaObject2(schema)) {
|
|
490
|
-
return false;
|
|
491
|
-
}
|
|
492
|
-
if (isObjectSchema(schema)) {
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
return "patternProperties" in schema || "additionalProperties" in schema || "if" in schema && "then" in schema;
|
|
496
|
-
};
|
|
497
|
-
var getBooleanSubSchemaType = (schema) => {
|
|
498
|
-
return schema ? "unknown" : "never";
|
|
499
|
-
};
|
|
500
|
-
var buildJsDocBlock = (title, description, commentUrl) => {
|
|
501
|
-
let block = `/**
|
|
502
|
-
`;
|
|
503
|
-
block += `* ${title}
|
|
504
|
-
`;
|
|
505
|
-
block += `*
|
|
506
|
-
`;
|
|
507
|
-
block += `* ${description}
|
|
508
|
-
`;
|
|
509
|
-
if (commentUrl?.startsWith("http")) {
|
|
510
|
-
block += `*
|
|
511
|
-
`;
|
|
512
|
-
block += `* @see {@link ${commentUrl}}
|
|
513
|
-
`;
|
|
514
|
-
}
|
|
515
|
-
block += `*/
|
|
516
|
-
`;
|
|
517
|
-
return block;
|
|
518
|
-
};
|
|
519
|
-
var getTypeScriptType = (schema, options = {}) => {
|
|
520
|
-
const base = getUnbrandedType(schema, options);
|
|
521
|
-
const brand = getMjstBrand(schema);
|
|
522
|
-
return brand ? `(${base} & { readonly __brand: '${brand}' })` : base;
|
|
523
|
-
};
|
|
524
|
-
var recordType = (keyType, valueType, options) => options.readonly ? `Readonly<Record<${keyType}, ${valueType}>>` : `Record<${keyType}, ${valueType}>`;
|
|
525
|
-
var getUnbrandedType = (schema, options = {}) => {
|
|
526
|
-
if (typeof schema === "boolean") {
|
|
527
|
-
return getBooleanSubSchemaType(schema);
|
|
528
|
-
}
|
|
529
|
-
if (typeof schema !== "object" || schema === null) {
|
|
530
|
-
return "unknown";
|
|
531
|
-
}
|
|
532
|
-
const instanceOf = getMjstInstanceOf(schema);
|
|
533
|
-
if (instanceOf) {
|
|
534
|
-
return instanceOf;
|
|
535
|
-
}
|
|
536
|
-
const primitive = getMjstPrimitive(schema);
|
|
537
|
-
if (primitive) {
|
|
538
|
-
return primitive;
|
|
539
|
-
}
|
|
540
|
-
if (schema.$ref) {
|
|
541
|
-
if (!schema.$ref.startsWith("#")) {
|
|
542
|
-
return "unknown";
|
|
543
|
-
}
|
|
544
|
-
return refToName2(schema.$ref, options.typeSuffix);
|
|
545
|
-
}
|
|
546
|
-
if (schema.$dynamicRef) {
|
|
547
|
-
if (schema.$dynamicRef === "#meta") {
|
|
548
|
-
return `Schema${options.typeSuffix ?? ""}`;
|
|
549
|
-
}
|
|
550
|
-
return refToName2(schema.$dynamicRef, options.typeSuffix);
|
|
551
|
-
}
|
|
552
|
-
if (schema.const !== undefined) {
|
|
553
|
-
return JSON.stringify(schema.const);
|
|
554
|
-
}
|
|
555
|
-
if (schema.enum && schema.enum.length > 0) {
|
|
556
|
-
if (schema.enum.length === 1) {
|
|
557
|
-
return JSON.stringify(schema.enum[0]);
|
|
558
|
-
}
|
|
559
|
-
let enumUnion = JSON.stringify(schema.enum[0]);
|
|
560
|
-
for (let i = 1;i < schema.enum.length; i++) {
|
|
561
|
-
enumUnion += " | " + JSON.stringify(schema.enum[i]);
|
|
562
|
-
}
|
|
563
|
-
return enumUnion;
|
|
564
|
-
}
|
|
565
|
-
if (schema.enum && schema.enum.length > 1) {
|
|
566
|
-
let multiEnumUnion = JSON.stringify(schema.enum[0]);
|
|
567
|
-
for (let i = 1;i < schema.enum.length; i++) {
|
|
568
|
-
multiEnumUnion += " | " + JSON.stringify(schema.enum[i]);
|
|
569
|
-
}
|
|
570
|
-
return multiEnumUnion;
|
|
571
|
-
}
|
|
572
|
-
if (schema.oneOf && Array.isArray(schema.oneOf) && schema.oneOf.length > 0) {
|
|
573
|
-
let oneOfUnion = getTypeScriptType(schema.oneOf[0], options);
|
|
574
|
-
for (let i = 1;i < schema.oneOf.length; i++) {
|
|
575
|
-
oneOfUnion += " | " + getTypeScriptType(schema.oneOf[i], options);
|
|
576
|
-
}
|
|
577
|
-
return oneOfUnion;
|
|
578
|
-
}
|
|
579
|
-
if (schema.anyOf && Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
|
|
580
|
-
let anyOfUnion = getTypeScriptType(schema.anyOf[0], options);
|
|
581
|
-
for (let i = 1;i < schema.anyOf.length; i++) {
|
|
582
|
-
anyOfUnion += " | " + getTypeScriptType(schema.anyOf[i], options);
|
|
583
|
-
}
|
|
584
|
-
return anyOfUnion;
|
|
585
|
-
}
|
|
586
|
-
if (schema.allOf && Array.isArray(schema.allOf) && schema.allOf.length > 0) {
|
|
587
|
-
let intersectionTypes = getTypeScriptType(schema.allOf[0], options);
|
|
588
|
-
for (let i = 1;i < schema.allOf.length; i++) {
|
|
589
|
-
intersectionTypes += " & " + getTypeScriptType(schema.allOf[i], options);
|
|
590
|
-
}
|
|
591
|
-
return intersectionTypes;
|
|
592
|
-
}
|
|
593
|
-
const conditionalResult = getConditionalObjectSchema(schema);
|
|
594
|
-
if (conditionalResult) {
|
|
595
|
-
const baseType = getTypeScriptType(conditionalResult.schema, options);
|
|
596
|
-
if (conditionalResult.thenRef) {
|
|
597
|
-
return `(${baseType}) & ${refToName2(conditionalResult.thenRef, options.typeSuffix)}`;
|
|
598
|
-
}
|
|
599
|
-
return baseType;
|
|
600
|
-
}
|
|
601
|
-
if (!schema.type) {
|
|
602
|
-
if (schema.additionalProperties !== undefined) {
|
|
603
|
-
if (typeof schema.additionalProperties === "boolean") {
|
|
604
|
-
return recordType("string", getBooleanSubSchemaType(schema.additionalProperties), options);
|
|
605
|
-
}
|
|
606
|
-
return recordType("string", getTypeScriptType(schema.additionalProperties, options), options);
|
|
607
|
-
}
|
|
608
|
-
if (schema.patternProperties && typeof schema.patternProperties === "object") {
|
|
609
|
-
const firstEntry = Object.entries(schema.patternProperties)[0];
|
|
610
|
-
if (firstEntry) {
|
|
611
|
-
const [pattern, value] = firstEntry;
|
|
612
|
-
if (value !== undefined) {
|
|
613
|
-
const valueType = typeof value === "boolean" ? getBooleanSubSchemaType(value) : getTypeScriptType(value, options);
|
|
614
|
-
if (pattern === "^x-") {
|
|
615
|
-
return recordType("`x-${string}`", valueType, options);
|
|
616
|
-
}
|
|
617
|
-
return recordType("string", valueType, options);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (schema.default !== undefined) {
|
|
622
|
-
if (typeof schema.default === "string") {
|
|
623
|
-
return "string";
|
|
624
|
-
}
|
|
625
|
-
if (typeof schema.default === "number") {
|
|
626
|
-
return "number";
|
|
627
|
-
}
|
|
628
|
-
if (typeof schema.default === "boolean") {
|
|
629
|
-
return "boolean";
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
return "unknown";
|
|
633
|
-
}
|
|
634
|
-
if (Array.isArray(schema.type)) {
|
|
635
|
-
const mapType = (t) => {
|
|
636
|
-
switch (t) {
|
|
637
|
-
case "string":
|
|
638
|
-
return "string";
|
|
639
|
-
case "number":
|
|
640
|
-
case "integer":
|
|
641
|
-
return "number";
|
|
642
|
-
case "boolean":
|
|
643
|
-
return "boolean";
|
|
644
|
-
case "null":
|
|
645
|
-
return "null";
|
|
646
|
-
case "array":
|
|
647
|
-
return "unknown[]";
|
|
648
|
-
case "object":
|
|
649
|
-
return "Record<string, unknown>";
|
|
650
|
-
default:
|
|
651
|
-
return "unknown";
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
let typeUnion = mapType(schema.type[0]);
|
|
655
|
-
for (let i = 1;i < schema.type.length; i++) {
|
|
656
|
-
typeUnion += " | " + mapType(schema.type[i]);
|
|
657
|
-
}
|
|
658
|
-
return typeUnion;
|
|
659
|
-
}
|
|
660
|
-
switch (schema.type) {
|
|
661
|
-
case "string":
|
|
662
|
-
return "string";
|
|
663
|
-
case "number":
|
|
664
|
-
case "integer":
|
|
665
|
-
return "number";
|
|
666
|
-
case "boolean":
|
|
667
|
-
return "boolean";
|
|
668
|
-
case "array":
|
|
669
|
-
if (schema.items) {
|
|
670
|
-
const itemType = getTypeScriptType(schema.items, options);
|
|
671
|
-
const wrappedItemType = itemType.includes(" | ") ? `(${itemType})` : itemType;
|
|
672
|
-
return options.readonly ? `readonly ${wrappedItemType}[]` : `${wrappedItemType}[]`;
|
|
673
|
-
}
|
|
674
|
-
return options.readonly ? "readonly unknown[]" : "unknown[]";
|
|
675
|
-
case "object":
|
|
676
|
-
if (schema.properties) {
|
|
677
|
-
const readonlyPrefix = options.readonly ? "readonly " : "";
|
|
678
|
-
const hasDescriptions = Object.values(schema.properties).some((p) => isSchemaObject2(p) && (typeof p.description === "string" || typeof p.$comment === "string"));
|
|
679
|
-
if (hasDescriptions) {
|
|
680
|
-
let properties2 = "";
|
|
681
|
-
let first2 = true;
|
|
682
|
-
for (const key in schema.properties) {
|
|
683
|
-
const propSchema = schema.properties[key];
|
|
684
|
-
const isRequired = schema.required?.includes(key) ?? false;
|
|
685
|
-
const optional = isRequired ? "" : "?";
|
|
686
|
-
const propType = getTypeScriptType(propSchema, options);
|
|
687
|
-
const inlineDescription = isSchemaObject2(propSchema) && typeof propSchema.description === "string" ? propSchema.description : isSchemaObject2(propSchema) && typeof propSchema.$comment === "string" ? propSchema.$comment : undefined;
|
|
688
|
-
if (!first2)
|
|
689
|
-
properties2 += `
|
|
690
|
-
`;
|
|
691
|
-
first2 = false;
|
|
692
|
-
if (inlineDescription) {
|
|
693
|
-
properties2 += " /** " + inlineDescription + ` */
|
|
694
|
-
` + readonlyPrefix + safeKey(key) + optional + ": " + propType + ";";
|
|
695
|
-
} else {
|
|
696
|
-
properties2 += " " + readonlyPrefix + safeKey(key) + optional + ": " + propType + ";";
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
return `{
|
|
700
|
-
` + properties2 + `
|
|
701
|
-
}`;
|
|
702
|
-
}
|
|
703
|
-
let properties = "";
|
|
704
|
-
let first = true;
|
|
705
|
-
for (const key in schema.properties) {
|
|
706
|
-
const propSchema = schema.properties[key];
|
|
707
|
-
const isRequired = schema.required?.includes(key) ?? false;
|
|
708
|
-
const optional = isRequired ? "" : "?";
|
|
709
|
-
const propType = getTypeScriptType(propSchema, options);
|
|
710
|
-
if (!first)
|
|
711
|
-
properties += "; ";
|
|
712
|
-
properties += readonlyPrefix + safeKey(key) + optional + ": " + propType;
|
|
713
|
-
first = false;
|
|
714
|
-
}
|
|
715
|
-
return "{ " + properties + " }";
|
|
716
|
-
}
|
|
717
|
-
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
718
|
-
const additionalPropType = getTypeScriptType(schema.additionalProperties, options);
|
|
719
|
-
return recordType("string", additionalPropType, options);
|
|
720
|
-
}
|
|
721
|
-
if (schema.patternProperties && typeof schema.patternProperties === "object") {
|
|
722
|
-
const firstEntry = Object.entries(schema.patternProperties)[0];
|
|
723
|
-
if (firstEntry) {
|
|
724
|
-
const [pattern, patternVal] = firstEntry;
|
|
725
|
-
if (patternVal) {
|
|
726
|
-
const valueType = getTypeScriptType(patternVal, options);
|
|
727
|
-
if (pattern === "^x-") {
|
|
728
|
-
return recordType("`x-${string}`", valueType, options);
|
|
729
|
-
}
|
|
730
|
-
return recordType("string", valueType, options);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
return "object";
|
|
735
|
-
default:
|
|
736
|
-
return "unknown";
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
var generateTypeDefinition = (schema, typeName, options = {}) => {
|
|
740
|
-
const readonlyPrefix = options.readonly ? "readonly " : "";
|
|
741
|
-
if (!isObjectLikeSchema(schema)) {
|
|
742
|
-
const tsType = getTypeScriptType(schema, options);
|
|
743
|
-
let result = "";
|
|
744
|
-
const topLevelComment = isSchemaObject2(schema) && typeof schema.description === "string" && schema.description || isSchemaObject2(schema) && typeof schema.$comment === "string" && schema.$comment || undefined;
|
|
745
|
-
if (topLevelComment) {
|
|
746
|
-
result += buildJsDocBlock(typeName, topLevelComment);
|
|
747
|
-
}
|
|
748
|
-
result += `export type ${typeName} = ${tsType};`;
|
|
749
|
-
return result;
|
|
750
|
-
}
|
|
751
|
-
if (isObjectLikeSchema(schema)) {
|
|
752
|
-
const conditionalResult = getConditionalObjectSchema(schema);
|
|
753
|
-
const normalizedSchema = conditionalResult?.schema ?? schema;
|
|
754
|
-
const conditionalThenRef = conditionalResult?.thenRef ?? null;
|
|
755
|
-
let jsDocTitle;
|
|
756
|
-
let jsDocDescription;
|
|
757
|
-
const topLevelComment = isSchemaObject2(schema) && typeof schema.description === "string" && schema.description || isSchemaObject2(schema) && typeof schema.$comment === "string" && schema.$comment || undefined;
|
|
758
|
-
if (topLevelComment) {
|
|
759
|
-
jsDocTitle = typeName;
|
|
760
|
-
jsDocDescription = topLevelComment;
|
|
761
|
-
}
|
|
762
|
-
const hasProperties2 = normalizedSchema.properties && Object.keys(normalizedSchema.properties).length > 0;
|
|
763
|
-
const hasAdditionalProperties2 = normalizedSchema.additionalProperties && typeof normalizedSchema.additionalProperties === "object";
|
|
764
|
-
const hasPatternProperties = normalizedSchema.patternProperties && typeof normalizedSchema.patternProperties === "object" && Object.keys(normalizedSchema.patternProperties).length > 0;
|
|
765
|
-
if (!hasProperties2 && hasPatternProperties && normalizedSchema.patternProperties) {
|
|
766
|
-
const firstEntry = Object.entries(normalizedSchema.patternProperties)[0];
|
|
767
|
-
const firstPattern = firstEntry?.[0];
|
|
768
|
-
const firstPatternProperty = firstEntry?.[1];
|
|
769
|
-
if (firstPatternProperty === undefined) {
|
|
770
|
-
return `export type ${typeName} = Record<string, unknown>;`;
|
|
771
|
-
}
|
|
772
|
-
const patternPropType = typeof firstPatternProperty === "boolean" ? getBooleanSubSchemaType(firstPatternProperty) : getTypeScriptType(firstPatternProperty, options);
|
|
773
|
-
const keyType = firstPattern === "^x-" ? "`x-${string}`" : "string";
|
|
774
|
-
let result2 = "";
|
|
775
|
-
if (jsDocTitle && jsDocDescription) {
|
|
776
|
-
result2 += buildJsDocBlock(jsDocTitle, jsDocDescription);
|
|
777
|
-
}
|
|
778
|
-
result2 += `export type ${typeName} = ${recordType(keyType, patternPropType, options)};`;
|
|
779
|
-
return result2;
|
|
780
|
-
}
|
|
781
|
-
if (!hasProperties2 && hasAdditionalProperties2 && normalizedSchema.additionalProperties) {
|
|
782
|
-
const additionalPropType = getTypeScriptType(normalizedSchema.additionalProperties, options);
|
|
783
|
-
let result2 = "";
|
|
784
|
-
if (jsDocTitle && jsDocDescription) {
|
|
785
|
-
result2 += buildJsDocBlock(jsDocTitle, jsDocDescription);
|
|
786
|
-
}
|
|
787
|
-
result2 += `export type ${typeName} = {
|
|
788
|
-
${readonlyPrefix}[key: string]: ${additionalPropType};
|
|
789
|
-
};`;
|
|
790
|
-
return result2;
|
|
791
|
-
}
|
|
792
|
-
const schemaProps = normalizedSchema.properties ?? {};
|
|
793
|
-
let properties = "";
|
|
794
|
-
let isFirstProp = true;
|
|
795
|
-
for (const key in schemaProps) {
|
|
796
|
-
const propSchema = schemaProps[key];
|
|
797
|
-
const isRequired = normalizedSchema.required?.includes(key) ?? false;
|
|
798
|
-
const optional = isRequired ? "" : "?";
|
|
799
|
-
const propType = getTypeScriptType(propSchema, options);
|
|
800
|
-
const quotedKey = readonlyPrefix + safeKey(key);
|
|
801
|
-
if (!isFirstProp)
|
|
802
|
-
properties += `
|
|
803
|
-
`;
|
|
804
|
-
isFirstProp = false;
|
|
805
|
-
const inlineDescription = isSchemaObject2(propSchema) && typeof propSchema.description === "string" ? propSchema.description : isSchemaObject2(propSchema) && typeof propSchema.$comment === "string" ? propSchema.$comment : undefined;
|
|
806
|
-
if (inlineDescription) {
|
|
807
|
-
properties += " /** " + inlineDescription + ` */
|
|
808
|
-
` + quotedKey + optional + ": " + propType + ";";
|
|
809
|
-
} else {
|
|
810
|
-
properties += " " + quotedKey + optional + ": " + propType + ";";
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
const allOfIntersections = [];
|
|
814
|
-
if (isSchemaObject2(schema) && Array.isArray(schema.allOf)) {
|
|
815
|
-
for (const entry of schema.allOf) {
|
|
816
|
-
if (isSchemaObject2(entry) && entry.$ref) {
|
|
817
|
-
allOfIntersections.push(refToName2(entry.$ref, options.typeSuffix));
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
if (isSchemaObject2(schema) && typeof schema.$ref === "string" && schema.$ref.startsWith("#")) {
|
|
822
|
-
allOfIntersections.push(refToName2(schema.$ref, options.typeSuffix));
|
|
823
|
-
}
|
|
824
|
-
let result = "";
|
|
825
|
-
if (jsDocTitle && jsDocDescription) {
|
|
826
|
-
result += buildJsDocBlock(jsDocTitle, jsDocDescription);
|
|
827
|
-
}
|
|
828
|
-
let typeBody = `{
|
|
829
|
-
` + properties + `
|
|
830
|
-
}`;
|
|
831
|
-
if (conditionalThenRef) {
|
|
832
|
-
typeBody += " & " + refToName2(conditionalThenRef, options.typeSuffix);
|
|
833
|
-
}
|
|
834
|
-
for (const intersectionType of allOfIntersections) {
|
|
835
|
-
typeBody += " & " + intersectionType;
|
|
836
|
-
}
|
|
837
|
-
result += "export type " + typeName + " = " + typeBody + ";";
|
|
838
|
-
return result;
|
|
839
|
-
}
|
|
840
|
-
return "export type " + typeName + " = unknown;";
|
|
841
|
-
};
|
|
842
|
-
|
|
843
|
-
// ../helpers/dist/schema-guards.js
|
|
844
|
-
var hasRef = (value) => {
|
|
845
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && "$ref" in value && typeof value.$ref === "string";
|
|
846
|
-
};
|
|
847
|
-
var isSchemaObject3 = (schema) => {
|
|
848
|
-
return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
|
|
849
|
-
};
|
|
850
|
-
var hasType = (schema) => {
|
|
851
|
-
return isSchemaObject3(schema) && "type" in schema && typeof schema.type === "string";
|
|
852
|
-
};
|
|
853
|
-
var isObjectSchema2 = (schema) => {
|
|
854
|
-
return isSchemaObject3(schema) && (("type" in schema) && schema.type === "object" || ("properties" in schema));
|
|
855
|
-
};
|
|
856
|
-
var hasProperties = (schema) => {
|
|
857
|
-
return isSchemaObject3(schema) && "properties" in schema && typeof schema.properties === "object" && schema.properties !== null;
|
|
858
|
-
};
|
|
859
|
-
var hasEnum = (schema) => {
|
|
860
|
-
return isSchemaObject3(schema) && "enum" in schema && Array.isArray(schema.enum);
|
|
861
|
-
};
|
|
862
|
-
var hasPattern = (schema) => {
|
|
863
|
-
return isSchemaObject3(schema) && "pattern" in schema && typeof schema.pattern === "string";
|
|
864
|
-
};
|
|
865
|
-
var hasOneOf = (schema) => {
|
|
866
|
-
return isSchemaObject3(schema) && "oneOf" in schema && Array.isArray(schema.oneOf);
|
|
867
|
-
};
|
|
868
|
-
var hasAnyOf = (schema) => {
|
|
869
|
-
return isSchemaObject3(schema) && "anyOf" in schema && Array.isArray(schema.anyOf);
|
|
870
|
-
};
|
|
871
|
-
var hasAllOf = (schema) => {
|
|
872
|
-
return isSchemaObject3(schema) && "allOf" in schema && Array.isArray(schema.allOf);
|
|
873
|
-
};
|
|
874
|
-
var hasRequired = (schema) => {
|
|
875
|
-
return isSchemaObject3(schema) && "required" in schema && Array.isArray(schema.required);
|
|
876
|
-
};
|
|
877
|
-
var hasItems = (schema) => {
|
|
878
|
-
return isSchemaObject3(schema) && "items" in schema && typeof schema.items === "object" && schema.items !== null && typeof schema.items !== "boolean";
|
|
879
|
-
};
|
|
880
|
-
var hasAdditionalProperties = (schema) => {
|
|
881
|
-
return isSchemaObject3(schema) && "additionalProperties" in schema;
|
|
882
|
-
};
|
|
883
|
-
var hasMinLength = (schema) => {
|
|
884
|
-
return isSchemaObject3(schema) && "minLength" in schema && typeof schema.minLength === "number";
|
|
885
|
-
};
|
|
886
|
-
var hasMaxLength = (schema) => {
|
|
887
|
-
return isSchemaObject3(schema) && "maxLength" in schema && typeof schema.maxLength === "number";
|
|
888
|
-
};
|
|
889
|
-
var hasMinimum = (schema) => {
|
|
890
|
-
return isSchemaObject3(schema) && "minimum" in schema && typeof schema.minimum === "number";
|
|
891
|
-
};
|
|
892
|
-
var hasMaximum = (schema) => {
|
|
893
|
-
return isSchemaObject3(schema) && "maximum" in schema && typeof schema.maximum === "number";
|
|
894
|
-
};
|
|
895
|
-
var hasExclusiveMinimum = (schema) => {
|
|
896
|
-
return isSchemaObject3(schema) && "exclusiveMinimum" in schema && typeof schema.exclusiveMinimum === "number";
|
|
897
|
-
};
|
|
898
|
-
var hasExclusiveMaximum = (schema) => {
|
|
899
|
-
return isSchemaObject3(schema) && "exclusiveMaximum" in schema && typeof schema.exclusiveMaximum === "number";
|
|
900
|
-
};
|
|
901
|
-
var hasMultipleOf = (schema) => {
|
|
902
|
-
return isSchemaObject3(schema) && "multipleOf" in schema && typeof schema.multipleOf === "number";
|
|
903
|
-
};
|
|
904
|
-
|
|
905
|
-
// src/generators/collect-validator-imports.ts
|
|
906
|
-
var buildImport = (ref, suffix) => {
|
|
907
|
-
const filename = refToFilename(ref);
|
|
908
|
-
const typeName = refToName(ref, suffix);
|
|
909
|
-
const validatorName = `validate${typeName}`;
|
|
910
|
-
return `import { type ${typeName}, ${validatorName} } from './${filename}'`;
|
|
911
|
-
};
|
|
912
|
-
var canonicalFilename = (ref) => {
|
|
913
|
-
const base = ref.endsWith("-or-reference") ? ref.replace("-or-reference", "") : ref;
|
|
914
|
-
return refToFilename(base);
|
|
915
|
-
};
|
|
916
|
-
var collectDirectRefs = (schema) => {
|
|
917
|
-
if (typeof schema === "boolean" || schema === null)
|
|
918
|
-
return [];
|
|
919
|
-
const refs = [];
|
|
920
|
-
if (hasRef(schema)) {
|
|
921
|
-
refs.push(schema.$ref);
|
|
922
|
-
return refs;
|
|
923
|
-
}
|
|
924
|
-
const propSchemas = "properties" in schema && typeof schema.properties === "object" && schema.properties !== null ? Object.values(schema.properties) : [];
|
|
925
|
-
for (const prop of propSchemas) {
|
|
926
|
-
if (hasRef(prop))
|
|
927
|
-
refs.push(prop.$ref);
|
|
928
|
-
if (hasItems(prop) && hasRef(prop.items))
|
|
929
|
-
refs.push(prop.items.$ref);
|
|
930
|
-
if (hasAdditionalProperties(prop) && hasRef(prop.additionalProperties)) {
|
|
931
|
-
refs.push(prop.additionalProperties.$ref);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
if (hasItems(schema) && hasRef(schema.items)) {
|
|
935
|
-
refs.push(schema.items.$ref);
|
|
936
|
-
}
|
|
937
|
-
if (hasAdditionalProperties(schema) && hasRef(schema.additionalProperties)) {
|
|
938
|
-
refs.push(schema.additionalProperties.$ref);
|
|
939
|
-
}
|
|
940
|
-
for (const branch of [
|
|
941
|
-
...hasOneOf(schema) ? schema.oneOf : [],
|
|
942
|
-
...hasAnyOf(schema) ? schema.anyOf : [],
|
|
943
|
-
...hasAllOf(schema) ? schema.allOf : []
|
|
944
|
-
]) {
|
|
945
|
-
if (hasRef(branch))
|
|
946
|
-
refs.push(branch.$ref);
|
|
947
|
-
}
|
|
948
|
-
return refs;
|
|
949
|
-
};
|
|
950
|
-
var collectValidatorImports = (schema, options) => {
|
|
951
|
-
const selfFilename = options?.selfRef ? refToFilename(options.selfRef) : null;
|
|
952
|
-
const rootSchema = options?.rootSchema;
|
|
953
|
-
const typeSuffix = options?.typeSuffix ?? "";
|
|
954
|
-
const refs = collectDirectRefs(schema);
|
|
955
|
-
const seen = new Set;
|
|
956
|
-
const imports = [];
|
|
957
|
-
for (const ref of refs) {
|
|
958
|
-
const filename = canonicalFilename(ref);
|
|
959
|
-
if (seen.has(filename))
|
|
960
|
-
continue;
|
|
961
|
-
if (selfFilename && filename === selfFilename)
|
|
962
|
-
continue;
|
|
963
|
-
if (rootSchema) {
|
|
964
|
-
const resolved = resolveRef(ref, rootSchema);
|
|
965
|
-
if (!resolved)
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
seen.add(filename);
|
|
969
|
-
const importRef = ref.endsWith("-or-reference") ? ref.replace("-or-reference", "") : ref;
|
|
970
|
-
imports.push(buildImport(importRef, typeSuffix));
|
|
971
|
-
}
|
|
972
|
-
return imports;
|
|
973
|
-
};
|
|
974
|
-
|
|
975
|
-
// ../helpers/dist/mjst-extension.js
|
|
976
|
-
var isSchemaObject4 = (schema) => {
|
|
977
|
-
return typeof schema === "object" && schema !== null && typeof schema !== "boolean";
|
|
978
|
-
};
|
|
979
|
-
var MJST_EXTENSION_KEY2 = "x-mjst";
|
|
980
|
-
var IDENTIFIER2 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
981
|
-
var SUPPORTED_PRIMITIVES2 = new Set(["bigint"]);
|
|
982
|
-
var readExtensionString2 = (schema, field) => {
|
|
983
|
-
if (!isSchemaObject4(schema))
|
|
984
|
-
return;
|
|
985
|
-
const extension = schema[MJST_EXTENSION_KEY2];
|
|
986
|
-
if (typeof extension !== "object" || extension === null)
|
|
987
|
-
return;
|
|
988
|
-
const value = extension[field];
|
|
989
|
-
return typeof value === "string" ? value : undefined;
|
|
990
|
-
};
|
|
991
|
-
var getMjstInstanceOf2 = (schema) => {
|
|
992
|
-
const instanceOf = readExtensionString2(schema, "instanceOf");
|
|
993
|
-
return instanceOf !== undefined && IDENTIFIER2.test(instanceOf) ? instanceOf : undefined;
|
|
994
|
-
};
|
|
995
|
-
var getMjstPrimitive2 = (schema) => {
|
|
996
|
-
const primitive = readExtensionString2(schema, "primitive");
|
|
997
|
-
return primitive !== undefined && SUPPORTED_PRIMITIVES2.has(primitive) ? primitive : undefined;
|
|
998
|
-
};
|
|
999
|
-
|
|
1000
|
-
// src/generators/generate-validator-function.ts
|
|
1001
|
-
var validatorName = (typeName) => `validate${typeName}`;
|
|
1002
|
-
var typeofString = (type) => {
|
|
1003
|
-
if (type === "integer")
|
|
1004
|
-
return "number";
|
|
1005
|
-
return type;
|
|
1006
|
-
};
|
|
1007
|
-
var wrongTypeCondition = (accessor, type) => {
|
|
1008
|
-
switch (type) {
|
|
1009
|
-
case "string":
|
|
1010
|
-
return `typeof ${accessor} !== 'string'`;
|
|
1011
|
-
case "number":
|
|
1012
|
-
case "integer":
|
|
1013
|
-
return `typeof ${accessor} !== 'number'`;
|
|
1014
|
-
case "boolean":
|
|
1015
|
-
return `typeof ${accessor} !== 'boolean'`;
|
|
1016
|
-
case "array":
|
|
1017
|
-
return `!Array.isArray(${accessor})`;
|
|
1018
|
-
case "object":
|
|
1019
|
-
return `typeof ${accessor} !== 'object' || ${accessor} === null || Array.isArray(${accessor})`;
|
|
1020
|
-
default:
|
|
1021
|
-
return "";
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
|
-
var generatePropertyChecks = (key, propSchema, isRequired, suffix) => {
|
|
1025
|
-
if (!isSchemaObject3(propSchema))
|
|
1026
|
-
return [];
|
|
1027
|
-
const raw = `obj[${JSON.stringify(key)}]`;
|
|
1028
|
-
const path = `\`\${_path}/${key}\``;
|
|
1029
|
-
const lines = [];
|
|
1030
|
-
if (hasRef(propSchema)) {
|
|
1031
|
-
const ref = propSchema.$ref;
|
|
1032
|
-
const vName = validatorName(refToName(ref, suffix));
|
|
1033
|
-
if (isRequired) {
|
|
1034
|
-
lines.push(` if (!(${JSON.stringify(key)} in obj)) {`);
|
|
1035
|
-
lines.push(` errors.push({ message: "must have required property '${key}'", path: _path })`);
|
|
1036
|
-
lines.push(` } else {`);
|
|
1037
|
-
lines.push(` const _r = ${vName}(${raw}, ${path})`);
|
|
1038
|
-
lines.push(` if (_r !== true) errors.push(..._r.errors)`);
|
|
1039
|
-
lines.push(` }`);
|
|
1040
|
-
} else {
|
|
1041
|
-
lines.push(` if (${raw} !== undefined) {`);
|
|
1042
|
-
lines.push(` const _r = ${vName}(${raw}, ${path})`);
|
|
1043
|
-
lines.push(` if (_r !== true) errors.push(..._r.errors)`);
|
|
1044
|
-
lines.push(` }`);
|
|
1045
|
-
}
|
|
1046
|
-
return lines;
|
|
1047
|
-
}
|
|
1048
|
-
const instanceOf = getMjstInstanceOf2(propSchema);
|
|
1049
|
-
if (instanceOf) {
|
|
1050
|
-
if (isRequired) {
|
|
1051
|
-
lines.push(` if (!(${JSON.stringify(key)} in obj)) {`);
|
|
1052
|
-
lines.push(` errors.push({ message: "must have required property '${key}'", path: _path })`);
|
|
1053
|
-
lines.push(` } else if (!(${raw} instanceof ${instanceOf})) {`);
|
|
1054
|
-
lines.push(` errors.push({ message: 'must be ${instanceOf}', path: ${path} })`);
|
|
1055
|
-
lines.push(` }`);
|
|
1056
|
-
} else {
|
|
1057
|
-
lines.push(` if (${raw} !== undefined && !(${raw} instanceof ${instanceOf})) {`);
|
|
1058
|
-
lines.push(` errors.push({ message: 'must be ${instanceOf}', path: ${path} })`);
|
|
1059
|
-
lines.push(` }`);
|
|
1060
|
-
}
|
|
1061
|
-
return lines;
|
|
1062
|
-
}
|
|
1063
|
-
const primitive = getMjstPrimitive2(propSchema);
|
|
1064
|
-
if (primitive) {
|
|
1065
|
-
if (isRequired) {
|
|
1066
|
-
lines.push(` if (!(${JSON.stringify(key)} in obj)) {`);
|
|
1067
|
-
lines.push(` errors.push({ message: "must have required property '${key}'", path: _path })`);
|
|
1068
|
-
lines.push(` } else if (typeof ${raw} !== "${primitive}") {`);
|
|
1069
|
-
lines.push(` errors.push({ message: 'must be ${primitive}', path: ${path} })`);
|
|
1070
|
-
lines.push(` }`);
|
|
1071
|
-
} else {
|
|
1072
|
-
lines.push(` if (${raw} !== undefined && typeof ${raw} !== "${primitive}") {`);
|
|
1073
|
-
lines.push(` errors.push({ message: 'must be ${primitive}', path: ${path} })`);
|
|
1074
|
-
lines.push(` }`);
|
|
1075
|
-
}
|
|
1076
|
-
return lines;
|
|
1077
|
-
}
|
|
1078
|
-
if (hasEnum(propSchema)) {
|
|
1079
|
-
const allowed = JSON.stringify(propSchema.enum);
|
|
1080
|
-
const label = propSchema.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
1081
|
-
if (isRequired) {
|
|
1082
|
-
lines.push(` if (!(${JSON.stringify(key)} in obj)) {`);
|
|
1083
|
-
lines.push(` errors.push({ message: "must have required property '${key}'", path: _path })`);
|
|
1084
|
-
lines.push(` } else if (!(${allowed} as unknown[]).includes(${raw})) {`);
|
|
1085
|
-
lines.push(` errors.push({ message: \`must be one of: ${label}\`, path: ${path} })`);
|
|
1086
|
-
lines.push(` }`);
|
|
1087
|
-
} else {
|
|
1088
|
-
lines.push(` if (${raw} !== undefined && !(${allowed} as unknown[]).includes(${raw})) {`);
|
|
1089
|
-
lines.push(` errors.push({ message: \`must be one of: ${label}\`, path: ${path} })`);
|
|
1090
|
-
lines.push(` }`);
|
|
1091
|
-
}
|
|
1092
|
-
return lines;
|
|
1093
|
-
}
|
|
1094
|
-
if (hasType(propSchema)) {
|
|
1095
|
-
const t = propSchema.type;
|
|
1096
|
-
const wrongType = wrongTypeCondition(raw, t);
|
|
1097
|
-
const typLabel = typeofString(t);
|
|
1098
|
-
if (isRequired) {
|
|
1099
|
-
lines.push(` if (!(${JSON.stringify(key)} in obj)) {`);
|
|
1100
|
-
lines.push(` errors.push({ message: "must have required property '${key}'", path: _path })`);
|
|
1101
|
-
if (wrongType) {
|
|
1102
|
-
lines.push(` } else if (${wrongType}) {`);
|
|
1103
|
-
lines.push(` errors.push({ message: 'must be ${typLabel}', path: ${path} })`);
|
|
1104
|
-
}
|
|
1105
|
-
lines.push(` }`);
|
|
1106
|
-
} else if (wrongType) {
|
|
1107
|
-
lines.push(` if (${raw} !== undefined && (${wrongType})) {`);
|
|
1108
|
-
lines.push(` errors.push({ message: 'must be ${typLabel}', path: ${path} })`);
|
|
1109
|
-
lines.push(` }`);
|
|
1110
|
-
}
|
|
1111
|
-
if (t === "string") {
|
|
1112
|
-
if (hasPattern(propSchema)) {
|
|
1113
|
-
lines.push(` if (typeof ${raw} === 'string' && !/${propSchema.pattern}/.test(${raw})) {`);
|
|
1114
|
-
lines.push(` errors.push({ message: 'must match pattern ${propSchema.pattern}', path: ${path} })`);
|
|
1115
|
-
lines.push(` }`);
|
|
1116
|
-
}
|
|
1117
|
-
if (hasMinLength(propSchema)) {
|
|
1118
|
-
lines.push(` if (typeof ${raw} === 'string' && ${raw}.length < ${propSchema.minLength}) {`);
|
|
1119
|
-
lines.push(` errors.push({ message: 'must have at least ${propSchema.minLength} characters', path: ${path} })`);
|
|
1120
|
-
lines.push(` }`);
|
|
1121
|
-
}
|
|
1122
|
-
if (hasMaxLength(propSchema)) {
|
|
1123
|
-
lines.push(` if (typeof ${raw} === 'string' && ${raw}.length > ${propSchema.maxLength}) {`);
|
|
1124
|
-
lines.push(` errors.push({ message: 'must have at most ${propSchema.maxLength} characters', path: ${path} })`);
|
|
1125
|
-
lines.push(` }`);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
if (t === "number" || t === "integer") {
|
|
1129
|
-
if (hasMinimum(propSchema)) {
|
|
1130
|
-
lines.push(` if (typeof ${raw} === 'number' && ${raw} < ${propSchema.minimum}) {`);
|
|
1131
|
-
lines.push(` errors.push({ message: 'must be >= ${propSchema.minimum}', path: ${path} })`);
|
|
1132
|
-
lines.push(` }`);
|
|
1133
|
-
}
|
|
1134
|
-
if (hasMaximum(propSchema)) {
|
|
1135
|
-
lines.push(` if (typeof ${raw} === 'number' && ${raw} > ${propSchema.maximum}) {`);
|
|
1136
|
-
lines.push(` errors.push({ message: 'must be <= ${propSchema.maximum}', path: ${path} })`);
|
|
1137
|
-
lines.push(` }`);
|
|
1138
|
-
}
|
|
1139
|
-
if (hasExclusiveMinimum(propSchema)) {
|
|
1140
|
-
lines.push(` if (typeof ${raw} === 'number' && ${raw} <= ${propSchema.exclusiveMinimum}) {`);
|
|
1141
|
-
lines.push(` errors.push({ message: 'must be > ${propSchema.exclusiveMinimum}', path: ${path} })`);
|
|
1142
|
-
lines.push(` }`);
|
|
1143
|
-
}
|
|
1144
|
-
if (hasExclusiveMaximum(propSchema)) {
|
|
1145
|
-
lines.push(` if (typeof ${raw} === 'number' && ${raw} >= ${propSchema.exclusiveMaximum}) {`);
|
|
1146
|
-
lines.push(` errors.push({ message: 'must be < ${propSchema.exclusiveMaximum}', path: ${path} })`);
|
|
1147
|
-
lines.push(` }`);
|
|
1148
|
-
}
|
|
1149
|
-
if (hasMultipleOf(propSchema)) {
|
|
1150
|
-
lines.push(` if (typeof ${raw} === 'number' && ${raw} % ${propSchema.multipleOf} !== 0) {`);
|
|
1151
|
-
lines.push(` errors.push({ message: 'must be a multiple of ${propSchema.multipleOf}', path: ${path} })`);
|
|
1152
|
-
lines.push(` }`);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
if (t === "array" && hasItems(propSchema)) {
|
|
1156
|
-
const itemSchema = propSchema.items;
|
|
1157
|
-
if (hasRef(itemSchema)) {
|
|
1158
|
-
const vName = validatorName(refToName(itemSchema.$ref, suffix));
|
|
1159
|
-
lines.push(` if (Array.isArray(${raw})) {`);
|
|
1160
|
-
lines.push(` for (let _i = 0; _i < ${raw}.length; _i++) {`);
|
|
1161
|
-
lines.push(` const _ir = ${vName}(${raw}[_i], \`${path.slice(1, -1)}/${key}/\${_i}\`)`);
|
|
1162
|
-
lines.push(` if (_ir !== true) errors.push(..._ir.errors)`);
|
|
1163
|
-
lines.push(` }`);
|
|
1164
|
-
lines.push(` }`);
|
|
1165
|
-
} else if (hasType(itemSchema)) {
|
|
1166
|
-
const itemType = itemSchema.type;
|
|
1167
|
-
const itemWrong = wrongTypeCondition("_item", itemType);
|
|
1168
|
-
const itemLabel = typeofString(itemType);
|
|
1169
|
-
if (itemWrong) {
|
|
1170
|
-
lines.push(` if (Array.isArray(${raw})) {`);
|
|
1171
|
-
lines.push(` for (let _i = 0; _i < ${raw}.length; _i++) {`);
|
|
1172
|
-
lines.push(` const _item = ${raw}[_i]`);
|
|
1173
|
-
lines.push(` if (${itemWrong}) errors.push({ message: 'items must be ${itemLabel}', path: \`${path.slice(1, -1)}/${key}/\${_i}\` })`);
|
|
1174
|
-
lines.push(` }`);
|
|
1175
|
-
lines.push(` }`);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
return lines;
|
|
1181
|
-
};
|
|
1182
|
-
var generateObjectValidator = (schema, typeName, suffix) => {
|
|
1183
|
-
const vName = validatorName(typeName);
|
|
1184
|
-
const required = new Set(hasRequired(schema) ? schema.required : []);
|
|
1185
|
-
const properties = hasProperties(schema) ? schema.properties : {};
|
|
1186
|
-
const propertyLines = [];
|
|
1187
|
-
for (const [key, propSchema] of Object.entries(properties)) {
|
|
1188
|
-
const checks = generatePropertyChecks(key, propSchema, required.has(key), suffix);
|
|
1189
|
-
if (checks.length > 0) {
|
|
1190
|
-
propertyLines.push(...checks);
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
if (hasAdditionalProperties(schema) && isSchemaObject3(schema.additionalProperties) && hasRef(schema.additionalProperties)) {
|
|
1194
|
-
const vRefName = validatorName(refToName(schema.additionalProperties.$ref, suffix));
|
|
1195
|
-
propertyLines.push(` for (const _key of Object.keys(obj)) {`);
|
|
1196
|
-
propertyLines.push(` if (${JSON.stringify(Object.keys(properties))}.includes(_key)) continue`);
|
|
1197
|
-
propertyLines.push(` const _r = ${vRefName}(obj[_key as keyof typeof obj], \`\${_path}/\${_key}\`)`);
|
|
1198
|
-
propertyLines.push(` if (_r !== true) errors.push(..._r.errors)`);
|
|
1199
|
-
propertyLines.push(` }`);
|
|
1200
|
-
}
|
|
1201
|
-
const body = propertyLines.length > 0 ? `
|
|
1202
|
-
` + propertyLines.join(`
|
|
1203
|
-
`) + `
|
|
1204
|
-
` : "";
|
|
1205
|
-
return [
|
|
1206
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1207
|
-
` if (typeof input !== 'object' || input === null || Array.isArray(input)) {`,
|
|
1208
|
-
` return { valid: false, errors: [{ message: 'must be object', path: _path }] }`,
|
|
1209
|
-
` }`,
|
|
1210
|
-
``,
|
|
1211
|
-
` const errors: ValidationError[] = []`,
|
|
1212
|
-
` const obj = input as Record<string, unknown>`,
|
|
1213
|
-
body,
|
|
1214
|
-
` return errors.length > 0 ? { valid: false, errors } : true`,
|
|
1215
|
-
`}`
|
|
1216
|
-
].join(`
|
|
1217
|
-
`);
|
|
1218
|
-
};
|
|
1219
|
-
var generateScalarValidator = (schema, typeName, suffix) => {
|
|
1220
|
-
const vName = validatorName(typeName);
|
|
1221
|
-
if (!isSchemaObject3(schema)) {
|
|
1222
|
-
return [`export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`, ` return true`, `}`].join(`
|
|
1223
|
-
`);
|
|
1224
|
-
}
|
|
1225
|
-
if (hasRef(schema)) {
|
|
1226
|
-
const delegateName = validatorName(refToName(schema.$ref, suffix));
|
|
1227
|
-
return [
|
|
1228
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1229
|
-
` return ${delegateName}(input, _path)`,
|
|
1230
|
-
`}`
|
|
1231
|
-
].join(`
|
|
1232
|
-
`);
|
|
1233
|
-
}
|
|
1234
|
-
const instanceOf = getMjstInstanceOf2(schema);
|
|
1235
|
-
if (instanceOf) {
|
|
1236
|
-
return [
|
|
1237
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1238
|
-
` if (!(input instanceof ${instanceOf})) {`,
|
|
1239
|
-
` return { valid: false, errors: [{ message: 'must be ${instanceOf}', path: _path }] }`,
|
|
1240
|
-
` }`,
|
|
1241
|
-
` return true`,
|
|
1242
|
-
`}`
|
|
1243
|
-
].join(`
|
|
1244
|
-
`);
|
|
1245
|
-
}
|
|
1246
|
-
const primitive = getMjstPrimitive2(schema);
|
|
1247
|
-
if (primitive) {
|
|
1248
|
-
return [
|
|
1249
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1250
|
-
` if (typeof input !== "${primitive}") {`,
|
|
1251
|
-
` return { valid: false, errors: [{ message: 'must be ${primitive}', path: _path }] }`,
|
|
1252
|
-
` }`,
|
|
1253
|
-
` return true`,
|
|
1254
|
-
`}`
|
|
1255
|
-
].join(`
|
|
1256
|
-
`);
|
|
1257
|
-
}
|
|
1258
|
-
if (hasEnum(schema)) {
|
|
1259
|
-
const allowed = JSON.stringify(schema.enum);
|
|
1260
|
-
const label = schema.enum.map((v) => JSON.stringify(v)).join(", ");
|
|
1261
|
-
return [
|
|
1262
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1263
|
-
` if (!(${allowed} as unknown[]).includes(input)) {`,
|
|
1264
|
-
` return { valid: false, errors: [{ message: \`must be one of: ${label}\`, path: _path }] }`,
|
|
1265
|
-
` }`,
|
|
1266
|
-
` return true`,
|
|
1267
|
-
`}`
|
|
1268
|
-
].join(`
|
|
1269
|
-
`);
|
|
1270
|
-
}
|
|
1271
|
-
if (hasOneOf(schema)) {
|
|
1272
|
-
const branches = schema.oneOf.map((branch, i) => {
|
|
1273
|
-
if (!hasRef(branch))
|
|
1274
|
-
return null;
|
|
1275
|
-
const bName = validatorName(refToName(branch.$ref, suffix));
|
|
1276
|
-
return ` const _r${i} = ${bName}(input, _path)
|
|
1277
|
-
if (_r${i} === true) return true`;
|
|
1278
|
-
}).filter(Boolean).join(`
|
|
1279
|
-
`);
|
|
1280
|
-
return [
|
|
1281
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1282
|
-
branches,
|
|
1283
|
-
` return { valid: false, errors: [{ message: 'must match one of the expected schemas', path: _path }] }`,
|
|
1284
|
-
`}`
|
|
1285
|
-
].join(`
|
|
1286
|
-
`);
|
|
1287
|
-
}
|
|
1288
|
-
if (hasType(schema)) {
|
|
1289
|
-
const t = schema.type;
|
|
1290
|
-
const wrongType = wrongTypeCondition("input", t);
|
|
1291
|
-
const typLabel = typeofString(t);
|
|
1292
|
-
const constraintLines = [];
|
|
1293
|
-
if (t === "string") {
|
|
1294
|
-
if (hasPattern(schema)) {
|
|
1295
|
-
constraintLines.push(` if (typeof input === 'string' && !/${schema.pattern}/.test(input)) {`);
|
|
1296
|
-
constraintLines.push(` errors.push({ message: 'must match pattern ${schema.pattern}', path: _path })`);
|
|
1297
|
-
constraintLines.push(` }`);
|
|
1298
|
-
}
|
|
1299
|
-
if (hasMinLength(schema)) {
|
|
1300
|
-
constraintLines.push(` if (typeof input === 'string' && input.length < ${schema.minLength}) {`);
|
|
1301
|
-
constraintLines.push(` errors.push({ message: 'must have at least ${schema.minLength} characters', path: _path })`);
|
|
1302
|
-
constraintLines.push(` }`);
|
|
1303
|
-
}
|
|
1304
|
-
if (hasMaxLength(schema)) {
|
|
1305
|
-
constraintLines.push(` if (typeof input === 'string' && input.length > ${schema.maxLength}) {`);
|
|
1306
|
-
constraintLines.push(` errors.push({ message: 'must have at most ${schema.maxLength} characters', path: _path })`);
|
|
1307
|
-
constraintLines.push(` }`);
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
if (!wrongType) {
|
|
1311
|
-
return [
|
|
1312
|
-
`export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`,
|
|
1313
|
-
` return true`,
|
|
1314
|
-
`}`
|
|
1315
|
-
].join(`
|
|
1316
|
-
`);
|
|
1317
|
-
}
|
|
1318
|
-
if (constraintLines.length === 0) {
|
|
1319
|
-
return [
|
|
1320
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1321
|
-
` if (${wrongType}) {`,
|
|
1322
|
-
` return { valid: false, errors: [{ message: 'must be ${typLabel}', path: _path }] }`,
|
|
1323
|
-
` }`,
|
|
1324
|
-
` return true`,
|
|
1325
|
-
`}`
|
|
1326
|
-
].join(`
|
|
1327
|
-
`);
|
|
1328
|
-
}
|
|
1329
|
-
return [
|
|
1330
|
-
`export const ${vName} = (input: unknown, _path = ''): ValidationResult => {`,
|
|
1331
|
-
` if (${wrongType}) {`,
|
|
1332
|
-
` return { valid: false, errors: [{ message: 'must be ${typLabel}', path: _path }] }`,
|
|
1333
|
-
` }`,
|
|
1334
|
-
` const errors: ValidationError[] = []`,
|
|
1335
|
-
constraintLines.join(`
|
|
1336
|
-
`),
|
|
1337
|
-
` return errors.length > 0 ? { valid: false, errors } : true`,
|
|
1338
|
-
`}`
|
|
1339
|
-
].join(`
|
|
1340
|
-
`);
|
|
1341
|
-
}
|
|
1342
|
-
return [`export const ${vName} = (_input: unknown, _path = ''): ValidationResult => {`, ` return true`, `}`].join(`
|
|
1343
|
-
`);
|
|
1344
|
-
};
|
|
1345
|
-
var generateValidatorFunction = (schema, typeName, suffix = "") => {
|
|
1346
|
-
if (isObjectSchema2(schema)) {
|
|
1347
|
-
return generateObjectValidator(schema, typeName, suffix);
|
|
1348
|
-
}
|
|
1349
|
-
return generateScalarValidator(schema, typeName, suffix);
|
|
1350
|
-
};
|
|
1351
|
-
|
|
1352
|
-
// src/generators/generate-files.ts
|
|
1353
|
-
var generateValidatorFile = (schema, typeName, options) => {
|
|
1354
|
-
const typeSuffix = options?.typeSuffix ?? "";
|
|
1355
|
-
const refImports = collectValidatorImports(schema, {
|
|
1356
|
-
selfRef: options?.selfRef,
|
|
1357
|
-
rootSchema: options?.rootSchema,
|
|
1358
|
-
typeSuffix
|
|
1359
|
-
});
|
|
1360
|
-
const typeDefinition = generateTypeDefinition(schema, typeName, { typeSuffix });
|
|
1361
|
-
const validatorFunction = generateValidatorFunction(schema, typeName, typeSuffix);
|
|
1362
|
-
let result = `import type { ValidationResult, ValidationError } from './validation-result'
|
|
1363
|
-
`;
|
|
1364
|
-
for (const imp of refImports) {
|
|
1365
|
-
result += imp + `
|
|
1366
|
-
`;
|
|
1367
|
-
}
|
|
1368
|
-
if (refImports.length > 0) {
|
|
1369
|
-
result += `
|
|
1370
|
-
`;
|
|
1371
|
-
} else {
|
|
1372
|
-
result += `
|
|
1373
|
-
`;
|
|
1374
|
-
}
|
|
1375
|
-
result += typeDefinition + `
|
|
1376
|
-
|
|
1377
|
-
` + validatorFunction;
|
|
1378
|
-
return result;
|
|
1379
|
-
};
|
|
1380
|
-
|
|
1381
|
-
// src/generators/build-schema.ts
|
|
1382
|
-
var VALIDATION_RESULT_CONTENT = `/**
|
|
1383
|
-
* A single validation error with a human-readable message and a JSON Pointer
|
|
1384
|
-
* path indicating where in the document the error occurred.
|
|
1385
|
-
*/
|
|
1386
|
-
export type ValidationError = {
|
|
1387
|
-
message: string
|
|
1388
|
-
path: string
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
/**
|
|
1392
|
-
* The result of a generated validator function.
|
|
1393
|
-
* Returns \`true\` when the input is valid, or an object with \`valid: false\`
|
|
1394
|
-
* and a list of errors when it is not.
|
|
1395
|
-
*/
|
|
1396
|
-
export type ValidationResult = true | { valid: false; errors: ValidationError[] }
|
|
1397
|
-
`;
|
|
1398
|
-
var buildValidatorSchema = async (rootSchema, rootTypeName, typeSuffix = "") => {
|
|
1399
|
-
rootSchema = upgradeDraft07Schema(rootSchema);
|
|
1400
|
-
const files = [];
|
|
1401
|
-
const processedRefs = new Set;
|
|
1402
|
-
const processedFilenames = new Set;
|
|
1403
|
-
const refsToProcess = [];
|
|
1404
|
-
const dynamicRefMap = buildDynamicRefMap(rootSchema);
|
|
1405
|
-
const processedRootSchema = resolveDynamicRefs(rootSchema, dynamicRefMap);
|
|
1406
|
-
const rootContent = generateValidatorFile(processedRootSchema, rootTypeName, {
|
|
1407
|
-
rootSchema,
|
|
1408
|
-
typeSuffix
|
|
1409
|
-
});
|
|
1410
|
-
const rootFilename = rootTypeName.toLowerCase();
|
|
1411
|
-
if (rootFilename !== "validation-result") {
|
|
1412
|
-
processedFilenames.add(rootFilename);
|
|
1413
|
-
files.push({ filename: `${rootFilename}.ts`, content: rootContent });
|
|
1414
|
-
}
|
|
1415
|
-
const rootRefs = extractRefs(rootSchema);
|
|
1416
|
-
refsToProcess.push(...rootRefs);
|
|
1417
|
-
while (refsToProcess.length > 0) {
|
|
1418
|
-
const ref = refsToProcess.shift();
|
|
1419
|
-
if (!ref || processedRefs.has(ref))
|
|
1420
|
-
continue;
|
|
1421
|
-
processedRefs.add(ref);
|
|
1422
|
-
const resolvedSchema = resolveRef(ref, rootSchema);
|
|
1423
|
-
if (!resolvedSchema) {
|
|
1424
|
-
console.warn(`Warning: Could not resolve ref: ${ref}`);
|
|
1425
|
-
continue;
|
|
1426
|
-
}
|
|
1427
|
-
const typeName = refToName(ref, typeSuffix);
|
|
1428
|
-
const filename = refToFilename(ref);
|
|
1429
|
-
const processedSchema = resolveDynamicRefs(resolvedSchema, dynamicRefMap);
|
|
1430
|
-
const content = generateValidatorFile(processedSchema, typeName, {
|
|
1431
|
-
selfRef: ref,
|
|
1432
|
-
rootSchema,
|
|
1433
|
-
typeSuffix
|
|
1434
|
-
});
|
|
1435
|
-
if (filename !== "validation-result" && !processedFilenames.has(filename)) {
|
|
1436
|
-
processedFilenames.add(filename);
|
|
1437
|
-
files.push({ filename: `${filename}.ts`, content });
|
|
1438
|
-
}
|
|
1439
|
-
for (const nestedRef of extractRefs(resolvedSchema)) {
|
|
1440
|
-
if (!processedRefs.has(nestedRef))
|
|
1441
|
-
refsToProcess.push(nestedRef);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
files.push({ filename: "validation-result.ts", content: VALIDATION_RESULT_CONTENT });
|
|
1445
|
-
const TYPE_EXPORT_RE = /^export type (\w+)/gm;
|
|
1446
|
-
const CONST_EXPORT_RE = /^export const (\w+)/gm;
|
|
1447
|
-
const sortedFiles = [...files].sort((a, b) => a.filename.localeCompare(b.filename));
|
|
1448
|
-
let indexContent = "";
|
|
1449
|
-
for (const file of sortedFiles) {
|
|
1450
|
-
const moduleName = file.filename.replace(/\.ts$/, "");
|
|
1451
|
-
const typeNames = [];
|
|
1452
|
-
const constNames = [];
|
|
1453
|
-
for (const match of file.content.matchAll(TYPE_EXPORT_RE))
|
|
1454
|
-
typeNames.push(match[1]);
|
|
1455
|
-
for (const match of file.content.matchAll(CONST_EXPORT_RE))
|
|
1456
|
-
constNames.push(match[1]);
|
|
1457
|
-
if (typeNames.length === 0 && constNames.length === 0)
|
|
1458
|
-
continue;
|
|
1459
|
-
const typeExports = typeNames.map((n) => `type ${n}`);
|
|
1460
|
-
indexContent += `export { ${[...typeExports, ...constNames].join(", ")} } from './${moduleName}';
|
|
1461
|
-
`;
|
|
1462
|
-
}
|
|
1463
|
-
files.push({ filename: "index.ts", content: indexContent });
|
|
1464
|
-
return files;
|
|
1465
|
-
};
|
|
1466
|
-
export {
|
|
1467
|
-
buildValidatorSchema
|
|
1468
|
-
};
|
|
1
|
+
export { buildValidatorSchema } from './generators/build-schema.js';
|