@kubb/mcp 5.0.0-alpha.9 → 5.0.0-beta.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/README.md +8 -7
- package/dist/index.cjs +156 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -67
- package/dist/index.js.map +1 -1
- package/package.json +40 -42
- package/src/constants.ts +1 -0
- package/src/schemas/generateSchema.ts +0 -1
- package/src/tools/generate.ts +24 -27
- package/src/utils/loadUserConfig.ts +38 -16
- package/src/utils/resolveUserConfig.ts +8 -5
package/src/tools/generate.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncEventEmitter } from '@internals/utils'
|
|
2
|
-
import { type Config,
|
|
2
|
+
import { type Config, createKubb, type KubbHooks } from '@kubb/core'
|
|
3
3
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.d.ts'
|
|
4
4
|
import type { z } from 'zod'
|
|
5
5
|
import type { generateSchema } from '../schemas/generateSchema.ts'
|
|
@@ -20,7 +20,7 @@ export async function generate(schema: z.infer<typeof generateSchema>, handler:
|
|
|
20
20
|
const { config: configPath, input, output, logLevel } = schema
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
|
-
const
|
|
23
|
+
const hooks = new AsyncEventEmitter<KubbHooks>()
|
|
24
24
|
const messages: string[] = []
|
|
25
25
|
|
|
26
26
|
// Helper to send notifications
|
|
@@ -36,50 +36,52 @@ export async function generate(schema: z.infer<typeof generateSchema>, handler:
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// Capture events for output and send notifications
|
|
39
|
-
|
|
39
|
+
hooks.on('kubb:info', async ({ message }: { message: string }) => {
|
|
40
40
|
await notify(NotifyTypes.INFO, message)
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
hooks.on('kubb:success', async ({ message }: { message: string }) => {
|
|
44
44
|
await notify(NotifyTypes.SUCCESS, message)
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
hooks.on('kubb:error', async ({ error }: { error: Error }) => {
|
|
48
48
|
await notify(NotifyTypes.ERROR, error.message, { stack: error.stack })
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
hooks.on('kubb:warn', async ({ message }: { message: string }) => {
|
|
52
52
|
await notify(NotifyTypes.WARN, message)
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
// Plugin lifecycle events
|
|
56
|
-
|
|
57
|
-
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${name}`)
|
|
56
|
+
hooks.on('kubb:plugin:start', async ({ plugin }) => {
|
|
57
|
+
await notify(NotifyTypes.PLUGIN_START, `Plugin starting: ${plugin.name}`)
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
await notify(NotifyTypes.PLUGIN_END, `Plugin finished: ${name}`, {
|
|
60
|
+
hooks.on('kubb:plugin:end', async ({ plugin, duration }) => {
|
|
61
|
+
await notify(NotifyTypes.PLUGIN_END, `Plugin finished: ${plugin.name}`, {
|
|
62
|
+
duration,
|
|
63
|
+
})
|
|
62
64
|
})
|
|
63
65
|
|
|
64
66
|
// File processing events
|
|
65
|
-
|
|
67
|
+
hooks.on('kubb:files:processing:start', async () => {
|
|
66
68
|
await notify(NotifyTypes.FILES_START, 'Starting file processing')
|
|
67
69
|
})
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
hooks.on('kubb:file:processing:update', async ({ file }: { file: { name: string } }) => {
|
|
70
72
|
await notify(NotifyTypes.FILE_UPDATE, `Processing file: ${file.name}`)
|
|
71
73
|
})
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
hooks.on('kubb:files:processing:end', async () => {
|
|
74
76
|
await notify(NotifyTypes.FILES_END, 'File processing complete')
|
|
75
77
|
})
|
|
76
78
|
|
|
77
79
|
// Generation events
|
|
78
|
-
|
|
80
|
+
hooks.on('kubb:generation:start', async () => {
|
|
79
81
|
await notify(NotifyTypes.GENERATION_START, 'Generation started')
|
|
80
82
|
})
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
hooks.on('kubb:generation:end', async () => {
|
|
83
85
|
await notify(NotifyTypes.GENERATION_END, 'Generation ended')
|
|
84
86
|
})
|
|
85
87
|
|
|
@@ -96,7 +98,10 @@ export async function generate(schema: z.infer<typeof generateSchema>, handler:
|
|
|
96
98
|
throw new Error('Array type in kubb.config.ts is not supported in this tool. Please provide a single configuration object.')
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
userConfig = await resolveUserConfig(userConfig, {
|
|
101
|
+
userConfig = await resolveUserConfig(userConfig, {
|
|
102
|
+
configPath,
|
|
103
|
+
logLevel,
|
|
104
|
+
})
|
|
100
105
|
} catch (error) {
|
|
101
106
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
102
107
|
await notify(NotifyTypes.CONFIG_ERROR, errorMessage)
|
|
@@ -138,20 +143,12 @@ export async function generate(schema: z.infer<typeof generateSchema>, handler:
|
|
|
138
143
|
// Setup and build
|
|
139
144
|
await notify(NotifyTypes.SETUP_START, 'Setting up Kubb')
|
|
140
145
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
events,
|
|
144
|
-
})
|
|
146
|
+
const kubb = createKubb(config, { hooks })
|
|
147
|
+
await kubb.setup()
|
|
145
148
|
await notify(NotifyTypes.SETUP_END, 'Kubb setup complete')
|
|
146
149
|
|
|
147
150
|
await notify(NotifyTypes.BUILD_START, 'Starting build')
|
|
148
|
-
const { files, failedPlugins, error } = await safeBuild(
|
|
149
|
-
{
|
|
150
|
-
config,
|
|
151
|
-
events,
|
|
152
|
-
},
|
|
153
|
-
{ driver, fabric, events, sources },
|
|
154
|
-
)
|
|
151
|
+
const { files, failedPlugins, error } = await kubb.safeBuild()
|
|
155
152
|
await notify(NotifyTypes.BUILD_END, `Build complete - Generated ${files.length} files`)
|
|
156
153
|
|
|
157
154
|
if (error || failedPlugins.size > 0) {
|
|
@@ -1,42 +1,64 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
1
2
|
import path from 'node:path'
|
|
2
3
|
import type { Config } from '@kubb/core'
|
|
3
|
-
import
|
|
4
|
+
import { unrun } from 'unrun'
|
|
5
|
+
import { ALLOWED_CONFIG_EXTENSIONS } from '../constants.ts'
|
|
4
6
|
import { NotifyTypes } from '../types.ts'
|
|
5
7
|
|
|
6
8
|
type NotifyFunction = (type: string, message: string, data?: Record<string, unknown>) => Promise<void>
|
|
7
9
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const loadedModules = new Map<string, unknown>()
|
|
11
|
+
|
|
12
|
+
async function loadModule(filePath: string): Promise<unknown> {
|
|
13
|
+
const ext = path.extname(filePath)
|
|
14
|
+
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
15
|
+
throw new Error(`Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(', ')}`)
|
|
16
|
+
}
|
|
17
|
+
if (loadedModules.has(filePath)) {
|
|
18
|
+
return loadedModules.get(filePath)
|
|
19
|
+
}
|
|
20
|
+
const { module } = await unrun({ path: filePath })
|
|
21
|
+
loadedModules.set(filePath, module)
|
|
22
|
+
return module
|
|
23
|
+
}
|
|
11
24
|
|
|
12
|
-
/**
|
|
13
|
-
* Load the user configuration from the specified path or current directory
|
|
14
|
-
*/
|
|
15
25
|
export async function loadUserConfig(configPath: string | undefined, { notify }: { notify: NotifyFunction }): Promise<{ userConfig: Config; cwd: string }> {
|
|
16
26
|
let userConfig: Config | undefined
|
|
17
27
|
let cwd: string
|
|
18
28
|
|
|
19
29
|
if (configPath) {
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
const ext = path.extname(configPath)
|
|
31
|
+
if (!ALLOWED_CONFIG_EXTENSIONS.has(ext)) {
|
|
32
|
+
const msg = `Invalid config file extension "${ext}". Allowed: ${[...ALLOWED_CONFIG_EXTENSIONS].join(', ')}`
|
|
33
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg)
|
|
34
|
+
throw new Error(msg)
|
|
35
|
+
}
|
|
36
|
+
const base = path.resolve(process.cwd())
|
|
37
|
+
const resolvedConfigPath = path.resolve(base, configPath)
|
|
38
|
+
const relative = path.relative(base, resolvedConfigPath)
|
|
39
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
40
|
+
const msg = 'Invalid config file path: must be within the current working directory'
|
|
41
|
+
await notify(NotifyTypes.CONFIG_ERROR, msg)
|
|
42
|
+
throw new Error(msg)
|
|
43
|
+
}
|
|
44
|
+
cwd = path.dirname(resolvedConfigPath)
|
|
22
45
|
|
|
23
|
-
// Try to load from path
|
|
24
46
|
try {
|
|
25
|
-
userConfig = await
|
|
26
|
-
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${
|
|
47
|
+
userConfig = (await loadModule(resolvedConfigPath)) as Config
|
|
48
|
+
await notify(NotifyTypes.CONFIG_LOADED, `Loaded config from ${resolvedConfigPath}`)
|
|
27
49
|
} catch (error) {
|
|
28
50
|
await notify(NotifyTypes.CONFIG_ERROR, `Failed to load config: ${error instanceof Error ? error.message : String(error)}`)
|
|
29
51
|
throw new Error(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`)
|
|
30
52
|
}
|
|
31
53
|
} else {
|
|
32
|
-
// Look for kubb.config in current directory with various extensions
|
|
33
54
|
cwd = process.cwd()
|
|
34
|
-
const configFileNames = ['kubb.config.ts', 'kubb.config.js', 'kubb.config.cjs']
|
|
55
|
+
const configFileNames = ['kubb.config.ts', 'kubb.config.mts', 'kubb.config.cts', 'kubb.config.js', 'kubb.config.cjs']
|
|
35
56
|
|
|
36
57
|
for (const configFileName of configFileNames) {
|
|
58
|
+
const configFilePath = path.resolve(process.cwd(), configFileName)
|
|
59
|
+
if (!existsSync(configFilePath)) continue
|
|
37
60
|
try {
|
|
38
|
-
|
|
39
|
-
userConfig = await jiti.import(configFilePath, { default: true })
|
|
61
|
+
userConfig = (await loadModule(configFilePath)) as Config
|
|
40
62
|
await notify(NotifyTypes.CONFIG_LOADED, `Loaded ${configFileName} from current directory`)
|
|
41
63
|
break
|
|
42
64
|
} catch {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isPromise } from '@internals/utils'
|
|
2
|
-
import type { CLIOptions, Config
|
|
2
|
+
import type { CLIOptions, Config } from '@kubb/core'
|
|
3
3
|
|
|
4
4
|
export type ResolveUserConfigOptions = {
|
|
5
5
|
configPath?: string
|
|
@@ -9,11 +9,14 @@ export type ResolveUserConfigOptions = {
|
|
|
9
9
|
/**
|
|
10
10
|
* Resolve the config by handling function configs and returning the final configuration
|
|
11
11
|
*/
|
|
12
|
-
export async function resolveUserConfig(
|
|
13
|
-
let kubbUserConfig = Promise.resolve(
|
|
12
|
+
export async function resolveUserConfig(config: Config, options: ResolveUserConfigOptions): Promise<Config> {
|
|
13
|
+
let kubbUserConfig = Promise.resolve(config) as Promise<Config>
|
|
14
14
|
|
|
15
|
-
if (typeof
|
|
16
|
-
const possiblePromise = (
|
|
15
|
+
if (typeof config === 'function') {
|
|
16
|
+
const possiblePromise = (config as any)({
|
|
17
|
+
logLevel: options.logLevel,
|
|
18
|
+
config: options.configPath,
|
|
19
|
+
} as CLIOptions)
|
|
17
20
|
if (isPromise(possiblePromise)) {
|
|
18
21
|
kubbUserConfig = possiblePromise
|
|
19
22
|
} else {
|