@gabrielbryk/json-schema-to-zod 2.7.3 → 2.8.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/.github/workflows/release.yml +0 -5
- package/CHANGELOG.md +17 -0
- package/dist/cjs/generators/generateBundle.js +311 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/jsonSchemaToZod.js +96 -2
- package/dist/cjs/parsers/parseArray.js +34 -15
- package/dist/cjs/parsers/parseIfThenElse.js +2 -1
- package/dist/cjs/parsers/parseNumber.js +81 -39
- package/dist/cjs/parsers/parseObject.js +32 -9
- package/dist/cjs/parsers/parseSchema.js +23 -1
- package/dist/cjs/parsers/parseString.js +294 -54
- package/dist/cjs/utils/cycles.js +113 -0
- package/dist/cjs/utils/withMessage.js +4 -5
- package/dist/esm/Types.js +2 -1
- package/dist/esm/cli.js +12 -10
- package/dist/esm/generators/generateBundle.js +311 -0
- package/dist/esm/index.js +46 -28
- package/dist/esm/jsonSchemaToZod.js +105 -7
- package/dist/esm/parsers/parseAllOf.js +8 -5
- package/dist/esm/parsers/parseAnyOf.js +10 -6
- package/dist/esm/parsers/parseArray.js +47 -24
- package/dist/esm/parsers/parseBoolean.js +5 -1
- package/dist/esm/parsers/parseConst.js +5 -1
- package/dist/esm/parsers/parseDefault.js +7 -3
- package/dist/esm/parsers/parseEnum.js +5 -1
- package/dist/esm/parsers/parseIfThenElse.js +11 -6
- package/dist/esm/parsers/parseMultipleType.js +7 -3
- package/dist/esm/parsers/parseNot.js +8 -4
- package/dist/esm/parsers/parseNull.js +5 -1
- package/dist/esm/parsers/parseNullable.js +8 -4
- package/dist/esm/parsers/parseNumber.js +88 -42
- package/dist/esm/parsers/parseObject.js +59 -33
- package/dist/esm/parsers/parseOneOf.js +10 -6
- package/dist/esm/parsers/parseSchema.js +85 -59
- package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +10 -6
- package/dist/esm/parsers/parseString.js +303 -59
- package/dist/esm/utils/anyOrUnknown.js +5 -1
- package/dist/esm/utils/cliTools.js +13 -7
- package/dist/esm/utils/cycles.js +113 -0
- package/dist/esm/utils/half.js +5 -1
- package/dist/esm/utils/jsdocs.js +8 -3
- package/dist/esm/utils/omit.js +5 -1
- package/dist/esm/utils/withMessage.js +8 -6
- package/dist/esm/zodToJsonSchema.js +4 -1
- package/dist/types/Types.d.ts +8 -0
- package/dist/types/generators/generateBundle.d.ts +57 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/parsers/parseString.d.ts +2 -2
- package/dist/types/utils/cycles.d.ts +7 -0
- package/dist/types/utils/jsdocs.d.ts +1 -1
- package/dist/types/utils/withMessage.d.ts +6 -1
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeScc = exports.detectCycles = exports.findRefDependencies = void 0;
|
|
4
|
+
const findRefDependencies = (schema, validDefNames) => {
|
|
5
|
+
const deps = new Set();
|
|
6
|
+
function traverse(obj) {
|
|
7
|
+
if (obj === null || typeof obj !== "object")
|
|
8
|
+
return;
|
|
9
|
+
if (Array.isArray(obj)) {
|
|
10
|
+
obj.forEach(traverse);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const record = obj;
|
|
14
|
+
if (typeof record["$ref"] === "string") {
|
|
15
|
+
const ref = record["$ref"];
|
|
16
|
+
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
17
|
+
if (match && validDefNames.includes(match[1])) {
|
|
18
|
+
deps.add(match[1]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const value of Object.values(record)) {
|
|
22
|
+
traverse(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
traverse(schema);
|
|
26
|
+
return deps;
|
|
27
|
+
};
|
|
28
|
+
exports.findRefDependencies = findRefDependencies;
|
|
29
|
+
const detectCycles = (defNames, deps) => {
|
|
30
|
+
const cycleNodes = new Set();
|
|
31
|
+
const visited = new Set();
|
|
32
|
+
const recursionStack = new Set();
|
|
33
|
+
function dfs(node, path) {
|
|
34
|
+
if (recursionStack.has(node)) {
|
|
35
|
+
const cycleStart = path.indexOf(node);
|
|
36
|
+
for (let i = cycleStart; i < path.length; i++) {
|
|
37
|
+
cycleNodes.add(path[i]);
|
|
38
|
+
}
|
|
39
|
+
cycleNodes.add(node);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (visited.has(node))
|
|
43
|
+
return;
|
|
44
|
+
visited.add(node);
|
|
45
|
+
recursionStack.add(node);
|
|
46
|
+
const targets = deps.get(node);
|
|
47
|
+
if (targets) {
|
|
48
|
+
for (const dep of targets) {
|
|
49
|
+
dfs(dep, [...path, node]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
recursionStack.delete(node);
|
|
53
|
+
}
|
|
54
|
+
for (const defName of defNames) {
|
|
55
|
+
if (!visited.has(defName))
|
|
56
|
+
dfs(defName, []);
|
|
57
|
+
}
|
|
58
|
+
return cycleNodes;
|
|
59
|
+
};
|
|
60
|
+
exports.detectCycles = detectCycles;
|
|
61
|
+
const computeScc = (defNames, deps) => {
|
|
62
|
+
// Tarjan's algorithm, keeps mapping for quick "is this ref in my cycle?"
|
|
63
|
+
const index = new Map();
|
|
64
|
+
const lowlink = new Map();
|
|
65
|
+
const stack = [];
|
|
66
|
+
const onStack = new Set();
|
|
67
|
+
const componentByName = new Map();
|
|
68
|
+
const cycleMembers = new Set();
|
|
69
|
+
let currentIndex = 0;
|
|
70
|
+
let componentId = 0;
|
|
71
|
+
const strongConnect = (v) => {
|
|
72
|
+
index.set(v, currentIndex);
|
|
73
|
+
lowlink.set(v, currentIndex);
|
|
74
|
+
currentIndex += 1;
|
|
75
|
+
stack.push(v);
|
|
76
|
+
onStack.add(v);
|
|
77
|
+
const neighbors = deps.get(v);
|
|
78
|
+
if (neighbors) {
|
|
79
|
+
for (const w of neighbors) {
|
|
80
|
+
if (!index.has(w)) {
|
|
81
|
+
strongConnect(w);
|
|
82
|
+
lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
83
|
+
}
|
|
84
|
+
else if (onStack.has(w)) {
|
|
85
|
+
lowlink.set(v, Math.min(lowlink.get(v), index.get(w)));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (lowlink.get(v) === index.get(v)) {
|
|
90
|
+
const component = [];
|
|
91
|
+
let w;
|
|
92
|
+
do {
|
|
93
|
+
w = stack.pop();
|
|
94
|
+
if (w === undefined)
|
|
95
|
+
break;
|
|
96
|
+
onStack.delete(w);
|
|
97
|
+
component.push(w);
|
|
98
|
+
componentByName.set(w, componentId);
|
|
99
|
+
} while (w !== v);
|
|
100
|
+
if (component.length > 1) {
|
|
101
|
+
component.forEach((name) => cycleMembers.add(name));
|
|
102
|
+
}
|
|
103
|
+
componentId += 1;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
for (const name of defNames) {
|
|
107
|
+
if (!index.has(name)) {
|
|
108
|
+
strongConnect(name);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { cycleMembers, componentByName };
|
|
112
|
+
};
|
|
113
|
+
exports.computeScc = computeScc;
|
|
@@ -7,14 +7,13 @@ function withMessage(schema, key, get) {
|
|
|
7
7
|
if (value !== undefined) {
|
|
8
8
|
const got = get({ value, json: JSON.stringify(value) });
|
|
9
9
|
if (got) {
|
|
10
|
-
const opener = got
|
|
11
|
-
const prefix = got.length === 3 ? got[1] : "";
|
|
12
|
-
const closer = got.length === 3 ? got[2] : got[1];
|
|
10
|
+
const { opener, closer, messagePrefix = "", messageCloser } = got;
|
|
13
11
|
r += opener;
|
|
14
12
|
if (schema.errorMessage?.[key] !== undefined) {
|
|
15
|
-
r +=
|
|
13
|
+
r += messagePrefix + JSON.stringify(schema.errorMessage[key]);
|
|
14
|
+
r += messageCloser ?? closer;
|
|
15
|
+
return r;
|
|
16
16
|
}
|
|
17
|
-
r;
|
|
18
17
|
r += closer;
|
|
19
18
|
}
|
|
20
19
|
}
|
package/dist/esm/Types.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const jsonSchemaToZod_js_1 = require("./jsonSchemaToZod.js");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const cliTools_js_1 = require("./utils/cliTools.js");
|
|
6
8
|
const params = {
|
|
7
9
|
input: {
|
|
8
10
|
shorthand: "i",
|
|
@@ -46,10 +48,10 @@ const params = {
|
|
|
46
48
|
},
|
|
47
49
|
};
|
|
48
50
|
async function main() {
|
|
49
|
-
const args = parseArgs(params, process.argv, true);
|
|
50
|
-
const input = args.input || (await readPipe());
|
|
51
|
-
const jsonSchema = parseOrReadJSON(input);
|
|
52
|
-
const zodSchema = jsonSchemaToZod(jsonSchema, {
|
|
51
|
+
const args = (0, cliTools_js_1.parseArgs)(params, process.argv, true);
|
|
52
|
+
const input = args.input || (await (0, cliTools_js_1.readPipe)());
|
|
53
|
+
const jsonSchema = (0, cliTools_js_1.parseOrReadJSON)(input);
|
|
54
|
+
const zodSchema = (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(jsonSchema, {
|
|
53
55
|
name: args.name,
|
|
54
56
|
depth: args.depth,
|
|
55
57
|
module: args.module || "esm",
|
|
@@ -58,8 +60,8 @@ async function main() {
|
|
|
58
60
|
withJsdocs: args.withJsdocs,
|
|
59
61
|
});
|
|
60
62
|
if (args.output) {
|
|
61
|
-
mkdirSync(dirname(args.output), { recursive: true });
|
|
62
|
-
writeFileSync(args.output, zodSchema);
|
|
63
|
+
(0, fs_1.mkdirSync)((0, path_1.dirname)(args.output), { recursive: true });
|
|
64
|
+
(0, fs_1.writeFileSync)(args.output, zodSchema);
|
|
63
65
|
}
|
|
64
66
|
else {
|
|
65
67
|
console.log(zodSchema);
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSchemaBundle = void 0;
|
|
4
|
+
const jsonSchemaToZod_js_1 = require("../jsonSchemaToZod.js");
|
|
5
|
+
const generateSchemaBundle = (schema, options = {}) => {
|
|
6
|
+
const module = options.module ?? "esm";
|
|
7
|
+
const rootName = options.splitDefs?.rootName ?? options.name ?? "RootSchema";
|
|
8
|
+
const rootTypeName = typeof options.type === "string"
|
|
9
|
+
? options.type
|
|
10
|
+
: options.splitDefs?.rootTypeName ?? (typeof options.type === "boolean" && options.type ? rootName : undefined);
|
|
11
|
+
if (!schema || typeof schema !== "object") {
|
|
12
|
+
throw new Error("generateSchemaBundle requires an object schema");
|
|
13
|
+
}
|
|
14
|
+
const defs = schema.$defs || schema.definitions || {};
|
|
15
|
+
const defNames = Object.keys(defs);
|
|
16
|
+
const defInfoMap = buildDefInfoMap(defNames, defs, options);
|
|
17
|
+
const cycles = detectCycles(defInfoMap);
|
|
18
|
+
for (const defName of cycles) {
|
|
19
|
+
const info = defInfoMap.get(defName);
|
|
20
|
+
if (info)
|
|
21
|
+
info.hasCycle = true;
|
|
22
|
+
}
|
|
23
|
+
const files = [];
|
|
24
|
+
// Generate individual $def files
|
|
25
|
+
for (const defName of defNames) {
|
|
26
|
+
const info = defInfoMap.get(defName);
|
|
27
|
+
const usedRefs = new Set();
|
|
28
|
+
const defSchemaWithDefs = {
|
|
29
|
+
...defs[defName],
|
|
30
|
+
$defs: defs,
|
|
31
|
+
};
|
|
32
|
+
const zodSchema = (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(defSchemaWithDefs, {
|
|
33
|
+
...options,
|
|
34
|
+
module,
|
|
35
|
+
name: info.schemaName,
|
|
36
|
+
type: info.typeName,
|
|
37
|
+
parserOverride: createRefHandler(defName, defInfoMap, usedRefs, defs, options),
|
|
38
|
+
});
|
|
39
|
+
const finalSchema = buildSchemaFile(zodSchema, usedRefs, defInfoMap, module);
|
|
40
|
+
const fileName = options.splitDefs?.fileName?.(defName, { isRoot: false }) ?? `${defName}.schema.ts`;
|
|
41
|
+
files.push({ fileName, contents: finalSchema });
|
|
42
|
+
}
|
|
43
|
+
// Generate root schema if requested
|
|
44
|
+
if (options.splitDefs?.includeRoot ?? true) {
|
|
45
|
+
const usedRefs = new Set();
|
|
46
|
+
const workflowSchemaWithDefs = {
|
|
47
|
+
...schema,
|
|
48
|
+
};
|
|
49
|
+
const workflowZodSchema = (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(workflowSchemaWithDefs, {
|
|
50
|
+
...options,
|
|
51
|
+
module,
|
|
52
|
+
name: rootName,
|
|
53
|
+
type: rootTypeName,
|
|
54
|
+
parserOverride: createRefHandler(null, defInfoMap, usedRefs, defs, options),
|
|
55
|
+
});
|
|
56
|
+
const finalWorkflow = buildSchemaFile(workflowZodSchema, usedRefs, defInfoMap, module);
|
|
57
|
+
const rootFile = options.splitDefs?.fileName?.("root", { isRoot: true }) ?? "workflow.schema.ts";
|
|
58
|
+
files.push({ fileName: rootFile, contents: finalWorkflow });
|
|
59
|
+
}
|
|
60
|
+
// Nested types extraction (optional)
|
|
61
|
+
const nestedTypesEnabled = options.nestedTypes?.enable;
|
|
62
|
+
if (nestedTypesEnabled) {
|
|
63
|
+
const nestedTypes = collectNestedTypes(schema, defs, defNames, rootTypeName ?? rootName);
|
|
64
|
+
if (nestedTypes.length > 0) {
|
|
65
|
+
const nestedFileName = options.nestedTypes?.fileName ?? "nested-types.ts";
|
|
66
|
+
const nestedContent = generateNestedTypesFile(nestedTypes);
|
|
67
|
+
files.push({ fileName: nestedFileName, contents: nestedContent });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { files, defNames };
|
|
71
|
+
};
|
|
72
|
+
exports.generateSchemaBundle = generateSchemaBundle;
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Internals
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
const toPascalCase = (str) => str
|
|
77
|
+
.split(/[-_]/)
|
|
78
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
79
|
+
.join("");
|
|
80
|
+
const buildDefInfoMap = (defNames, defs, options) => {
|
|
81
|
+
const map = new Map();
|
|
82
|
+
for (const defName of defNames) {
|
|
83
|
+
const dependencies = findRefDependencies(defs[defName], defNames);
|
|
84
|
+
const pascalName = toPascalCase(defName);
|
|
85
|
+
const schemaName = options.splitDefs?.schemaName?.(defName, { isRoot: false }) ?? `${pascalName}Schema`;
|
|
86
|
+
const typeName = options.splitDefs?.typeName?.(defName, { isRoot: false }) ?? pascalName;
|
|
87
|
+
map.set(defName, {
|
|
88
|
+
name: defName,
|
|
89
|
+
pascalName,
|
|
90
|
+
schemaName,
|
|
91
|
+
typeName,
|
|
92
|
+
dependencies,
|
|
93
|
+
hasCycle: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return map;
|
|
97
|
+
};
|
|
98
|
+
const createRefHandler = (currentDefName, defInfoMap, usedRefs, allDefs, options) => {
|
|
99
|
+
return (schema, refs) => {
|
|
100
|
+
if (typeof schema["$ref"] === "string") {
|
|
101
|
+
const refPath = schema["$ref"];
|
|
102
|
+
const match = refPath.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
103
|
+
if (match) {
|
|
104
|
+
const refName = match[1];
|
|
105
|
+
const refInfo = defInfoMap.get(refName);
|
|
106
|
+
if (refInfo) {
|
|
107
|
+
// Track imports when referencing other defs
|
|
108
|
+
if (refName !== currentDefName) {
|
|
109
|
+
usedRefs.add(refName);
|
|
110
|
+
}
|
|
111
|
+
const isCycle = refName === currentDefName || (refInfo.hasCycle && !!currentDefName);
|
|
112
|
+
const resolved = options.refResolution?.onRef?.({
|
|
113
|
+
ref: refPath,
|
|
114
|
+
refName,
|
|
115
|
+
currentDef: currentDefName,
|
|
116
|
+
path: refs.path,
|
|
117
|
+
isCycle,
|
|
118
|
+
});
|
|
119
|
+
if (resolved)
|
|
120
|
+
return resolved;
|
|
121
|
+
return refInfo.schemaName;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const unknown = options.refResolution?.onUnknownRef?.({ ref: refPath, currentDef: currentDefName });
|
|
125
|
+
if (unknown)
|
|
126
|
+
return unknown;
|
|
127
|
+
return options.useUnknown ? "z.unknown()" : "z.any()";
|
|
128
|
+
}
|
|
129
|
+
// Inline $defs within a schema
|
|
130
|
+
if (schema["$defs"] && typeof schema["$defs"] === "object") {
|
|
131
|
+
return (0, jsonSchemaToZod_js_1.jsonSchemaToZod)(schema, {
|
|
132
|
+
...options,
|
|
133
|
+
module: options.module ?? "esm",
|
|
134
|
+
parserOverride: createRefHandler(currentDefName, defInfoMap, usedRefs, allDefs, options),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
const buildSchemaFile = (zodCode, usedRefs, defInfoMap, module) => {
|
|
141
|
+
if (module !== "esm")
|
|
142
|
+
return zodCode;
|
|
143
|
+
const imports = [];
|
|
144
|
+
for (const refName of [...usedRefs].sort()) {
|
|
145
|
+
const refInfo = defInfoMap.get(refName);
|
|
146
|
+
if (refInfo) {
|
|
147
|
+
imports.push(`import { ${refInfo.schemaName} } from './${refName}.schema.js';`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!imports.length)
|
|
151
|
+
return zodCode;
|
|
152
|
+
return zodCode.replace('import { z } from "zod"', `import { z } from "zod"\n${imports.join("\n")}`);
|
|
153
|
+
};
|
|
154
|
+
const findRefDependencies = (schema, validDefNames) => {
|
|
155
|
+
const deps = new Set();
|
|
156
|
+
function traverse(obj) {
|
|
157
|
+
if (obj === null || typeof obj !== "object")
|
|
158
|
+
return;
|
|
159
|
+
if (Array.isArray(obj)) {
|
|
160
|
+
obj.forEach(traverse);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const record = obj;
|
|
164
|
+
if (typeof record["$ref"] === "string") {
|
|
165
|
+
const ref = record["$ref"];
|
|
166
|
+
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
167
|
+
if (match && validDefNames.includes(match[1])) {
|
|
168
|
+
deps.add(match[1]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const value of Object.values(record)) {
|
|
172
|
+
traverse(value);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
traverse(schema);
|
|
176
|
+
return deps;
|
|
177
|
+
};
|
|
178
|
+
const detectCycles = (defInfoMap) => {
|
|
179
|
+
const cycleNodes = new Set();
|
|
180
|
+
const visited = new Set();
|
|
181
|
+
const recursionStack = new Set();
|
|
182
|
+
function dfs(node, path) {
|
|
183
|
+
if (recursionStack.has(node)) {
|
|
184
|
+
const cycleStart = path.indexOf(node);
|
|
185
|
+
for (let i = cycleStart; i < path.length; i++) {
|
|
186
|
+
cycleNodes.add(path[i]);
|
|
187
|
+
}
|
|
188
|
+
cycleNodes.add(node);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (visited.has(node))
|
|
192
|
+
return false;
|
|
193
|
+
visited.add(node);
|
|
194
|
+
recursionStack.add(node);
|
|
195
|
+
const info = defInfoMap.get(node);
|
|
196
|
+
if (info) {
|
|
197
|
+
for (const dep of info.dependencies) {
|
|
198
|
+
dfs(dep, [...path, node]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
recursionStack.delete(node);
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
for (const defName of defInfoMap.keys()) {
|
|
205
|
+
if (!visited.has(defName)) {
|
|
206
|
+
dfs(defName, []);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return cycleNodes;
|
|
210
|
+
};
|
|
211
|
+
const collectNestedTypes = (rootSchema, defs, defNames, rootTypeName) => {
|
|
212
|
+
const allNestedTypes = [];
|
|
213
|
+
for (const defName of defNames) {
|
|
214
|
+
const defSchema = defs[defName];
|
|
215
|
+
const parentTypeName = toPascalCase(defName);
|
|
216
|
+
const nestedTypes = findNestedTypesInSchema(defSchema, parentTypeName, defNames);
|
|
217
|
+
for (const nested of nestedTypes) {
|
|
218
|
+
nested.file = defName;
|
|
219
|
+
nested.parentType = parentTypeName;
|
|
220
|
+
allNestedTypes.push(nested);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const workflowNestedTypes = findNestedTypesInSchema({ properties: rootSchema.properties, required: rootSchema.required }, rootTypeName, defNames);
|
|
224
|
+
for (const nested of workflowNestedTypes) {
|
|
225
|
+
nested.file = "workflow";
|
|
226
|
+
nested.parentType = rootTypeName;
|
|
227
|
+
allNestedTypes.push(nested);
|
|
228
|
+
}
|
|
229
|
+
const uniqueNestedTypes = new Map();
|
|
230
|
+
for (const nested of allNestedTypes) {
|
|
231
|
+
if (!uniqueNestedTypes.has(nested.typeName) && nested.propertyPath.length > 0) {
|
|
232
|
+
uniqueNestedTypes.set(nested.typeName, nested);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return [...uniqueNestedTypes.values()];
|
|
236
|
+
};
|
|
237
|
+
const findNestedTypesInSchema = (schema, parentTypeName, defNames, currentPath = []) => {
|
|
238
|
+
const nestedTypes = [];
|
|
239
|
+
if (schema === null || typeof schema !== "object")
|
|
240
|
+
return nestedTypes;
|
|
241
|
+
const record = schema;
|
|
242
|
+
if (record.title && typeof record.title === "string" && record.type === "object") {
|
|
243
|
+
const title = record.title;
|
|
244
|
+
if (title !== parentTypeName && !defNames.map((d) => toPascalCase(d)).includes(title)) {
|
|
245
|
+
nestedTypes.push({
|
|
246
|
+
typeName: title,
|
|
247
|
+
parentType: parentTypeName,
|
|
248
|
+
propertyPath: [...currentPath],
|
|
249
|
+
file: "",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (record.properties && typeof record.properties === "object") {
|
|
254
|
+
for (const [propName, propSchema] of Object.entries(record.properties)) {
|
|
255
|
+
nestedTypes.push(...findNestedTypesInSchema(propSchema, parentTypeName, defNames, [...currentPath, propName]));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (Array.isArray(record.allOf)) {
|
|
259
|
+
for (const item of record.allOf) {
|
|
260
|
+
nestedTypes.push(...findNestedTypesInSchema(item, parentTypeName, defNames, currentPath));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (record.items) {
|
|
264
|
+
nestedTypes.push(...findNestedTypesInSchema(record.items, parentTypeName, defNames, currentPath));
|
|
265
|
+
}
|
|
266
|
+
if (record.additionalProperties && typeof record.additionalProperties === "object") {
|
|
267
|
+
nestedTypes.push(...findNestedTypesInSchema(record.additionalProperties, parentTypeName, defNames, currentPath));
|
|
268
|
+
}
|
|
269
|
+
return nestedTypes;
|
|
270
|
+
};
|
|
271
|
+
const generateNestedTypesFile = (nestedTypes) => {
|
|
272
|
+
const lines = [
|
|
273
|
+
"/**",
|
|
274
|
+
" * Auto-generated nested type exports",
|
|
275
|
+
" * ",
|
|
276
|
+
" * These types are inline within parent schemas but commonly needed separately.",
|
|
277
|
+
" * They are extracted using TypeScript indexed access types.",
|
|
278
|
+
" */",
|
|
279
|
+
"",
|
|
280
|
+
];
|
|
281
|
+
const byParent = new Map();
|
|
282
|
+
for (const info of nestedTypes) {
|
|
283
|
+
if (!byParent.has(info.parentType)) {
|
|
284
|
+
byParent.set(info.parentType, []);
|
|
285
|
+
}
|
|
286
|
+
byParent.get(info.parentType).push(info);
|
|
287
|
+
}
|
|
288
|
+
const imports = new Set();
|
|
289
|
+
for (const info of nestedTypes) {
|
|
290
|
+
imports.add(info.file);
|
|
291
|
+
}
|
|
292
|
+
for (const file of [...imports].sort()) {
|
|
293
|
+
const typeName = file === "workflow" ? "Workflow" : toPascalCase(file);
|
|
294
|
+
lines.push(`import type { ${typeName} } from './${file}.schema.js';`);
|
|
295
|
+
}
|
|
296
|
+
lines.push("");
|
|
297
|
+
for (const [parentType, types] of [...byParent.entries()].sort()) {
|
|
298
|
+
lines.push(`// From ${parentType}`);
|
|
299
|
+
for (const info of types.sort((a, b) => a.typeName.localeCompare(b.typeName))) {
|
|
300
|
+
if (info.propertyPath.length > 0) {
|
|
301
|
+
let accessExpr = parentType;
|
|
302
|
+
for (const prop of info.propertyPath) {
|
|
303
|
+
accessExpr = `NonNullable<${accessExpr}['${prop}']>`;
|
|
304
|
+
}
|
|
305
|
+
lines.push(`export type ${info.typeName} = ${accessExpr};`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
lines.push("");
|
|
309
|
+
}
|
|
310
|
+
return lines.join("\n");
|
|
311
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -1,28 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Types.js"), exports);
|
|
18
|
+
__exportStar(require("./generators/generateBundle.js"), exports);
|
|
19
|
+
__exportStar(require("./jsonSchemaToZod.js"), exports);
|
|
20
|
+
__exportStar(require("./parsers/parseAllOf.js"), exports);
|
|
21
|
+
__exportStar(require("./parsers/parseAnyOf.js"), exports);
|
|
22
|
+
__exportStar(require("./parsers/parseArray.js"), exports);
|
|
23
|
+
__exportStar(require("./parsers/parseBoolean.js"), exports);
|
|
24
|
+
__exportStar(require("./parsers/parseConst.js"), exports);
|
|
25
|
+
__exportStar(require("./parsers/parseDefault.js"), exports);
|
|
26
|
+
__exportStar(require("./parsers/parseEnum.js"), exports);
|
|
27
|
+
__exportStar(require("./parsers/parseIfThenElse.js"), exports);
|
|
28
|
+
__exportStar(require("./parsers/parseMultipleType.js"), exports);
|
|
29
|
+
__exportStar(require("./parsers/parseNot.js"), exports);
|
|
30
|
+
__exportStar(require("./parsers/parseNull.js"), exports);
|
|
31
|
+
__exportStar(require("./parsers/parseNullable.js"), exports);
|
|
32
|
+
__exportStar(require("./parsers/parseNumber.js"), exports);
|
|
33
|
+
__exportStar(require("./parsers/parseObject.js"), exports);
|
|
34
|
+
__exportStar(require("./parsers/parseOneOf.js"), exports);
|
|
35
|
+
__exportStar(require("./parsers/parseSchema.js"), exports);
|
|
36
|
+
__exportStar(require("./parsers/parseSimpleDiscriminatedOneOf.js"), exports);
|
|
37
|
+
__exportStar(require("./parsers/parseString.js"), exports);
|
|
38
|
+
__exportStar(require("./utils/anyOrUnknown.js"), exports);
|
|
39
|
+
__exportStar(require("./utils/cycles.js"), exports);
|
|
40
|
+
__exportStar(require("./utils/half.js"), exports);
|
|
41
|
+
__exportStar(require("./utils/jsdocs.js"), exports);
|
|
42
|
+
__exportStar(require("./utils/omit.js"), exports);
|
|
43
|
+
__exportStar(require("./utils/withMessage.js"), exports);
|
|
44
|
+
__exportStar(require("./zodToJsonSchema.js"), exports);
|
|
45
|
+
const jsonSchemaToZod_js_1 = require("./jsonSchemaToZod.js");
|
|
46
|
+
exports.default = jsonSchemaToZod_js_1.jsonSchemaToZod;
|