@kubb/plugin-mcp 5.0.0-beta.15 → 5.0.0-beta.25
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/index.cjs +100 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +69 -21
- package/dist/index.js +100 -66
- package/dist/index.js.map +1 -1
- package/extension.yaml +600 -147
- package/package.json +7 -7
- package/src/components/McpHandler.tsx +8 -8
- package/src/components/Server.tsx +6 -6
- package/src/generators/mcpGenerator.tsx +11 -2
- package/src/generators/serverGenerator.tsx +15 -12
- package/src/plugin.ts +33 -1
- package/src/resolvers/resolverMcp.ts +8 -4
- package/src/types.ts +18 -12
- package/src/utils.ts +14 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/plugin-mcp",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.25",
|
|
4
4
|
"description": "Generate Model Context Protocol (MCP) tool definitions from your OpenAPI specification. Expose your REST APIs as AI-callable tools for LLMs, Claude, ChatGPT, and other AI assistants.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -47,18 +47,18 @@
|
|
|
47
47
|
"registry": "https://registry.npmjs.org/"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@kubb/core": "5.0.0-beta.
|
|
51
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
52
|
-
"@kubb/plugin-client": "5.0.0-beta.
|
|
53
|
-
"@kubb/plugin-ts": "5.0.0-beta.
|
|
54
|
-
"@kubb/plugin-zod": "5.0.0-beta.
|
|
50
|
+
"@kubb/core": "5.0.0-beta.25",
|
|
51
|
+
"@kubb/renderer-jsx": "5.0.0-beta.25",
|
|
52
|
+
"@kubb/plugin-client": "5.0.0-beta.25",
|
|
53
|
+
"@kubb/plugin-ts": "5.0.0-beta.25",
|
|
54
|
+
"@kubb/plugin-zod": "5.0.0-beta.25"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@internals/shared": "0.0.0",
|
|
58
58
|
"@internals/utils": "0.0.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@kubb/renderer-jsx": "5.0.0-beta.
|
|
61
|
+
"@kubb/renderer-jsx": "5.0.0-beta.25"
|
|
62
62
|
},
|
|
63
63
|
"size-limit": [
|
|
64
64
|
{
|
|
@@ -23,7 +23,7 @@ type Props = {
|
|
|
23
23
|
/**
|
|
24
24
|
* Base URL prepended to every generated request URL.
|
|
25
25
|
*/
|
|
26
|
-
baseURL: string | undefined
|
|
26
|
+
baseURL: string | null | undefined
|
|
27
27
|
/**
|
|
28
28
|
* Return type when calling fetch.
|
|
29
29
|
* - 'data' returns response data only.
|
|
@@ -57,7 +57,7 @@ export function McpHandler({ name, node, resolver, baseURL, dataReturnType, para
|
|
|
57
57
|
const { query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing })
|
|
58
58
|
const { path: originalPathParams, query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node)
|
|
59
59
|
|
|
60
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) :
|
|
60
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null
|
|
61
61
|
const responseName = resolver.resolveResponseName(node)
|
|
62
62
|
|
|
63
63
|
const errorResponses = node.responses.filter((r) => Number(r.statusCode) >= 400).map((r) => resolver.resolveResponseStatusName(node, r.statusCode))
|
|
@@ -77,15 +77,15 @@ export function McpHandler({ name, node, resolver, baseURL, dataReturnType, para
|
|
|
77
77
|
? `${baseParamsSignature}, request: RequestHandlerExtra<ServerRequest, ServerNotification>`
|
|
78
78
|
: 'request: RequestHandlerExtra<ServerRequest, ServerNotification>'
|
|
79
79
|
|
|
80
|
-
const pathParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalPathParams, camelCase) :
|
|
81
|
-
const queryParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalQueryParams, camelCase) :
|
|
82
|
-
const headerParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalHeaderParams, camelCase) :
|
|
80
|
+
const pathParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalPathParams, camelCase) : null
|
|
81
|
+
const queryParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalQueryParams, camelCase) : null
|
|
82
|
+
const headerParamsMapping = paramsCasing ? buildTransformedParamsMapping(originalHeaderParams, camelCase) : null
|
|
83
83
|
|
|
84
84
|
const contentTypeHeader =
|
|
85
|
-
contentType && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` :
|
|
86
|
-
const headers = [headerParams.length ? (headerParamsMapping ? '...mappedHeaders' : '...headers') :
|
|
85
|
+
contentType && contentType !== 'application/json' && contentType !== 'multipart/form-data' ? `'Content-Type': '${contentType}'` : null
|
|
86
|
+
const headers = [headerParams.length ? (headerParamsMapping ? '...mappedHeaders' : '...headers') : null, contentTypeHeader].filter(Boolean)
|
|
87
87
|
|
|
88
|
-
const fetchConfig: string
|
|
88
|
+
const fetchConfig: Array<string> = []
|
|
89
89
|
fetchConfig.push(`method: ${JSON.stringify(node.method.toUpperCase())}`)
|
|
90
90
|
fetchConfig.push(`url: ${urlPath.template}`)
|
|
91
91
|
if (baseURL) fetchConfig.push(`baseURL: \`${baseURL}\``)
|
|
@@ -43,13 +43,13 @@ type Props = {
|
|
|
43
43
|
/**
|
|
44
44
|
* Query params — individual schemas to compose into `z.object({ ... })`.
|
|
45
45
|
*/
|
|
46
|
-
queryParams?: string | Array<ZodParam>
|
|
46
|
+
queryParams?: string | Array<ZodParam> | null
|
|
47
47
|
/**
|
|
48
48
|
* Header params — individual schemas to compose into `z.object({ ... })`.
|
|
49
49
|
*/
|
|
50
|
-
headerParams?: string | Array<ZodParam>
|
|
51
|
-
requestName?: string
|
|
52
|
-
responseName?: string
|
|
50
|
+
headerParams?: string | Array<ZodParam> | null
|
|
51
|
+
requestName?: string | null
|
|
52
|
+
responseName?: string | null
|
|
53
53
|
}
|
|
54
54
|
node: ast.OperationNode
|
|
55
55
|
}>
|
|
@@ -104,10 +104,10 @@ export function Server({ name, serverName, serverVersion, paramsCasing, operatio
|
|
|
104
104
|
}),
|
|
105
105
|
],
|
|
106
106
|
})
|
|
107
|
-
:
|
|
107
|
+
: null
|
|
108
108
|
|
|
109
109
|
const destructured = paramsNode ? (keysPrinter.print(paramsNode) ?? '') : ''
|
|
110
|
-
const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(', ')} }` :
|
|
110
|
+
const inputSchema = entries.length ? `{ ${entries.map((e) => `${e.key}: ${e.value}`).join(', ')} }` : null
|
|
111
111
|
const outputSchema = zod.responseName
|
|
112
112
|
|
|
113
113
|
const config = [
|
|
@@ -6,6 +6,12 @@ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
|
|
|
6
6
|
import { McpHandler } from '../components/McpHandler.tsx'
|
|
7
7
|
import type { PluginMcp } from '../types.ts'
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Built-in operation generator for `@kubb/plugin-mcp`. Emits one MCP tool
|
|
11
|
+
* handler per OpenAPI operation, wiring the input Zod schema, the HTTP call,
|
|
12
|
+
* and the response shape into a single function that an MCP server can
|
|
13
|
+
* register as a callable tool.
|
|
14
|
+
*/
|
|
9
15
|
export const mcpGenerator = defineGenerator<PluginMcp>({
|
|
10
16
|
name: 'mcp',
|
|
11
17
|
renderer: jsxRendererSync,
|
|
@@ -25,13 +31,16 @@ export const mcpGenerator = defineGenerator<PluginMcp>({
|
|
|
25
31
|
|
|
26
32
|
const meta = {
|
|
27
33
|
name: resolver.resolveHandlerName(node),
|
|
28
|
-
file: resolver.resolveFile(
|
|
34
|
+
file: resolver.resolveFile(
|
|
35
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
36
|
+
{ root, output, group: group ?? undefined },
|
|
37
|
+
),
|
|
29
38
|
fileTs: tsResolver.resolveFile(
|
|
30
39
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
31
40
|
{
|
|
32
41
|
root,
|
|
33
42
|
output: pluginTs.options?.output ?? output,
|
|
34
|
-
group: pluginTs.options?.group,
|
|
43
|
+
group: pluginTs.options?.group ?? undefined,
|
|
35
44
|
},
|
|
36
45
|
),
|
|
37
46
|
} as const
|
|
@@ -17,7 +17,7 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
17
17
|
name: 'operations',
|
|
18
18
|
renderer: jsxRendererSync,
|
|
19
19
|
operations(nodes, ctx) {
|
|
20
|
-
const { config, resolver, plugin, driver, root
|
|
20
|
+
const { config, resolver, plugin, driver, root } = ctx
|
|
21
21
|
const { output, paramsCasing, group } = ctx.options
|
|
22
22
|
|
|
23
23
|
const pluginZod = driver.getPlugin(pluginZodName)
|
|
@@ -46,20 +46,23 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
46
46
|
const operationsMapped = nodes.map((node) => {
|
|
47
47
|
const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing })
|
|
48
48
|
|
|
49
|
-
const mcpFile = resolver.resolveFile(
|
|
49
|
+
const mcpFile = resolver.resolveFile(
|
|
50
|
+
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
51
|
+
{ root, output, group: group ?? undefined },
|
|
52
|
+
)
|
|
50
53
|
|
|
51
54
|
const zodFile = zodResolver.resolveFile(
|
|
52
55
|
{ name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
|
|
53
56
|
{
|
|
54
57
|
root,
|
|
55
58
|
output: pluginZod.options?.output ?? output,
|
|
56
|
-
group: pluginZod.options?.group,
|
|
59
|
+
group: pluginZod.options?.group ?? undefined,
|
|
57
60
|
},
|
|
58
61
|
)
|
|
59
62
|
|
|
60
|
-
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) :
|
|
63
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName(node) : null
|
|
61
64
|
const successStatus = findSuccessStatusCode(node.responses)
|
|
62
|
-
const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) :
|
|
65
|
+
const responseName = successStatus ? zodResolver.resolveResponseStatusName(node, successStatus) : null
|
|
63
66
|
|
|
64
67
|
const resolveParams = (params: typeof pathParams) => params.map((p) => ({ name: p.name, schemaName: zodResolver.resolveParamName(node, p) }))
|
|
65
68
|
|
|
@@ -75,8 +78,8 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
75
78
|
},
|
|
76
79
|
zod: {
|
|
77
80
|
pathParams: resolveParams(pathParams),
|
|
78
|
-
queryParams: queryParams.length ? resolveParams(queryParams) :
|
|
79
|
-
headerParams: headerParams.length ? resolveParams(headerParams) :
|
|
81
|
+
queryParams: queryParams.length ? resolveParams(queryParams) : null,
|
|
82
|
+
headerParams: headerParams.length ? resolveParams(headerParams) : null,
|
|
80
83
|
requestName,
|
|
81
84
|
responseName,
|
|
82
85
|
file: zodFile,
|
|
@@ -108,8 +111,8 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
108
111
|
baseName={serverFile.baseName}
|
|
109
112
|
path={serverFile.path}
|
|
110
113
|
meta={serverFile.meta}
|
|
111
|
-
banner={resolver.resolveBanner(
|
|
112
|
-
footer={resolver.resolveFooter(
|
|
114
|
+
banner={resolver.resolveBanner(ctx.meta, { output, config })}
|
|
115
|
+
footer={resolver.resolveFooter(ctx.meta, { output, config })}
|
|
113
116
|
>
|
|
114
117
|
<File.Import name={['McpServer']} path={'@modelcontextprotocol/sdk/server/mcp'} />
|
|
115
118
|
<File.Import name={['z']} path={'zod'} />
|
|
@@ -118,8 +121,8 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
118
121
|
{imports}
|
|
119
122
|
<Server
|
|
120
123
|
name={name}
|
|
121
|
-
serverName={
|
|
122
|
-
serverVersion={
|
|
124
|
+
serverName={ctx.meta.title ?? 'server'}
|
|
125
|
+
serverVersion={ctx.meta.version ?? '0.0.0'}
|
|
123
126
|
paramsCasing={paramsCasing}
|
|
124
127
|
operations={operationsMapped}
|
|
125
128
|
/>
|
|
@@ -130,7 +133,7 @@ export const serverGenerator = defineGenerator<PluginMcp>({
|
|
|
130
133
|
{`
|
|
131
134
|
{
|
|
132
135
|
"mcpServers": {
|
|
133
|
-
"${
|
|
136
|
+
"${ctx.meta.title || 'server'}": {
|
|
134
137
|
"type": "stdio",
|
|
135
138
|
"command": "npx",
|
|
136
139
|
"args": ["tsx", "${path.relative(path.dirname(jsonFile.path), serverFile.path)}"]
|
package/src/plugin.ts
CHANGED
|
@@ -13,8 +13,40 @@ import { serverGenerator } from './generators/serverGenerator.tsx'
|
|
|
13
13
|
import { resolverMcp } from './resolvers/resolverMcp.ts'
|
|
14
14
|
import type { PluginMcp } from './types.ts'
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Canonical plugin name for `@kubb/plugin-mcp`. Used for driver lookups and
|
|
18
|
+
* cross-plugin dependency references.
|
|
19
|
+
*/
|
|
16
20
|
export const pluginMcpName = 'plugin-mcp' satisfies PluginMcp['name']
|
|
17
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Generates a Model Context Protocol (MCP) server from an OpenAPI spec. Every
|
|
24
|
+
* operation becomes a typed MCP tool that AI assistants (Claude Desktop, Claude
|
|
25
|
+
* Code, MCP-compatible clients) can call directly.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { defineConfig } from 'kubb'
|
|
30
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
31
|
+
* import { pluginClient } from '@kubb/plugin-client'
|
|
32
|
+
* import { pluginZod } from '@kubb/plugin-zod'
|
|
33
|
+
* import { pluginMcp } from '@kubb/plugin-mcp'
|
|
34
|
+
*
|
|
35
|
+
* export default defineConfig({
|
|
36
|
+
* input: { path: './petStore.yaml' },
|
|
37
|
+
* output: { path: './src/gen' },
|
|
38
|
+
* plugins: [
|
|
39
|
+
* pluginTs(),
|
|
40
|
+
* pluginClient(),
|
|
41
|
+
* pluginZod(),
|
|
42
|
+
* pluginMcp({
|
|
43
|
+
* output: { path: './mcp' },
|
|
44
|
+
* client: { baseURL: 'https://petstore.swagger.io/v2' },
|
|
45
|
+
* }),
|
|
46
|
+
* ],
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
18
50
|
export const pluginMcp = definePlugin<PluginMcp>((options) => {
|
|
19
51
|
const {
|
|
20
52
|
output = { path: 'mcp', barrelType: 'named' },
|
|
@@ -44,7 +76,7 @@ export const pluginMcp = definePlugin<PluginMcp>((options) => {
|
|
|
44
76
|
return `${camelCase(ctx.group)}Requests`
|
|
45
77
|
},
|
|
46
78
|
} satisfies Group)
|
|
47
|
-
:
|
|
79
|
+
: null
|
|
48
80
|
|
|
49
81
|
return {
|
|
50
82
|
name: pluginMcpName,
|
|
@@ -3,12 +3,16 @@ import { defineResolver } from '@kubb/core'
|
|
|
3
3
|
import type { PluginMcp } from '../types.ts'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Default resolver used by `@kubb/plugin-mcp`. Decides the names and file
|
|
7
|
+
* paths for every generated MCP tool handler. Function names get a `Handler`
|
|
8
|
+
* suffix so an operation `addPet` becomes `addPetHandler`.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
10
|
+
* @example Resolve a handler name
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { resolverMcp } from '@kubb/plugin-mcp'
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
14
|
+
* resolverMcp.default('addPet', 'function') // 'addPetHandler'
|
|
15
|
+
* ```
|
|
12
16
|
*/
|
|
13
17
|
export const resolverMcp = defineResolver<PluginMcp>(() => ({
|
|
14
18
|
name: 'default',
|
package/src/types.ts
CHANGED
|
@@ -24,44 +24,50 @@ export type ResolverMcp = Resolver & {
|
|
|
24
24
|
|
|
25
25
|
export type Options = {
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* Where the generated MCP tool handlers are written and how they are exported.
|
|
28
|
+
*
|
|
29
|
+
* @default { path: 'mcp', barrel: { type: 'named' } }
|
|
29
30
|
*/
|
|
30
31
|
output?: Output
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
+
* HTTP client used by each MCP handler to call the underlying API. Mirrors a
|
|
34
|
+
* subset of `pluginClient` options.
|
|
33
35
|
*/
|
|
34
36
|
client?: ClientImportPath & Pick<PluginClient['options'], 'clientType' | 'dataReturnType' | 'baseURL' | 'bundle' | 'paramsCasing'>
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
38
|
+
* Rename parameter properties in the generated handlers. The HTTP layer still
|
|
39
|
+
* uses the original spec names; Kubb writes the mapping for you.
|
|
40
|
+
*
|
|
41
|
+
* @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
|
|
37
42
|
*/
|
|
38
43
|
paramsCasing?: 'camelcase'
|
|
39
44
|
/**
|
|
40
|
-
*
|
|
45
|
+
* Split generated files into subfolders based on the operation's tag.
|
|
41
46
|
*/
|
|
42
47
|
group?: Group
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
49
|
+
* Skip operations matching at least one entry in the list.
|
|
45
50
|
*/
|
|
46
51
|
exclude?: Array<Exclude>
|
|
47
52
|
/**
|
|
48
|
-
*
|
|
53
|
+
* Restrict generation to operations matching at least one entry in the list.
|
|
49
54
|
*/
|
|
50
55
|
include?: Array<Include>
|
|
51
56
|
/**
|
|
52
|
-
*
|
|
57
|
+
* Apply a different options object to operations matching a pattern.
|
|
53
58
|
*/
|
|
54
59
|
override?: Array<Override<ResolvedOptions>>
|
|
55
60
|
/**
|
|
56
|
-
* Override
|
|
61
|
+
* Override how handler names and file paths are built. Methods you omit fall
|
|
62
|
+
* back to the default `resolverMcp`.
|
|
57
63
|
*/
|
|
58
64
|
resolver?: Partial<ResolverMcp> & ThisType<ResolverMcp>
|
|
59
65
|
/**
|
|
60
|
-
* AST visitor to
|
|
66
|
+
* AST visitor applied to each operation node before printing.
|
|
61
67
|
*/
|
|
62
68
|
transformer?: ast.Visitor
|
|
63
69
|
/**
|
|
64
|
-
*
|
|
70
|
+
* Custom generators that run alongside the built-in MCP generators.
|
|
65
71
|
*/
|
|
66
72
|
generators?: Array<Generator<PluginMcp>>
|
|
67
73
|
}
|
|
@@ -71,7 +77,7 @@ type ResolvedOptions = {
|
|
|
71
77
|
exclude: Array<Exclude>
|
|
72
78
|
include: Array<Include> | undefined
|
|
73
79
|
override: Array<Override<ResolvedOptions>>
|
|
74
|
-
group: Group |
|
|
80
|
+
group: Group | null
|
|
75
81
|
client: Pick<PluginClient['options'], 'client' | 'clientType' | 'dataReturnType' | 'importPath' | 'baseURL' | 'bundle' | 'paramsCasing'>
|
|
76
82
|
paramsCasing: Options['paramsCasing']
|
|
77
83
|
resolver: ResolverMcp
|
package/src/utils.ts
CHANGED
|
@@ -22,43 +22,27 @@ export function zodGroupExpr(entry: string | Array<ZodParam>): string {
|
|
|
22
22
|
* Used as fallback when no named zod schema is available for a path parameter.
|
|
23
23
|
*/
|
|
24
24
|
export function zodExprFromSchemaNode(schema: ast.SchemaNode): string {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
case 'enum': {
|
|
28
|
-
// namedEnumValues takes priority over enumValues
|
|
25
|
+
const baseExpr = (() => {
|
|
26
|
+
if (schema.type === 'enum') {
|
|
29
27
|
const rawValues: Array<string | number | boolean> = schema.namedEnumValues?.length
|
|
30
28
|
? schema.namedEnumValues.map((v) => v.value)
|
|
31
29
|
: (schema.enumValues ?? []).filter((v): v is string | number | boolean => v !== null)
|
|
32
30
|
|
|
33
31
|
if (rawValues.length > 0 && rawValues.every((v) => typeof v === 'string')) {
|
|
34
|
-
|
|
35
|
-
}
|
|
32
|
+
return `z.enum([${rawValues.map((v) => JSON.stringify(v)).join(', ')}])`
|
|
33
|
+
}
|
|
34
|
+
if (rawValues.length > 0) {
|
|
36
35
|
const literals = rawValues.map((v) => `z.literal(${JSON.stringify(v)})`)
|
|
37
|
-
|
|
38
|
-
} else {
|
|
39
|
-
expr = 'z.string()'
|
|
36
|
+
return literals.length === 1 ? literals[0]! : `z.union([${literals.join(', ')}])`
|
|
40
37
|
}
|
|
41
|
-
|
|
38
|
+
return 'z.string()'
|
|
42
39
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
case 'boolean':
|
|
50
|
-
expr = 'z.boolean()'
|
|
51
|
-
break
|
|
52
|
-
case 'array':
|
|
53
|
-
expr = 'z.array(z.unknown())'
|
|
54
|
-
break
|
|
55
|
-
default:
|
|
56
|
-
expr = 'z.string()'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (schema.nullable) {
|
|
60
|
-
expr = `${expr}.nullable()`
|
|
61
|
-
}
|
|
40
|
+
if (schema.type === 'integer') return 'z.coerce.number()'
|
|
41
|
+
if (schema.type === 'number') return 'z.number()'
|
|
42
|
+
if (schema.type === 'boolean') return 'z.boolean()'
|
|
43
|
+
if (schema.type === 'array') return 'z.array(z.unknown())'
|
|
44
|
+
return 'z.string()'
|
|
45
|
+
})()
|
|
62
46
|
|
|
63
|
-
return
|
|
47
|
+
return schema.nullable ? `${baseExpr}.nullable()` : baseExpr
|
|
64
48
|
}
|