@kubb/cli 5.0.0-alpha.7 → 5.0.0-alpha.71

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.
Files changed (138) hide show
  1. package/README.md +4 -2
  2. package/bin/kubb.js +6 -0
  3. package/dist/agent-CouS-H2S.js +112 -0
  4. package/dist/agent-CouS-H2S.js.map +1 -0
  5. package/dist/{agent-Cc-0-A8t.cjs → agent-D5OIwM0z.cjs} +5 -5
  6. package/dist/agent-D5OIwM0z.cjs.map +1 -0
  7. package/dist/{agent-CjXjyuPe.js → agent-DypvJBwO.js} +5 -5
  8. package/dist/agent-DypvJBwO.js.map +1 -0
  9. package/dist/agent-Zo8CQwxZ.cjs +116 -0
  10. package/dist/agent-Zo8CQwxZ.cjs.map +1 -0
  11. package/dist/{constants-BTUap0zs.cjs → constants-BgLUQ1nx.cjs} +50 -9
  12. package/dist/constants-BgLUQ1nx.cjs.map +1 -0
  13. package/dist/{constants-CM3dJzjK.js → constants-mIdg9ls2.js} +39 -10
  14. package/dist/constants-mIdg9ls2.js.map +1 -0
  15. package/dist/define-Bdn8j5VM.cjs +54 -0
  16. package/dist/define-Bdn8j5VM.cjs.map +1 -0
  17. package/dist/define-Ctii4bel.js +43 -0
  18. package/dist/define-Ctii4bel.js.map +1 -0
  19. package/dist/{errors-DBW0N9w4.cjs → errors-CLCjoSg0.cjs} +22 -6
  20. package/dist/errors-CLCjoSg0.cjs.map +1 -0
  21. package/dist/errors-CjPmyZHy.js +43 -0
  22. package/dist/errors-CjPmyZHy.js.map +1 -0
  23. package/dist/{generate-BP0WpC20.cjs → generate-BW0J1wvg.cjs} +3 -3
  24. package/dist/generate-BW0J1wvg.cjs.map +1 -0
  25. package/dist/{generate-yOyKmo19.cjs → generate-CLYhWYY1.cjs} +553 -251
  26. package/dist/generate-CLYhWYY1.cjs.map +1 -0
  27. package/dist/{generate-BX6LmxKo.js → generate-Cy11Ra5n.js} +3 -3
  28. package/dist/generate-Cy11Ra5n.js.map +1 -0
  29. package/dist/{generate-BYqhZfbq.js → generate-DfhKa1mG.js} +546 -244
  30. package/dist/generate-DfhKa1mG.js.map +1 -0
  31. package/dist/index.cjs +49 -21
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +49 -21
  35. package/dist/index.js.map +1 -1
  36. package/dist/{init-DkwG4Gbs.cjs → init-C_EgqqO4.cjs} +4 -4
  37. package/dist/init-C_EgqqO4.cjs.map +1 -0
  38. package/dist/{init-BqkRvRTM.js → init-CaQZcAvy.js} +4 -4
  39. package/dist/init-CaQZcAvy.js.map +1 -0
  40. package/dist/{init-hmolV6B4.cjs → init-CmrUd26x.cjs} +31 -21
  41. package/dist/init-CmrUd26x.cjs.map +1 -0
  42. package/dist/{init-C-InrmSY.js → init-DEC1gCsk.js} +27 -17
  43. package/dist/init-DEC1gCsk.js.map +1 -0
  44. package/dist/{mcp-Cqn_PHUg.js → mcp-7rBVhoQz.js} +4 -4
  45. package/dist/{mcp-Cqn_PHUg.js.map → mcp-7rBVhoQz.js.map} +1 -1
  46. package/dist/{mcp-BnEafD5r.cjs → mcp-CmXrXzDF.cjs} +4 -4
  47. package/dist/{mcp-BnEafD5r.cjs.map → mcp-CmXrXzDF.cjs.map} +1 -1
  48. package/dist/{mcp-D2SHEg_d.js → mcp-nlFo36z0.js} +4 -5
  49. package/dist/mcp-nlFo36z0.js.map +1 -0
  50. package/dist/{mcp-ChHFPRzD.cjs → mcp-vi8t2Rkb.cjs} +5 -6
  51. package/dist/mcp-vi8t2Rkb.cjs.map +1 -0
  52. package/dist/package-BD90dOVc.js +6 -0
  53. package/dist/package-BD90dOVc.js.map +1 -0
  54. package/dist/{package-DZb6I8FQ.cjs → package-DsKVMEXO.cjs} +2 -2
  55. package/dist/package-DsKVMEXO.cjs.map +1 -0
  56. package/dist/{shell-7HPrTCJ5.cjs → shell-475fQKaX.cjs} +8 -3
  57. package/dist/shell-475fQKaX.cjs.map +1 -0
  58. package/dist/{shell-DqqWsHCD.js → shell-DLzN4fRo.js} +8 -3
  59. package/dist/shell-DLzN4fRo.js.map +1 -0
  60. package/dist/{telemetry-DxiR7clS.js → telemetry-CS5ZE0cN.js} +48 -6
  61. package/dist/telemetry-CS5ZE0cN.js.map +1 -0
  62. package/dist/{telemetry-Cn9X1I5B.cjs → telemetry-kmnc5OAT.cjs} +50 -8
  63. package/dist/telemetry-kmnc5OAT.cjs.map +1 -0
  64. package/dist/{validate-l8vLmwKA.js → validate-BR_0eveE.js} +5 -13
  65. package/dist/validate-BR_0eveE.js.map +1 -0
  66. package/dist/{validate-9FoHgtvi.cjs → validate-BZKRjT0Y.cjs} +4 -4
  67. package/dist/validate-BZKRjT0Y.cjs.map +1 -0
  68. package/dist/{validate-Bbrn3Q-A.cjs → validate-Db7vfaU9.cjs} +6 -14
  69. package/dist/validate-Db7vfaU9.cjs.map +1 -0
  70. package/dist/{validate-Bgqwmhir.js → validate-RG85Fqjh.js} +4 -4
  71. package/dist/validate-RG85Fqjh.js.map +1 -0
  72. package/package.json +47 -46
  73. package/src/commands/agent/start.ts +20 -4
  74. package/src/commands/generate.ts +35 -6
  75. package/src/commands/init.ts +6 -1
  76. package/src/commands/validate.ts +6 -1
  77. package/src/constants.ts +40 -11
  78. package/src/index.ts +10 -12
  79. package/src/loggers/clackLogger.ts +54 -46
  80. package/src/loggers/fileSystemLogger.ts +13 -11
  81. package/src/loggers/githubActionsLogger.ts +22 -22
  82. package/src/loggers/plainLogger.ts +21 -21
  83. package/src/runners/agent.ts +81 -34
  84. package/src/runners/generate.ts +90 -100
  85. package/src/runners/init.ts +9 -9
  86. package/src/runners/mcp.ts +17 -4
  87. package/src/runners/validate.ts +19 -15
  88. package/src/types.ts +11 -0
  89. package/src/utils/executeHooks.ts +15 -15
  90. package/src/utils/flags.ts +10 -0
  91. package/src/utils/getConfig.ts +10 -0
  92. package/src/utils/getCosmiConfig.ts +21 -12
  93. package/src/utils/getSummary.ts +1 -1
  94. package/src/utils/runHook.ts +28 -10
  95. package/src/utils/telemetry.ts +16 -3
  96. package/bin/kubb.cjs +0 -18
  97. package/dist/agent-C6o_6GSJ.cjs +0 -92
  98. package/dist/agent-C6o_6GSJ.cjs.map +0 -1
  99. package/dist/agent-Cc-0-A8t.cjs.map +0 -1
  100. package/dist/agent-CjXjyuPe.js.map +0 -1
  101. package/dist/agent-L50VNhXv.js +0 -88
  102. package/dist/agent-L50VNhXv.js.map +0 -1
  103. package/dist/constants-BTUap0zs.cjs.map +0 -1
  104. package/dist/constants-CM3dJzjK.js.map +0 -1
  105. package/dist/define--M_JMcDC.js +0 -25
  106. package/dist/define--M_JMcDC.js.map +0 -1
  107. package/dist/define-D6Kfm7-Z.cjs +0 -36
  108. package/dist/define-D6Kfm7-Z.cjs.map +0 -1
  109. package/dist/errors-6mF_WKxg.js +0 -27
  110. package/dist/errors-6mF_WKxg.js.map +0 -1
  111. package/dist/errors-DBW0N9w4.cjs.map +0 -1
  112. package/dist/generate-BP0WpC20.cjs.map +0 -1
  113. package/dist/generate-BX6LmxKo.js.map +0 -1
  114. package/dist/generate-BYqhZfbq.js.map +0 -1
  115. package/dist/generate-yOyKmo19.cjs.map +0 -1
  116. package/dist/init-BqkRvRTM.js.map +0 -1
  117. package/dist/init-C-InrmSY.js.map +0 -1
  118. package/dist/init-DkwG4Gbs.cjs.map +0 -1
  119. package/dist/init-hmolV6B4.cjs.map +0 -1
  120. package/dist/jiti-Cd3S0xwr.cjs +0 -16
  121. package/dist/jiti-Cd3S0xwr.cjs.map +0 -1
  122. package/dist/jiti-e08mD2Ph.js +0 -11
  123. package/dist/jiti-e08mD2Ph.js.map +0 -1
  124. package/dist/mcp-ChHFPRzD.cjs.map +0 -1
  125. package/dist/mcp-D2SHEg_d.js.map +0 -1
  126. package/dist/package-DLd3fZNd.js +0 -6
  127. package/dist/package-DLd3fZNd.js.map +0 -1
  128. package/dist/package-DZb6I8FQ.cjs.map +0 -1
  129. package/dist/shell-7HPrTCJ5.cjs.map +0 -1
  130. package/dist/shell-DqqWsHCD.js.map +0 -1
  131. package/dist/telemetry-Cn9X1I5B.cjs.map +0 -1
  132. package/dist/telemetry-DxiR7clS.js.map +0 -1
  133. package/dist/validate-9FoHgtvi.cjs.map +0 -1
  134. package/dist/validate-Bbrn3Q-A.cjs.map +0 -1
  135. package/dist/validate-Bgqwmhir.js.map +0 -1
  136. package/dist/validate-l8vLmwKA.js.map +0 -1
  137. package/src/utils/getIntro.ts +0 -1
  138. package/src/utils/jiti.ts +0 -9
