hono-takibi 0.8.6 → 0.8.7
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/cli/index.d.ts +7 -4
- package/dist/cli/index.js +11 -5
- package/dist/cli/takibi.d.ts +7 -4
- package/dist/cli/takibi.js +11 -7
- package/dist/generator/zod-openapi-hono/openapi/components/index.js +2 -3
- package/dist/generator/zod-openapi-hono/openapi/route/index.js +0 -4
- package/dist/generator/zod-openapi-hono/openapi/route/params/params-object.js +4 -5
- package/dist/generator/zod-openapi-hono/openapi/route/params/request-parameter.js +2 -2
- package/dist/generator/zod-openapi-hono/openapi/route/response/index.js +2 -2
- package/dist/generator/zod-to-openapi/index.d.ts +2 -0
- package/dist/generator/zod-to-openapi/index.js +141 -0
- package/dist/generator/zod-to-openapi/z/array.js +17 -0
- package/dist/generator/{zod → zod-to-openapi}/z/enum.d.ts +0 -3
- package/dist/generator/zod-to-openapi/z/enum.js +41 -0
- package/dist/generator/{zod → zod-to-openapi}/z/index.d.ts +0 -2
- package/dist/generator/{zod → zod-to-openapi}/z/index.js +0 -2
- package/dist/generator/{zod → zod-to-openapi}/z/integer.js +1 -3
- package/dist/generator/{zod → zod-to-openapi}/z/number.js +1 -3
- package/dist/generator/zod-to-openapi/z/object.js +40 -0
- package/dist/generator/{zod → zod-to-openapi}/z/string.js +1 -3
- package/dist/helper/index.d.ts +1 -6
- package/dist/helper/index.js +1 -6
- package/dist/{generator/zod/helper → helper}/properties-schema.d.ts +1 -1
- package/dist/{generator/zod/helper → helper}/properties-schema.js +3 -3
- package/dist/helper/wrap.d.ts +1 -1
- package/dist/helper/wrap.js +21 -6
- package/dist/index.js +1 -1
- package/dist/openapi/index.d.ts +1 -1
- package/dist/utils/index.d.ts +4 -23
- package/dist/utils/index.js +7 -32
- package/package.json +1 -5
- package/dist/generator/zod/helper/index.d.ts +0 -2
- package/dist/generator/zod/helper/index.js +0 -2
- package/dist/generator/zod/helper/property-schema.d.ts +0 -27
- package/dist/generator/zod/helper/property-schema.js +0 -45
- package/dist/generator/zod/index.d.ts +0 -54
- package/dist/generator/zod/index.js +0 -113
- package/dist/generator/zod/z/array.js +0 -23
- package/dist/generator/zod/z/boolean.d.ts +0 -2
- package/dist/generator/zod/z/boolean.js +0 -5
- package/dist/generator/zod/z/date.d.ts +0 -2
- package/dist/generator/zod/z/date.js +0 -5
- package/dist/generator/zod/z/enum.js +0 -63
- package/dist/generator/zod/z/object.js +0 -55
- package/dist/helper/allof.d.ts +0 -2
- package/dist/helper/allof.js +0 -34
- package/dist/helper/anyof.d.ts +0 -14
- package/dist/helper/anyof.js +0 -26
- package/dist/helper/const.d.ts +0 -2
- package/dist/helper/const.js +0 -5
- package/dist/helper/normalize-types.d.ts +0 -2
- package/dist/helper/normalize-types.js +0 -3
- package/dist/helper/not.d.ts +0 -2
- package/dist/helper/not.js +0 -16
- package/dist/helper/oneof.d.ts +0 -12
- package/dist/helper/oneof.js +0 -30
- package/dist/helper/zod-to-openapi.d.ts +0 -21
- package/dist/helper/zod-to-openapi.js +0 -39
- /package/dist/generator/{zod → zod-to-openapi}/z/array.d.ts +0 -0
- /package/dist/generator/{zod → zod-to-openapi}/z/integer.d.ts +0 -0
- /package/dist/generator/{zod → zod-to-openapi}/z/number.d.ts +0 -0
- /package/dist/generator/{zod → zod-to-openapi}/z/object.d.ts +0 -0
- /package/dist/generator/{zod → zod-to-openapi}/z/string.d.ts +0 -0
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { Result } from '../result/index.js';
|
|
2
1
|
/**
|
|
3
2
|
* CLI entry point for `hono-takibi`.
|
|
4
3
|
*
|
|
5
4
|
* @returns A `Result` containing help text or CLI execution result.
|
|
6
5
|
*/
|
|
7
|
-
export declare function honoTakibi(): Promise<
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export declare function honoTakibi(): Promise<{
|
|
7
|
+
ok: true;
|
|
8
|
+
value: string;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
error: string;
|
|
12
|
+
}>;
|
package/dist/cli/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { asyncAndThen
|
|
1
|
+
import { asyncAndThen } from '../result/index.js';
|
|
2
2
|
import { parseCli } from '../utils/index.js';
|
|
3
3
|
import { takibi } from './takibi.js';
|
|
4
4
|
const HELP_TEXT = `Usage: hono-takibi <input.{yaml,json,tsp}> -o <routes.ts> [options]
|
|
@@ -22,9 +22,15 @@ export async function honoTakibi() {
|
|
|
22
22
|
return args.length === 1 && (args[0] === '--help' || args[0] === '-h');
|
|
23
23
|
};
|
|
24
24
|
if (isHelpRequested(args)) {
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
return {
|
|
26
|
+
ok: true,
|
|
27
|
+
value: HELP_TEXT,
|
|
28
|
+
};
|
|
28
29
|
}
|
|
29
|
-
return await asyncAndThen(parseCli(args), async (cli) => asyncAndThen(await takibi(cli.input, cli.output, cli.exportSchema ?? false, cli.exportType ?? false, cli.template ?? false, cli.test ?? false, cli.basePath), async (result) =>
|
|
30
|
+
return await asyncAndThen(parseCli(args), async (cli) => asyncAndThen(await takibi(cli.input, cli.output, cli.exportSchema ?? false, cli.exportType ?? false, cli.template ?? false, cli.test ?? false, cli.basePath), async (result) => {
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
value: result,
|
|
34
|
+
};
|
|
35
|
+
}));
|
|
30
36
|
}
|
package/dist/cli/takibi.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Result } from '../result/index.js';
|
|
2
1
|
/**
|
|
3
2
|
* Generates TypeScript code from an OpenAPI spec and optional templates.
|
|
4
3
|
*
|
|
@@ -11,6 +10,10 @@ import type { Result } from '../result/index.js';
|
|
|
11
10
|
* @param basePath - Optional base path for template output.
|
|
12
11
|
* @returns A `Result` containing a success message or an error string.
|
|
13
12
|
*/
|
|
14
|
-
export declare function takibi(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: `${string}.ts`, exportSchema: boolean, exportType: boolean, template: boolean, test: boolean, basePath?: string): Promise<
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export declare function takibi(input: `${string}.yaml` | `${string}.json` | `${string}.tsp`, output: `${string}.ts`, exportSchema: boolean, exportType: boolean, template: boolean, test: boolean, basePath?: string): Promise<{
|
|
14
|
+
ok: true;
|
|
15
|
+
value: string;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
}>;
|
package/dist/cli/takibi.js
CHANGED
|
@@ -3,7 +3,7 @@ import { fmt } from '../format/index.js';
|
|
|
3
3
|
import { mkdir, writeFile } from '../fsp/index.js';
|
|
4
4
|
import zodOpenAPIHono from '../generator/zod-openapi-hono/openapi/index.js';
|
|
5
5
|
import { parseOpenAPI } from '../openapi/index.js';
|
|
6
|
-
import { asyncAndThen
|
|
6
|
+
import { asyncAndThen } from '../result/index.js';
|
|
7
7
|
import { templateCode } from './template-code.js';
|
|
8
8
|
/**
|
|
9
9
|
* Generates TypeScript code from an OpenAPI spec and optional templates.
|
|
@@ -19,10 +19,14 @@ import { templateCode } from './template-code.js';
|
|
|
19
19
|
*/
|
|
20
20
|
export async function takibi(input, output, exportSchema, exportType, template, test, basePath) {
|
|
21
21
|
return await asyncAndThen(await parseOpenAPI(input), async (openAPI) => asyncAndThen(await fmt(zodOpenAPIHono(openAPI, exportSchema, exportType)), async (code) => asyncAndThen(await mkdir(path.dirname(output)), async () => asyncAndThen(await writeFile(output, code), async () => template && output.includes('/')
|
|
22
|
-
? asyncAndThen(await templateCode(openAPI, output, test, basePath), async () =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
22
|
+
? asyncAndThen(await templateCode(openAPI, output, test, basePath), async () => {
|
|
23
|
+
return {
|
|
24
|
+
ok: true,
|
|
25
|
+
value: 'Generated code and template files written',
|
|
26
|
+
};
|
|
27
|
+
})
|
|
28
|
+
: {
|
|
29
|
+
ok: true,
|
|
30
|
+
value: `Generated code written to ${output}`,
|
|
31
|
+
}))));
|
|
28
32
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { resolveSchemasDependencies } from '../../../../helper/resolve-schemas-dependencies.js';
|
|
2
|
-
import { zodToOpenAPI } from '../../../../helper/zod-to-openapi.js';
|
|
3
2
|
import { zodToOpenAPISchema } from '../../../../helper/zod-to-openapi-schema.js';
|
|
4
|
-
import
|
|
3
|
+
import { zodToOpenAPI } from '../../../zod-to-openapi/index.js';
|
|
5
4
|
/**
|
|
6
5
|
* Converts OpenAPI component schemas to Zod-based TypeScript definitions.
|
|
7
6
|
*
|
|
@@ -33,7 +32,7 @@ export function componentsCode(components, exportSchema, exportType) {
|
|
|
33
32
|
// 4.1 get schema definition corresponding to schema name
|
|
34
33
|
const schema = schemas[schemaName];
|
|
35
34
|
// 4.2 generate zod schema
|
|
36
|
-
const zodSchema = zodToOpenAPI(
|
|
35
|
+
const zodSchema = zodToOpenAPI(schema);
|
|
37
36
|
// 4.3 generate zod schema definition
|
|
38
37
|
return zodToOpenAPISchema(schemaName, zodSchema, exportSchema, exportType);
|
|
39
38
|
})
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isHttpMethod } from '../../../../utils/index.js';
|
|
2
1
|
import { route } from './route.js';
|
|
3
2
|
/**
|
|
4
3
|
* Generates TypeScript code for all valid Hono routes from OpenAPI paths.
|
|
@@ -24,9 +23,6 @@ export function routeCode(openAPIPaths) {
|
|
|
24
23
|
// 3.1. skip parameters key and undefined operations
|
|
25
24
|
if (method === 'parameters' || !pathItemValue)
|
|
26
25
|
continue;
|
|
27
|
-
// 3.2. check if the method is an HTTP method
|
|
28
|
-
if (!isHttpMethod(method))
|
|
29
|
-
throw new Error('Invalid HTTP method');
|
|
30
26
|
// 3.3 exclude the possibility of string or Parameter[]
|
|
31
27
|
if (typeof pathItemValue === 'string' || Array.isArray(pathItemValue))
|
|
32
28
|
continue;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { zodToOpenAPI } from '../../../../../helper/zod-to-openapi.js';
|
|
2
1
|
import { getToSafeIdentifier } from '../../../../../utils/index.js';
|
|
3
|
-
import
|
|
2
|
+
import { zodToOpenAPI } from '../../../../zod-to-openapi/index.js';
|
|
4
3
|
import { queryParameter } from './index.js';
|
|
5
4
|
/**
|
|
6
5
|
* Converts OpenAPI component schemas into TypeScript code using Zod.
|
|
@@ -17,12 +16,12 @@ import { queryParameter } from './index.js';
|
|
|
17
16
|
*/
|
|
18
17
|
export function paramsObject(parameters) {
|
|
19
18
|
return parameters.reduce((acc, param) => {
|
|
20
|
-
const z = zod(param.schema)
|
|
19
|
+
// const z = zod(param.schema)
|
|
21
20
|
const optionalSuffix = param.required ? '' : '.optional()';
|
|
22
21
|
// path params are generated with the param name
|
|
23
22
|
const baseSchema = param.in
|
|
24
|
-
? zodToOpenAPI(
|
|
25
|
-
: zodToOpenAPI(
|
|
23
|
+
? zodToOpenAPI(param.schema, param.name, param.in)
|
|
24
|
+
: zodToOpenAPI(param.schema, param.name);
|
|
26
25
|
// Initialize section if it doesn't exist
|
|
27
26
|
if (!acc[param.in]) {
|
|
28
27
|
acc[param.in] = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { requestParamsArray } from '../../../../../utils/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { zodToOpenAPI } from '../../../../zod-to-openapi/index.js';
|
|
3
3
|
import { requestBody } from '../request/body/index.js';
|
|
4
4
|
import { paramsObject } from './index.js';
|
|
5
5
|
/**
|
|
@@ -35,7 +35,7 @@ export function requestParameter(parameters, body) {
|
|
|
35
35
|
const uniqueSchemas = new Map();
|
|
36
36
|
for (const contentType of requestBodyContentTypes) {
|
|
37
37
|
const { schema } = body.content[contentType];
|
|
38
|
-
const z =
|
|
38
|
+
const z = zodToOpenAPI(schema);
|
|
39
39
|
uniqueSchemas.set(z, z);
|
|
40
40
|
}
|
|
41
41
|
const request_body_required = body.required ?? false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapeStringLiteral, isUniqueContentSchema } from '../../../../../utils/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { zodToOpenAPI } from '../../../../zod-to-openapi/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates a Zod-compatible response schema definition from OpenAPI responses.
|
|
5
5
|
*
|
|
@@ -29,7 +29,7 @@ export function response(responses) {
|
|
|
29
29
|
const contentParts = [];
|
|
30
30
|
for (const contentType of contentTypes) {
|
|
31
31
|
const content = response.content[contentType];
|
|
32
|
-
const zodSchema =
|
|
32
|
+
const zodSchema = zodToOpenAPI(content.schema);
|
|
33
33
|
const examples = content.examples;
|
|
34
34
|
const exampleString = examples && Object.keys(examples).length > 0
|
|
35
35
|
? `,examples:{${Object.entries(examples)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { wrap } from '../../helper/wrap.js';
|
|
2
|
+
import { normalizeTypes, refSchema } from '../../utils/index.js';
|
|
3
|
+
import { array } from './z/array.js';
|
|
4
|
+
import { _enum } from './z/enum.js';
|
|
5
|
+
import { integer } from './z/integer.js';
|
|
6
|
+
import { number } from './z/number.js';
|
|
7
|
+
import { object } from './z/object.js';
|
|
8
|
+
import { string } from './z/string.js';
|
|
9
|
+
export function zodToOpenAPI(schema, paramName, paramIn) {
|
|
10
|
+
if (schema === undefined)
|
|
11
|
+
throw new Error('hono-takibi: only #/components/schemas/* is supported');
|
|
12
|
+
// ref
|
|
13
|
+
if (schema.$ref) {
|
|
14
|
+
if (Boolean(schema.$ref) === true) {
|
|
15
|
+
return wrap(refSchema(schema.$ref), schema, paramName, paramIn);
|
|
16
|
+
}
|
|
17
|
+
if (schema.type === 'array' && Boolean(schema.items?.$ref)) {
|
|
18
|
+
if (schema.items?.$ref) {
|
|
19
|
+
const ref = wrap(refSchema(schema.items.$ref), schema.items);
|
|
20
|
+
return `z.array(${ref})`;
|
|
21
|
+
}
|
|
22
|
+
const z = 'z.array(z.any())';
|
|
23
|
+
return wrap(z, schema, paramName, paramIn);
|
|
24
|
+
}
|
|
25
|
+
const z = 'z.any()';
|
|
26
|
+
return wrap(z, schema, paramName, paramIn);
|
|
27
|
+
}
|
|
28
|
+
/* combinators */
|
|
29
|
+
// allOf
|
|
30
|
+
if (schema.allOf) {
|
|
31
|
+
if (!schema.allOf || schema.allOf.length === 0) {
|
|
32
|
+
return wrap('z.any()', schema);
|
|
33
|
+
}
|
|
34
|
+
const { schemas, nullable } = schema.allOf.reduce((acc, s) => {
|
|
35
|
+
const isOnlyNullable = (typeof s === 'object' && s.type === 'null') ||
|
|
36
|
+
(typeof s === 'object' && s?.nullable === true && Object.keys(s).length === 1);
|
|
37
|
+
if (isOnlyNullable) {
|
|
38
|
+
return {
|
|
39
|
+
schemas: acc.schemas,
|
|
40
|
+
nullable: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const z = zodToOpenAPI(s, paramName, paramIn);
|
|
44
|
+
return {
|
|
45
|
+
schemas: [...acc.schemas, wrap(z, s)],
|
|
46
|
+
nullable: acc.nullable,
|
|
47
|
+
};
|
|
48
|
+
}, {
|
|
49
|
+
schemas: [],
|
|
50
|
+
nullable: schema.nullable === true ||
|
|
51
|
+
(Array.isArray(schema.type) ? schema.type.includes('null') : schema.type === 'null'),
|
|
52
|
+
});
|
|
53
|
+
if (schemas.length === 0) {
|
|
54
|
+
return wrap('z.any()', { ...schema, nullable }, paramName, paramIn);
|
|
55
|
+
}
|
|
56
|
+
if (schemas.length === 1) {
|
|
57
|
+
return wrap(schemas[0], { ...schema, nullable }, paramName, paramIn);
|
|
58
|
+
}
|
|
59
|
+
const z = `z.intersection(${schemas.join(',')})`;
|
|
60
|
+
return wrap(z, schema, paramName, paramIn);
|
|
61
|
+
}
|
|
62
|
+
// anyOf
|
|
63
|
+
if (schema.anyOf) {
|
|
64
|
+
if (!schema.anyOf || schema.anyOf.length === 0) {
|
|
65
|
+
return 'z.any()';
|
|
66
|
+
}
|
|
67
|
+
const schemas = schema.anyOf.map((subSchema) => {
|
|
68
|
+
return zodToOpenAPI(subSchema, paramName, paramIn);
|
|
69
|
+
});
|
|
70
|
+
const z = `z.union([${schemas.join(',')}])`;
|
|
71
|
+
return wrap(z, schema);
|
|
72
|
+
}
|
|
73
|
+
// oneOf
|
|
74
|
+
if (schema.oneOf) {
|
|
75
|
+
if (!schema.oneOf || schema.oneOf.length === 0) {
|
|
76
|
+
return 'z.any()';
|
|
77
|
+
}
|
|
78
|
+
const schemas = schema.oneOf.map((schema) => {
|
|
79
|
+
return zodToOpenAPI(schema, paramName, paramIn);
|
|
80
|
+
});
|
|
81
|
+
// discriminatedUnion Support hesitant
|
|
82
|
+
// This is because using intersection causes a type error.
|
|
83
|
+
// const discriminator = schema.discriminator?.propertyName
|
|
84
|
+
// const z = discriminator
|
|
85
|
+
// ? `z.discriminatedUnion('${discriminator}',[${schemas.join(',')}])`
|
|
86
|
+
// : `z.union([${schemas.join(',')}])`
|
|
87
|
+
const z = `z.union([${schemas.join(',')}])`;
|
|
88
|
+
return wrap(z, schema, paramName, paramIn);
|
|
89
|
+
}
|
|
90
|
+
// not
|
|
91
|
+
if (schema.not) {
|
|
92
|
+
if (typeof schema.not === 'object' && schema.not.type && typeof schema.not.type === 'string') {
|
|
93
|
+
const predicate = `(v) => typeof v !== '${schema.not.type}'`;
|
|
94
|
+
return `z.any().refine(${predicate})`;
|
|
95
|
+
}
|
|
96
|
+
if (typeof schema.not === 'object' && Array.isArray(schema.not.enum)) {
|
|
97
|
+
const list = JSON.stringify(schema.not.enum);
|
|
98
|
+
const predicate = `(v) => !${list}.includes(v)`;
|
|
99
|
+
return `z.any().refine(${predicate})`;
|
|
100
|
+
}
|
|
101
|
+
return 'z.any()';
|
|
102
|
+
}
|
|
103
|
+
// const
|
|
104
|
+
if (schema.const) {
|
|
105
|
+
const z = `z.literal(${JSON.stringify(schema.const)})`;
|
|
106
|
+
return wrap(z, schema, paramName, paramIn);
|
|
107
|
+
}
|
|
108
|
+
/* enum */
|
|
109
|
+
if (schema.enum)
|
|
110
|
+
return wrap(_enum(schema), schema, paramName, paramIn);
|
|
111
|
+
/* properties */
|
|
112
|
+
if (schema.properties)
|
|
113
|
+
return wrap(object(schema), schema, paramName, paramIn);
|
|
114
|
+
const t = normalizeTypes(schema.type);
|
|
115
|
+
/* string */
|
|
116
|
+
if (t.includes('string'))
|
|
117
|
+
return wrap(string(schema), schema, paramName, paramIn);
|
|
118
|
+
/* number */
|
|
119
|
+
if (t.includes('number'))
|
|
120
|
+
return wrap(number(schema), schema, paramName, paramIn);
|
|
121
|
+
/* integer & bigint */
|
|
122
|
+
if (t.includes('integer'))
|
|
123
|
+
return wrap(integer(schema), schema, paramName, paramIn);
|
|
124
|
+
/* boolean */
|
|
125
|
+
if (t.includes('boolean'))
|
|
126
|
+
return wrap('z.boolean()', schema, paramName, paramIn);
|
|
127
|
+
/* array */
|
|
128
|
+
if (t.includes('array'))
|
|
129
|
+
return wrap(array(schema), schema, paramName, paramIn);
|
|
130
|
+
/* object */
|
|
131
|
+
if (t.includes('object'))
|
|
132
|
+
return wrap(object(schema), schema, paramName, paramIn);
|
|
133
|
+
/* date */
|
|
134
|
+
if (t.includes('date'))
|
|
135
|
+
return wrap('z.date()', schema, paramName, paramIn);
|
|
136
|
+
/* null only */
|
|
137
|
+
if (t.length === 1 && t[0] === 'null')
|
|
138
|
+
return wrap('z.null()', schema, paramName, paramIn);
|
|
139
|
+
console.warn(`fallback to z.any(): schema=${JSON.stringify(schema)}`);
|
|
140
|
+
return wrap('z.any()', schema, paramName, paramIn);
|
|
141
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { zodToOpenAPI } from '../index.js';
|
|
2
|
+
export function array(schema) {
|
|
3
|
+
const array = `z.array(${schema.items ? zodToOpenAPI(schema.items) : 'z.any()'})`;
|
|
4
|
+
if (typeof schema.minItems === 'number' && typeof schema.maxItems === 'number') {
|
|
5
|
+
if (schema.minItems === schema.maxItems) {
|
|
6
|
+
return `${array}.length(${schema.minItems})`;
|
|
7
|
+
}
|
|
8
|
+
return `${array}.min(${schema.minItems}).max(${schema.maxItems})`;
|
|
9
|
+
}
|
|
10
|
+
if (typeof schema.minItems === 'number') {
|
|
11
|
+
return `${array}.min(${schema.minItems})`;
|
|
12
|
+
}
|
|
13
|
+
if (typeof schema.maxItems === 'number') {
|
|
14
|
+
return `${array}.max(${schema.maxItems})`;
|
|
15
|
+
}
|
|
16
|
+
return array;
|
|
17
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function _enum(schema) {
|
|
2
|
+
/* -------------------------- helpers -------------------------- */
|
|
3
|
+
const hasType = (t) => schema.type === t || (Array.isArray(schema.type) && schema.type.some((x) => x === t));
|
|
4
|
+
const lit = (v) => v === null ? 'null' : typeof v === 'string' ? `'${v}'` : String(v);
|
|
5
|
+
const tuple = (arr) => `z.tuple([${arr.map((i) => `z.literal(${lit(i)})`).join(',')}])`;
|
|
6
|
+
/* --------------------------- guard --------------------------- */
|
|
7
|
+
if (!schema.enum || schema.enum.length === 0)
|
|
8
|
+
return 'z.any()';
|
|
9
|
+
/* ------------------- number / integer enum ------------------- */
|
|
10
|
+
if (hasType('number') || hasType('integer')) {
|
|
11
|
+
return schema.enum.length > 1
|
|
12
|
+
? `z.union([${schema.enum.map((v) => `z.literal(${v})`).join(',')}])`
|
|
13
|
+
: `z.literal(${schema.enum[0]})`;
|
|
14
|
+
}
|
|
15
|
+
/* ----------------------- boolean enum ------------------------ */
|
|
16
|
+
if (hasType('boolean')) {
|
|
17
|
+
return schema.enum.length > 1
|
|
18
|
+
? `z.union([${schema.enum.map((v) => `z.literal(${v})`).join(',')}])`
|
|
19
|
+
: `z.literal(${schema.enum[0]})`;
|
|
20
|
+
}
|
|
21
|
+
/* ----------------------- array enum -------------------------- */
|
|
22
|
+
if (hasType('array')) {
|
|
23
|
+
if (schema.enum.length === 1 && Array.isArray(schema.enum[0])) {
|
|
24
|
+
return tuple(schema.enum[0]);
|
|
25
|
+
}
|
|
26
|
+
const parts = schema.enum.map((v) => (Array.isArray(v) ? tuple(v) : `z.literal(${lit(v)})`));
|
|
27
|
+
return `z.union([${parts.join(',')}])`;
|
|
28
|
+
}
|
|
29
|
+
/* ----------------------- string enum ------------------------- */
|
|
30
|
+
if (schema.enum.every((v) => typeof v === 'string')) {
|
|
31
|
+
return schema.enum.length > 1
|
|
32
|
+
? `z.enum(${JSON.stringify(schema.enum)})`
|
|
33
|
+
: `z.literal('${schema.enum[0]}')`;
|
|
34
|
+
}
|
|
35
|
+
/* -------------------- mixed / null only ---------------------- */
|
|
36
|
+
if (schema.enum.length > 1) {
|
|
37
|
+
const parts = schema.enum.map((v) => `z.literal(${lit(v)})`);
|
|
38
|
+
return `z.union([${parts.join(',')}])`;
|
|
39
|
+
}
|
|
40
|
+
return `z.literal(${lit(schema.enum[0])})`;
|
|
41
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { wrap } from '../../../helper/wrap.js';
|
|
2
1
|
/**
|
|
3
2
|
* Generates a Zod schema for integer types based on OpenAPI schema.
|
|
4
3
|
* Supports int32, int64, and bigint formats.
|
|
@@ -88,6 +87,5 @@ export function integer(schema) {
|
|
|
88
87
|
if (schema.multipleOf !== undefined && typeof schema.multipleOf === 'number') {
|
|
89
88
|
o.push(`.multipleOf(${lit(schema.multipleOf)})`);
|
|
90
89
|
}
|
|
91
|
-
|
|
92
|
-
return wrap(z, schema);
|
|
90
|
+
return o.join('');
|
|
93
91
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { wrap } from '../../../helper/wrap.js';
|
|
2
1
|
/**
|
|
3
2
|
* Generates a Zod schema for number types based on OpenAPI schema.
|
|
4
3
|
* Supports float, float32, float64, and number formats.
|
|
@@ -78,6 +77,5 @@ export function number(schema) {
|
|
|
78
77
|
if (schema.multipleOf !== undefined) {
|
|
79
78
|
o.push(`.multipleOf(${schema.multipleOf})`);
|
|
80
79
|
}
|
|
81
|
-
|
|
82
|
-
return wrap(z, schema);
|
|
80
|
+
return o.join('');
|
|
83
81
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { propertiesSchema } from '../../../helper/properties-schema.js';
|
|
2
|
+
import { zodToOpenAPI } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates a Zod object schema from an OpenAPI schema definition.
|
|
5
|
+
*
|
|
6
|
+
* @param schema - Schema definition.
|
|
7
|
+
* @returns The Zod object schema string.
|
|
8
|
+
*/
|
|
9
|
+
export function object(schema) {
|
|
10
|
+
// // allOf, oneOf, anyOf, not
|
|
11
|
+
if (schema.oneOf)
|
|
12
|
+
return zodToOpenAPI(schema);
|
|
13
|
+
if (schema.anyOf)
|
|
14
|
+
return zodToOpenAPI(schema);
|
|
15
|
+
if (schema.allOf)
|
|
16
|
+
return zodToOpenAPI(schema);
|
|
17
|
+
if (schema.not)
|
|
18
|
+
return zodToOpenAPI(schema);
|
|
19
|
+
if (schema.additionalProperties) {
|
|
20
|
+
if (typeof schema.additionalProperties === 'boolean') {
|
|
21
|
+
if (schema.properties) {
|
|
22
|
+
const s = propertiesSchema(schema.properties, Array.isArray(schema.required) ? schema.required : []);
|
|
23
|
+
if (schema.additionalProperties === true) {
|
|
24
|
+
return s.replace('object', 'looseObject');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return 'z.any()';
|
|
28
|
+
}
|
|
29
|
+
const s = zodToOpenAPI(schema.additionalProperties);
|
|
30
|
+
return `z.record(z.string(),${s})`;
|
|
31
|
+
}
|
|
32
|
+
if (schema.properties) {
|
|
33
|
+
const s = propertiesSchema(schema.properties, Array.isArray(schema.required) ? schema.required : []);
|
|
34
|
+
if (schema.additionalProperties === false) {
|
|
35
|
+
return s.replace('object', 'strictObject');
|
|
36
|
+
}
|
|
37
|
+
return s;
|
|
38
|
+
}
|
|
39
|
+
return 'z.object({})';
|
|
40
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { wrap } from '../../../helper/wrap.js';
|
|
2
1
|
import { regex } from '../../../utils/index.js';
|
|
3
2
|
const FORMAT_STRING = {
|
|
4
3
|
email: 'email()',
|
|
@@ -51,6 +50,5 @@ export function string(schema) {
|
|
|
51
50
|
o.push(`.max(${schema.maxLength})`);
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
return wrap(z, schema);
|
|
53
|
+
return o.join('');
|
|
56
54
|
}
|
package/dist/helper/index.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
export { allOf } from './allof.js';
|
|
2
|
-
export { anyOf } from './anyof.js';
|
|
3
1
|
export { docs } from './docs.js';
|
|
4
2
|
export { getRouteMaps } from './get-route-maps.js';
|
|
5
|
-
export {
|
|
6
|
-
export { not } from './not.js';
|
|
7
|
-
export { oneOf } from './oneof.js';
|
|
3
|
+
export { propertiesSchema } from './properties-schema.js';
|
|
8
4
|
export { resolveSchemasDependencies } from './resolve-schemas-dependencies.js';
|
|
9
5
|
export { wrap } from './wrap.js';
|
|
10
|
-
export { zodToOpenAPI } from './zod-to-openapi.js';
|
|
11
6
|
export { zodToOpenAPISchema } from './zod-to-openapi-schema.js';
|
package/dist/helper/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
export { allOf } from './allof.js';
|
|
2
|
-
export { anyOf } from './anyof.js';
|
|
3
1
|
export { docs } from './docs.js';
|
|
4
2
|
export { getRouteMaps } from './get-route-maps.js';
|
|
5
|
-
export {
|
|
6
|
-
export { not } from './not.js';
|
|
7
|
-
export { oneOf } from './oneof.js';
|
|
3
|
+
export { propertiesSchema } from './properties-schema.js';
|
|
8
4
|
export { resolveSchemasDependencies } from './resolve-schemas-dependencies.js';
|
|
9
5
|
export { wrap } from './wrap.js';
|
|
10
|
-
export { zodToOpenAPI } from './zod-to-openapi.js';
|
|
11
6
|
export { zodToOpenAPISchema } from './zod-to-openapi-schema.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { zodToOpenAPI } from '../generator/zod-to-openapi/index.js';
|
|
2
|
+
import { getToSafeIdentifier } from '../utils/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Generates a Zod object schema string from a set of OpenAPI properties.
|
|
5
5
|
*
|
|
@@ -48,7 +48,7 @@ export function propertiesSchema(properties, required) {
|
|
|
48
48
|
const objectProperties = Object.entries(properties).map(([key, schema]) => {
|
|
49
49
|
const isRequired = required.includes(key);
|
|
50
50
|
const safeKey = getToSafeIdentifier(key);
|
|
51
|
-
return `${safeKey}:${
|
|
51
|
+
return `${safeKey}:${zodToOpenAPI(schema)}${isRequired ? '' : '.optional()'}`;
|
|
52
52
|
});
|
|
53
53
|
// Check if all properties are optional
|
|
54
54
|
const allOptional = objectProperties.every((prop) => prop.includes('.optional()'));
|
package/dist/helper/wrap.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Schema } from '../openapi/index.js';
|
|
2
|
-
export declare function wrap(zod: string, schema: Schema): string;
|
|
2
|
+
export declare function wrap(zod: string, schema: Schema, paramName?: string, paramIn?: string): string;
|
package/dist/helper/wrap.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function wrap(zod, schema) {
|
|
1
|
+
export function wrap(zod, schema, paramName, paramIn) {
|
|
2
2
|
const formatLiteral = (v) => {
|
|
3
3
|
// boolean true or false
|
|
4
4
|
if (typeof v === 'boolean') {
|
|
@@ -14,9 +14,6 @@ export function wrap(zod, schema) {
|
|
|
14
14
|
}
|
|
15
15
|
return `${v}`;
|
|
16
16
|
}
|
|
17
|
-
if (typeof v === 'bigint') {
|
|
18
|
-
return `${v}n`;
|
|
19
|
-
}
|
|
20
17
|
// date
|
|
21
18
|
if (schema.type === 'date' && typeof v === 'string') {
|
|
22
19
|
return `new Date(${JSON.stringify(v)})`;
|
|
@@ -29,8 +26,26 @@ export function wrap(zod, schema) {
|
|
|
29
26
|
return JSON.stringify(v);
|
|
30
27
|
};
|
|
31
28
|
// why schema.default !== undefined becasue schema.default === 0 // → falsy
|
|
32
|
-
const
|
|
29
|
+
const s = schema.default !== undefined ? `${zod}.default(${formatLiteral(schema.default)})` : zod;
|
|
33
30
|
const isNullable = schema.nullable === true ||
|
|
34
31
|
(Array.isArray(schema.type) ? schema.type.includes('null') : schema.type === 'null');
|
|
35
|
-
|
|
32
|
+
const z = isNullable ? `${s}.nullable()` : s;
|
|
33
|
+
const openapiProps = [];
|
|
34
|
+
if (paramIn && paramName) {
|
|
35
|
+
const required = paramIn === 'path' ? true : !!schema.required;
|
|
36
|
+
openapiProps.push(`param:{in:"${paramIn}",name:${JSON.stringify(paramName)},required:${required}}`);
|
|
37
|
+
}
|
|
38
|
+
// Add 'example' if defined
|
|
39
|
+
if ('example' in schema && schema.example !== undefined) {
|
|
40
|
+
openapiProps.push(`example:${JSON.stringify(schema.example)}`);
|
|
41
|
+
}
|
|
42
|
+
// Add 'examples' if defined
|
|
43
|
+
if ('examples' in schema && Array.isArray(schema.examples) && schema.examples.length > 0) {
|
|
44
|
+
openapiProps.push(`examples:${JSON.stringify(schema.examples)}`);
|
|
45
|
+
}
|
|
46
|
+
// Add 'description' if defined
|
|
47
|
+
if ('description' in schema && schema.description !== undefined) {
|
|
48
|
+
openapiProps.push(`description:${JSON.stringify(schema.description)}`);
|
|
49
|
+
}
|
|
50
|
+
return openapiProps.length === 0 ? z : `${z}.openapi({${openapiProps.join(',')}})`;
|
|
36
51
|
}
|
package/dist/index.js
CHANGED
package/dist/openapi/index.d.ts
CHANGED
|
@@ -126,7 +126,7 @@ export type Schema = {
|
|
|
126
126
|
example?: unknown;
|
|
127
127
|
examples?: unknown;
|
|
128
128
|
properties?: Record<string, Schema>;
|
|
129
|
-
required?: string[]
|
|
129
|
+
required?: string[];
|
|
130
130
|
items?: Schema;
|
|
131
131
|
enum?: (string | number | boolean | null | (string | number | boolean | null)[])[];
|
|
132
132
|
nullable?: boolean;
|