@@ -4,25 +4,13 @@ import process from 'node:process'
4
4
  import { styleText } from 'node:util'
5
5
  import * as clack from '@clack/prompts'
6
6
  import type { AsyncEventEmitter } from '@internals/utils'
7
- import { AsyncEventEmitter as AsyncEventEmitterClass, executeIfOnline, toError } from '@internals/utils'
8
- import {
9
- type CLIOptions,
10
- type Config,
11
- detectFormatter,
12
- detectLinter,
13
- formatters,
14
- getConfigs,
15
- isInputPath,
16
- type KubbEvents,
17
- linters,
18
- logLevel as logLevelMap,
19
- safeBuild,
20
- setup,
21
- } from '@kubb/core'
7
+ import { AsyncEventEmitter as AsyncEventEmitterClass, detectFormatter, detectLinter, executeIfOnline, formatters, linters, toError } from '@internals/utils'
8
+ import { type CLIOptions, type Config, createKubb, isInputPath, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
22
9
  import { version } from '../../package.json'
23
10
  import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
24
11
  import { setupLogger } from '../loggers/utils.ts'
25
12
  import { executeHooks } from '../utils/executeHooks.ts'
13
+ import { getConfigs } from '../utils/getConfig.ts'
26
14
  import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
27
15
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
28
16
  import { startWatcher } from '../utils/watcher.ts'
@@ -30,7 +18,7 @@ import { startWatcher } from '../utils/watcher.ts'
30
18
  type GenerateProps = {
31
19
  input?: string
32
20
  config: Config
33
- events: AsyncEventEmitter<KubbEvents>
21
+ hooks: AsyncEventEmitter<KubbHooks>
34
22
  logLevel: number
35
23
  }
36
24
 
@@ -38,7 +26,7 @@ type ToolMap = typeof formatters | typeof linters
38
26
 
39
27
  type RunToolPassOptions = {
40
28
  toolValue: string
41
- detect: () => Promise<string | undefined>
29
+ detect: () => Promise<string | null>
42
30
  toolMap: ToolMap
43
31
  /** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
44
32
  toolLabel: string
@@ -48,7 +36,7 @@ type RunToolPassOptions = {
48
36
  configName: string | undefined
49
37
  outputPath: string
50
38
  logLevel: number
51
- events: AsyncEventEmitter<KubbEvents>
39
+ hooks: AsyncEventEmitter<KubbHooks>
52
40
  onStart: () => Promise<void>
53
41
  onEnd: () => Promise<void>
54
42
  }
@@ -63,7 +51,7 @@ async function runToolPass({
63
51
  configName,
64
52
  outputPath,
65
53
  logLevel,
66
- events,
54
+ hooks,
67
55
  onStart,
68
56
  onEnd,
69
57
  }: RunToolPassOptions) {
@@ -73,13 +61,15 @@ async function runToolPass({
73
61
  if (resolvedTool === 'auto') {
74
62
  const detected = await detect()
75
63
  if (!detected) {
76
- await events.emit('warn', noToolMessage)
64
+ await hooks.emit('kubb:warn', { message: noToolMessage })
77
65
  } else {
78
66
  resolvedTool = detected
79
- await events.emit('info', `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}`)
67
+ await hooks.emit('kubb:info', { message: `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}` })
80
68
  }
81
69
  }
82
70
 
71
+ let toolError: Error | undefined
72
+
83
73
  if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
84
74
  const toolConfig = toolMap[resolvedTool as keyof ToolMap]
85
75
 
@@ -87,33 +77,32 @@ async function runToolPass({
87
77
  const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
88
78
 
89
79
  // Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
90
- // where hook:end fires synchronously inside emit('hook:start') before the listener is registered.
80
+ // where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
91
81
  const hookEndPromise = new Promise<void>((resolve, reject) => {
92
- const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
93
- if (id !== hookId) return
94
- events.off('hook:end', handler)
95
- if (!success) {
96
- reject(error ?? new Error(`${toolConfig.errorMessage}`))
82
+ const handler = (ctx: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
83
+ if (ctx.id !== hookId) return
84
+ hooks.off('kubb:hook:end', handler)
85
+ if (!ctx.success) {
86
+ reject(ctx.error ?? new Error(`${toolConfig.errorMessage}`))
97
87
  return
98
88
  }
99
- events
100
- .emit(
101
- 'success',
102
- [
89
+ hooks
90
+ .emit('kubb:success', {
91
+ message: [
103
92
  `${successPrefix} with ${styleText('dim', resolvedTool)}`,
104
93
  logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
105
94
  'successfully',
106
95
  ]
107
96
  .filter(Boolean)
108
97
  .join(' '),
109
- )
98
+ })
110
99
  .then(resolve)
111
100
  .catch(reject)
112
101
  }
113
- events.on('hook:end', handler)
102
+ hooks.on('kubb:hook:end', handler)
114
103
  })
115
104
 
116
- await events.emit('hook:start', {
105
+ await hooks.emit('kubb:hook:start', {
117
106
  id: hookId,
118
107
  command: toolConfig.command,
119
108
  args: toolConfig.args(outputPath),
@@ -121,59 +110,52 @@ async function runToolPass({
121
110
 
122
111
  await hookEndPromise
123
112
  } catch (caughtError) {
124
- const err = new Error(toolConfig.errorMessage)
125
- err.cause = caughtError
126
- await events.emit('error', err)
113
+ // Use the actual error from the hook. toolConfig.errorMessage (e.g. "Oxlint not found")
114
+ // is misleading when the binary was found and ran but exited with a non-zero code.
115
+ // runHook already emitted kubb:error for binary-not-found cases; here we surface the
116
+ // real reason (e.g. "Hook execute failed: oxlint --fix …") for non-zero exits.
117
+ const err = toError(caughtError)
118
+ await hooks.emit('kubb:error', { error: err })
119
+ toolError = err
127
120
  }
128
121
  }
129
122
 
130
123
  await onEnd()
124
+
125
+ if (toolError) {
126
+ throw toolError
127
+ }
131
128
  }
132
129
 
133
- async function generate({ input, config: userConfig, events, logLevel }: GenerateProps): Promise<void> {
134
- const inputPath = input ?? ('path' in userConfig.input ? userConfig.input.path : undefined)
130
+ async function generate(options: GenerateProps): Promise<void> {
131
+ const { input, hooks, logLevel } = options
132
+
135
133
  const hrStart = process.hrtime()
134
+ const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
136
135
 
137
136
  const config: Config = {
138
- ...userConfig,
139
- root: userConfig.root || process.cwd(),
137
+ ...options.config,
140
138
  input: inputPath
141
139
  ? {
142
- ...userConfig.input,
140
+ ...options.config.input,
143
141
  path: inputPath,
144
142
  }
145
- : userConfig.input,
146
- output: {
147
- write: true,
148
- barrelType: 'named',
149
- extension: {
150
- '.ts': '.ts',
151
- },
152
- format: 'prettier',
153
- ...userConfig.output,
154
- },
155
- }
143
+ : options.config.input,
144
+ ...options.config.output,
145
+ } satisfies Config
156
146
 
157
- await events.emit('generation:start', config)
147
+ const kubb = createKubb(config, { hooks })
148
+ await kubb.setup()
158
149
 
159
- await events.emit('info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
150
+ await hooks.emit('kubb:generation:start', { config })
160
151
 
161
- const { sources, fabric, driver } = await setup({
162
- config,
163
- events,
164
- })
152
+ await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
165
153
 
166
- await events.emit('info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
154
+ await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
167
155
 
168
- const { files, failedPlugins, pluginTimings, error } = await safeBuild(
169
- {
170
- config,
171
- events,
172
- },
173
- { driver, fabric, events, sources },
174
- )
156
+ const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
175
157
 
176
- await events.emit('info', 'Load summary')
158
+ await hooks.emit('kubb:info', { message: 'Load summary' })
177
159
 
178
160
  // Handle build failures (either from failed plugins or general errors)
179
161
 
@@ -188,12 +170,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
188
170
  ].filter(Boolean)
189
171
 
190
172
  for (const err of allErrors) {
191
- await events.emit('error', err)
173
+ await hooks.emit('kubb:error', { error: err })
192
174
  }
193
175
 
194
- await events.emit('generation:end', config, files, sources)
176
+ await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
195
177
 
196
- await events.emit('generation:summary', config, {
178
+ await hooks.emit('kubb:generation:summary', {
179
+ config,
197
180
  failedPlugins,
198
181
  filesCreated: files.length,
199
182
  status: 'failed',
@@ -205,7 +188,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
205
188
  buildTelemetryEvent({
206
189
  command: 'generate',
207
190
  kubbVersion: version,
208
- plugins: driver.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
191
+ plugins: Array.from(driver.plugins.values(), (p) => ({
192
+ name: p.name,
193
+ options: p.options as Record<string, unknown>,
194
+ })),
209
195
  hrStart,
210
196
  filesCreated: files.length,
211
197
  status: 'failed',
@@ -215,8 +201,8 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
215
201
  process.exit(1)
216
202
  }
217
203
 
218
- await events.emit('success', 'Generation successfully', inputPath)
219
- await events.emit('generation:end', config, files, sources)
204
+ await hooks.emit('kubb:success', { message: 'Generation successfully', info: inputPath })
205
+ await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
220
206
 
221
207
  const outputPath = path.resolve(config.root, config.output.path)
222
208
 
@@ -227,13 +213,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
227
213
  toolMap: formatters,
228
214
  toolLabel: 'formatter',
229
215
  successPrefix: 'Formatting',
230
- noToolMessage: 'No formatter found (biome, prettier, or oxfmt). Skipping formatting.',
216
+ noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
231
217
  configName: config.name,
232
218
  outputPath,
233
219
  logLevel,
234
- events,
235
- onStart: () => events.emit('format:start'),
236
- onEnd: () => events.emit('format:end'),
220
+ hooks,
221
+ onStart: () => hooks.emit('kubb:format:start'),
222
+ onEnd: () => hooks.emit('kubb:format:end'),
237
223
  })
238
224
  }
239
225
 
@@ -244,25 +230,26 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
244
230
  toolMap: linters,
245
231
  toolLabel: 'linter',
246
232
  successPrefix: 'Linting',
247
- noToolMessage: 'No linter found (biome, oxlint, or eslint). Skipping linting.',
233
+ noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
248
234
  configName: config.name,
249
235
  outputPath,
250
236
  logLevel,
251
- events,
252
- onStart: () => events.emit('lint:start'),
253
- onEnd: () => events.emit('lint:end'),
237
+ hooks,
238
+ onStart: () => hooks.emit('kubb:lint:start'),
239
+ onEnd: () => hooks.emit('kubb:lint:end'),
254
240
  })
255
241
  }
256
242
 
257
243
  if (config.hooks) {
258
- await events.emit('hooks:start')
259
- await executeHooks({ hooks: config.hooks, events })
244
+ await hooks.emit('kubb:hooks:start')
245
+ await executeHooks({ configHooks: config.hooks, hooks })
260
246
 
261
- await events.emit('hooks:end')
247
+ await hooks.emit('kubb:hooks:end')
262
248
  }
263
249
 
264
250
  // Only reached when there are no failures (process.exit(1) is called above otherwise)
265
- await events.emit('generation:summary', config, {
251
+ await hooks.emit('kubb:generation:summary', {
252
+ config,
266
253
  failedPlugins,
267
254
  filesCreated: files.length,
268
255
  status: 'success',
@@ -273,7 +260,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
273
260
  const telemetryEvent = buildTelemetryEvent({
274
261
  command: 'generate',
275
262
  kubbVersion: version,
276
- plugins: driver.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
263
+ plugins: Array.from(driver.plugins.values(), (p) => ({
264
+ name: p.name,
265
+ options: p.options as Record<string, unknown>,
266
+ })),
277
267
  hrStart,
278
268
  filesCreated: files.length,
279
269
  status: 'success',
@@ -291,9 +281,9 @@ type GenerateCommandOptions = {
291
281
 
292
282
  export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
293
283
  const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
294
- const events = new AsyncEventEmitterClass<KubbEvents>()
284
+ const hooks = new AsyncEventEmitterClass<KubbHooks>()
295
285
 
296
- await setupLogger(events, { logLevel })
286
+ await setupLogger(hooks, { logLevel })
297
287
 
298
288
  await executeIfOnline(async () => {
299
289
  try {
@@ -302,7 +292,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
302
292
  const latestVersion = data.version
303
293
 
304
294
  if (latestVersion && version < latestVersion) {
305
- await events.emit('version:new', version, latestVersion)
295
+ await hooks.emit('kubb:version:new', { currentVersion: version, latestVersion })
306
296
  }
307
297
  } catch {
308
298
  // Ignore network errors for version check
@@ -313,31 +303,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
313
303
  const result = await getCosmiConfig('kubb', configPath)
314
304
  const configs = await getConfigs(result.config, { input } as CLIOptions)
315
305
 
316
- await events.emit('config:start')
317
- await events.emit('info', 'Config loaded', path.relative(process.cwd(), result.filepath))
318
- await events.emit('success', 'Config loaded successfully', path.relative(process.cwd(), result.filepath))
319
- await events.emit('config:end', configs)
306
+ await hooks.emit('kubb:config:start')
307
+ await hooks.emit('kubb:info', { message: 'Config loaded', info: path.relative(process.cwd(), result.filepath) })
308
+ await hooks.emit('kubb:success', { message: 'Config loaded successfully', info: path.relative(process.cwd(), result.filepath) })
309
+ await hooks.emit('kubb:config:end', { configs })
320
310
 
321
- await events.emit('lifecycle:start', version)
311
+ await hooks.emit('kubb:lifecycle:start', { version })
322
312
 
323
313
  for (const config of configs) {
324
314
  if (isInputPath(config) && watch) {
325
315
  await startWatcher([input || config.input.path], async (paths) => {
326
316
  // remove to avoid duplicate listeners after each change
327
- events.removeAll()
317
+ hooks.removeAll()
328
318
 
329
- await generate({ input, config, logLevel, events })
319
+ await generate({ input, config, logLevel, hooks })
330
320
 
331
321
  clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
332
322
  })
333
323
  } else {
334
- await generate({ input, config, logLevel, events })
324
+ await generate({ input, config, logLevel, hooks })
335
325
  }
336
326
  }
337
327
 
338
- await events.emit('lifecycle:end')
328
+ await hooks.emit('kubb:lifecycle:end')
339
329
  } catch (error) {
340
- await events.emit('error', toError(error))
330
+ await hooks.emit('kubb:error', { error: toError(error) })
341
331
  process.exit(1)
342
332
  }
343
333
  }
@@ -5,7 +5,7 @@ import { styleText } from 'node:util'
5
5
  import * as clack from '@clack/prompts'
6
6
  import type { PackageManagerInfo } from '@internals/utils'
7
7
  import { detectPackageManager } from '@internals/utils'
8
- import { initDefaults, pluginDefaultConfigs } from '../constants.ts'
8
+ import { initDefaults, KUBB_CONFIG_FILENAME, pluginDefaultConfigs } from '../constants.ts'
9
9
  import { hasPackageJson, initPackageJson, installPackages } from '../utils/packageManager.ts'
10
10
 
11
11
  type PluginOption = {
@@ -123,7 +123,7 @@ function generateConfigFile(selectedPlugins: PluginOption[], inputPath: string,
123
123
  })
124
124
  .join('\n')
125
125
 
126
- return `import { defineConfig } from '@kubb/core'
126
+ return `import { defineConfig } from 'kubb'
127
127
  ${imports}
128
128
 
129
129
  export default defineConfig({
@@ -257,7 +257,7 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
257
257
  }
258
258
 
259
259
  // Install packages
260
- const packagesToInstall = ['@kubb/core', '@kubb/cli', '@kubb/agent', ...selectedPlugins.map((p) => p.packageName)]
260
+ const packagesToInstall = ['kubb', ...selectedPlugins.map((p) => p.packageName)]
261
261
 
262
262
  const spinner = clack.spinner()
263
263
  spinner.start(`Installing ${packagesToInstall.length} packages with ${packageManager.name}`)
@@ -272,17 +272,17 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
272
272
 
273
273
  // Generate config file
274
274
  const configSpinner = clack.spinner()
275
- configSpinner.start('Creating kubb.config.ts')
275
+ configSpinner.start(`Creating ${KUBB_CONFIG_FILENAME}`)
276
276
 
277
277
  const configContent = generateConfigFile(selectedPlugins, inputPath, outputPath)
278
- const configPath = path.join(cwd, 'kubb.config.ts')
278
+ const configPath = path.join(cwd, KUBB_CONFIG_FILENAME)
279
279
 
280
280
  if (fs.existsSync(configPath)) {
281
- configSpinner.stop('kubb.config.ts already exists')
281
+ configSpinner.stop(`${KUBB_CONFIG_FILENAME} already exists`)
282
282
 
283
283
  if (!yes) {
284
284
  const shouldOverwrite = await clack.confirm({
285
- message: 'kubb.config.ts already exists. Overwrite?',
285
+ message: `${KUBB_CONFIG_FILENAME} already exists. Overwrite?`,
286
286
  initialValue: false,
287
287
  })
288
288
 
@@ -291,12 +291,12 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
291
291
  }
292
292
  }
293
293
 
294
- configSpinner.start('Overwriting kubb.config.ts')
294
+ configSpinner.start(`Overwriting ${KUBB_CONFIG_FILENAME}`)
295
295
  }
296
296
 
297
297
  fs.writeFileSync(configPath, configContent, 'utf-8')
298
298
 
299
- configSpinner.stop('Created kubb.config.ts')
299
+ configSpinner.stop(`Created ${KUBB_CONFIG_FILENAME}`)
300
300
 
301
301
  clack.outro(
302
302
  styleText('green', '✓ All set!') +
@@ -2,7 +2,6 @@ import process from 'node:process'
2
2
  import { styleText } from 'node:util'
3
3
  import { getErrorMessage } from '@internals/utils'
4
4
  import type * as McpModule from '@kubb/mcp'
5
- import { jiti } from '../utils/jiti.ts'
6
5
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
7
6
 
8
7
  type McpOptions = {
@@ -12,7 +11,7 @@ type McpOptions = {
12
11
  export async function runMcp({ version }: McpOptions): Promise<void> {
13
12
  let mod: typeof McpModule
14
13
  try {
15
- mod = (await jiti.import('@kubb/mcp', { default: true })) as typeof McpModule
14
+ mod = (await import('@kubb/mcp')) as typeof McpModule
16
15
  } catch (_e) {
17
16
  console.error(`Import of '@kubb/mcp' is required to start the MCP server`)
18
17
  process.exit(1)
@@ -24,9 +23,23 @@ export async function runMcp({ version }: McpOptions): Promise<void> {
24
23
  console.log('⏳ Starting MCP server...')
25
24
  console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
26
25
  run()
27
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'success' }))
26
+ await sendTelemetry(
27
+ buildTelemetryEvent({
28
+ command: 'mcp',
29
+ kubbVersion: version,
30
+ hrStart,
31
+ status: 'success',
32
+ }),
33
+ )
28
34
  } catch (error) {
29
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'failed' }))
35
+ await sendTelemetry(
36
+ buildTelemetryEvent({
37
+ command: 'mcp',
38
+ kubbVersion: version,
39
+ hrStart,
40
+ status: 'failed',
41
+ }),
42
+ )
30
43
  console.error(getErrorMessage(error))
31
44
  }
32
45
  }
@@ -1,7 +1,6 @@
1
1
  import process from 'node:process'
2
2
  import { getErrorMessage } from '@internals/utils'
3
- import type * as OasModule from '@kubb/oas'
4
- import { jiti } from '../utils/jiti.ts'
3
+ import { parseDocument, validateDocument } from '@kubb/adapter-oas'
5
4
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
6
5
 
7
6
  type ValidateOptions = {
@@ -10,24 +9,29 @@ type ValidateOptions = {
10
9
  }
11
10
 
12
11
  export async function runValidate({ input, version }: ValidateOptions): Promise<void> {
13
- let mod: typeof OasModule
14
- try {
15
- mod = (await jiti.import('@kubb/oas', { default: true })) as typeof OasModule
16
- } catch (_e) {
17
- console.error(`Import of '@kubb/oas' is required to do validation`)
18
- process.exit(1)
19
- }
20
-
21
- const { parse } = mod
22
12
  const hrStart = process.hrtime()
23
13
  try {
24
- const oas = await parse(input)
25
- await oas.validate()
14
+ const document = await parseDocument(input)
15
+ await validateDocument(document, { throwOnError: true })
26
16
 
27
- await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'success' }))
17
+ await sendTelemetry(
18
+ buildTelemetryEvent({
19
+ command: 'validate',
20
+ kubbVersion: version,
21
+ hrStart,
22
+ status: 'success',
23
+ }),
24
+ )
28
25
  console.log('✅ Validation success')
29
26
  } catch (error) {
30
- await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'failed' }))
27
+ await sendTelemetry(
28
+ buildTelemetryEvent({
29
+ command: 'validate',
30
+ kubbVersion: version,
31
+ hrStart,
32
+ status: 'failed',
33
+ }),
34
+ )
31
35
  console.error('❌ Validation failed')
32
36
  console.error(getErrorMessage(error))
33
37
  process.exit(1)
package/src/types.ts ADDED
@@ -0,0 +1,11 @@
1
+ export type QuiteFlag = '--help' | '-h' | '--version' | '-v'
2
+
3
+ export type GenerateFlag = '--config' | '-c' | '--log-level' | '-l' | '--watch' | '-w' | '--debug' | '-d' | '--verbose' | '-v' | '--silent' | '-s'
4
+
5
+ export type ValidateFlag = '--input' | '-i'
6
+
7
+ export type InitFlag = '--yes' | '-y'
8
+
9
+ export type AgentStartFlag = '--config' | '-c' | '--port' | '-p' | '--host' | '--allow-write' | '--allow-all'
10
+
11
+ export type Arg = QuiteFlag | GenerateFlag | ValidateFlag | InitFlag | AgentStartFlag
@@ -2,15 +2,15 @@ import { createHash } from 'node:crypto'
2
2
  import { styleText } from 'node:util'
3
3
  import type { AsyncEventEmitter } from '@internals/utils'
4
4
  import { tokenize } from '@internals/utils'
5
- import type { Config, KubbEvents } from '@kubb/core'
5
+ import type { Config, KubbHookEndContext, KubbHooks } from '@kubb/core'
6
6
 
7
7
  type ExecutingHooksProps = {
8
- hooks: NonNullable<Config['hooks']>
9
- events: AsyncEventEmitter<KubbEvents>
8
+ configHooks: NonNullable<Config['hooks']>
9
+ hooks: AsyncEventEmitter<KubbHooks>
10
10
  }
11
11
 
12
- export async function executeHooks({ hooks, events }: ExecutingHooksProps): Promise<void> {
13
- const commands = Array.isArray(hooks.done) ? hooks.done : [hooks.done].filter(Boolean)
12
+ export async function executeHooks({ configHooks, hooks }: ExecutingHooksProps): Promise<void> {
13
+ const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean)
14
14
 
15
15
  for (const command of commands) {
16
16
  const [cmd, ...args] = tokenize(command)
@@ -22,24 +22,24 @@ export async function executeHooks({ hooks, events }: ExecutingHooksProps): Prom
22
22
  const hookId = createHash('sha256').update(command).digest('hex')
23
23
 
24
24
  // Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
25
- // where hook:end fires synchronously inside emit('hook:start') before the listener is registered.
25
+ // where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
26
26
  const hookEndPromise = new Promise<void>((resolve, reject) => {
27
- const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
28
- if (id !== hookId) return
29
- events.off('hook:end', handler)
30
- if (!success) {
31
- reject(error ?? new Error(`Hook failed: ${command}`))
27
+ const handler = (ctx: KubbHookEndContext) => {
28
+ if (ctx.id !== hookId) return
29
+ hooks.off('kubb:hook:end', handler)
30
+ if (!ctx.success) {
31
+ reject(ctx.error ?? new Error(`Hook failed: ${command}`))
32
32
  return
33
33
  }
34
- events
35
- .emit('success', `${styleText('dim', command)} successfully executed`)
34
+ hooks
35
+ .emit('kubb:success', { message: `${styleText('dim', command)} successfully executed` })
36
36
  .then(resolve)
37
37
  .catch(reject)
38
38
  }
39
- events.on('hook:end', handler)
39
+ hooks.on('kubb:hook:end', handler)
40
40
  })
41
41
 
42
- await events.emit('hook:start', { id: hookId, command: cmd, args })
42
+ await hooks.emit('kubb:hook:start', { id: hookId, command: cmd, args })
43
43
  await hookEndPromise
44
44
  }
45
45
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Type guard that checks whether a raw string is a member of a typed flag set.
3
+ * Avoids the need for type assertions when working with `Set<T extends string>`.
4
+ */
5
+ export function isFlag<T extends string>(set: ReadonlySet<T>, value: string): value is T {
6
+ for (const flag of set) {
7
+ if (flag === value) return true
8
+ }
9
+ return false
10
+ }
@@ -0,0 +1,10 @@
1
+ import type { CLIOptions, Config, PossibleConfig } from '@kubb/core'
2
+
3
+ type ConfigInput = PossibleConfig<CLIOptions>
4
+
5
+ export async function getConfigs(config: ConfigInput, args: CLIOptions): Promise<Array<Config>> {
6
+ const resolved = await (typeof config === 'function' ? config(args as CLIOptions) : config)
7
+ const userConfigs = Array.isArray(resolved) ? resolved : [resolved]
8
+
9
+ return userConfigs.map((item) => ({ ...item, plugins: item.plugins ?? [] }) as Config)
10
+ }