@kubb/cli 5.0.0-alpha.5 → 5.0.0-alpha.51

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-Bt-Z24BD.cjs +116 -0
  4. package/dist/agent-Bt-Z24BD.cjs.map +1 -0
  5. package/dist/{agent-CEHZSBVT.cjs → agent-CEY6yeqU.cjs} +5 -5
  6. package/dist/agent-CEY6yeqU.cjs.map +1 -0
  7. package/dist/agent-LgBdb1gx.js +112 -0
  8. package/dist/agent-LgBdb1gx.js.map +1 -0
  9. package/dist/{agent-SWUT_sgq.js → agent-NAz3U695.js} +5 -5
  10. package/dist/agent-NAz3U695.js.map +1 -0
  11. package/dist/{constants-BTUap0zs.cjs → constants-B2q7VPbi.cjs} +91 -9
  12. package/dist/constants-B2q7VPbi.cjs.map +1 -0
  13. package/dist/constants-T_2rsi7T.js +137 -0
  14. package/dist/constants-T_2rsi7T.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-CfoLoEd0.js → generate-BspZWGKE.js} +3 -3
  24. package/dist/generate-BspZWGKE.js.map +1 -0
  25. package/dist/{generate-CNkdrX6u.cjs → generate-DaqmCSQU.cjs} +3 -3
  26. package/dist/generate-DaqmCSQU.cjs.map +1 -0
  27. package/dist/{generate-CtFkZeTU.js → generate-L7N_8n1y.js} +493 -248
  28. package/dist/generate-L7N_8n1y.js.map +1 -0
  29. package/dist/{generate-nJaqPdGL.cjs → generate-uiU5r6nt.cjs} +500 -255
  30. package/dist/generate-uiU5r6nt.cjs.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-hmolV6B4.cjs → init-AVT-2Uxi.cjs} +31 -21
  37. package/dist/init-AVT-2Uxi.cjs.map +1 -0
  38. package/dist/{init-C-InrmSY.js → init-BecEYFRb.js} +27 -17
  39. package/dist/init-BecEYFRb.js.map +1 -0
  40. package/dist/{init-D_oERuMn.js → init-DyKEzT4F.js} +4 -4
  41. package/dist/init-DyKEzT4F.js.map +1 -0
  42. package/dist/{init-BAs1d5rK.cjs → init-SBJcdXR6.cjs} +4 -4
  43. package/dist/init-SBJcdXR6.cjs.map +1 -0
  44. package/dist/{mcp-DEvDB6FY.cjs → mcp-1vHQuAyo.cjs} +4 -4
  45. package/dist/{mcp-DEvDB6FY.cjs.map → mcp-1vHQuAyo.cjs.map} +1 -1
  46. package/dist/{mcp-D2SHEg_d.js → mcp-BDHEbuy3.js} +11 -4
  47. package/dist/mcp-BDHEbuy3.js.map +1 -0
  48. package/dist/{mcp-ChHFPRzD.cjs → mcp-CqpVw3PO.cjs} +12 -6
  49. package/dist/mcp-CqpVw3PO.cjs.map +1 -0
  50. package/dist/{mcp-jKYiomZT.js → mcp-k4i3z_Ug.js} +4 -4
  51. package/dist/{mcp-jKYiomZT.js.map → mcp-k4i3z_Ug.js.map} +1 -1
  52. package/dist/{package-C-fC2Eoy.cjs → package-BwQBf-kE.cjs} +2 -2
  53. package/dist/package-BwQBf-kE.cjs.map +1 -0
  54. package/dist/package-CXcGIpWz.js +6 -0
  55. package/dist/package-CXcGIpWz.js.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-Cn9X1I5B.cjs → telemetry-B6ZbZOrb.cjs} +50 -8
  61. package/dist/telemetry-B6ZbZOrb.cjs.map +1 -0
  62. package/dist/{telemetry-DxiR7clS.js → telemetry-Dis5nWR_.js} +48 -6
  63. package/dist/telemetry-Dis5nWR_.js.map +1 -0
  64. package/dist/{validate--IXRJEhp.js → validate-C2n4-h7x.js} +4 -4
  65. package/dist/validate-C2n4-h7x.js.map +1 -0
  66. package/dist/{validate-Bbrn3Q-A.cjs → validate-D-8_wAFV.cjs} +6 -14
  67. package/dist/validate-D-8_wAFV.cjs.map +1 -0
  68. package/dist/{validate-BK3pc8zv.cjs → validate-Dx1S1gng.cjs} +4 -4
  69. package/dist/validate-Dx1S1gng.cjs.map +1 -0
  70. package/dist/{validate-l8vLmwKA.js → validate-GfErHsnI.js} +5 -13
  71. package/dist/validate-GfErHsnI.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 +65 -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 -109
  85. package/src/runners/init.ts +9 -9
  86. package/src/runners/mcp.ts +19 -3
  87. package/src/runners/validate.ts +19 -15
  88. package/src/types.ts +11 -0
  89. package/src/utils/executeHooks.ts +11 -11
  90. package/src/utils/flags.ts +10 -0
  91. package/src/utils/getConfig.ts +10 -0
  92. package/src/utils/getCosmiConfig.ts +9 -3
  93. package/src/utils/getSummary.ts +1 -1
  94. package/src/utils/runHook.ts +27 -9
  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-CEHZSBVT.cjs.map +0 -1
  100. package/dist/agent-L50VNhXv.js +0 -88
  101. package/dist/agent-L50VNhXv.js.map +0 -1
  102. package/dist/agent-SWUT_sgq.js.map +0 -1
  103. package/dist/constants-BTUap0zs.cjs.map +0 -1
  104. package/dist/constants-CM3dJzjK.js +0 -67
  105. package/dist/constants-CM3dJzjK.js.map +0 -1
  106. package/dist/define--M_JMcDC.js +0 -25
  107. package/dist/define--M_JMcDC.js.map +0 -1
  108. package/dist/define-D6Kfm7-Z.cjs +0 -36
  109. package/dist/define-D6Kfm7-Z.cjs.map +0 -1
  110. package/dist/errors-6mF_WKxg.js +0 -27
  111. package/dist/errors-6mF_WKxg.js.map +0 -1
  112. package/dist/errors-DBW0N9w4.cjs.map +0 -1
  113. package/dist/generate-CNkdrX6u.cjs.map +0 -1
  114. package/dist/generate-CfoLoEd0.js.map +0 -1
  115. package/dist/generate-CtFkZeTU.js.map +0 -1
  116. package/dist/generate-nJaqPdGL.cjs.map +0 -1
  117. package/dist/init-BAs1d5rK.cjs.map +0 -1
  118. package/dist/init-C-InrmSY.js.map +0 -1
  119. package/dist/init-D_oERuMn.js.map +0 -1
  120. package/dist/init-hmolV6B4.cjs.map +0 -1
  121. package/dist/jiti-Cd3S0xwr.cjs +0 -16
  122. package/dist/jiti-Cd3S0xwr.cjs.map +0 -1
  123. package/dist/jiti-e08mD2Ph.js +0 -11
  124. package/dist/jiti-e08mD2Ph.js.map +0 -1
  125. package/dist/mcp-ChHFPRzD.cjs.map +0 -1
  126. package/dist/mcp-D2SHEg_d.js.map +0 -1
  127. package/dist/package-C-fC2Eoy.cjs.map +0 -1
  128. package/dist/package-DxH_MEG4.js +0 -6
  129. package/dist/package-DxH_MEG4.js.map +0 -1
  130. package/dist/shell-7HPrTCJ5.cjs.map +0 -1
  131. package/dist/shell-DqqWsHCD.js.map +0 -1
  132. package/dist/telemetry-Cn9X1I5B.cjs.map +0 -1
  133. package/dist/telemetry-DxiR7clS.js.map +0 -1
  134. package/dist/validate--IXRJEhp.js.map +0 -1
  135. package/dist/validate-BK3pc8zv.cjs.map +0 -1
  136. package/dist/validate-Bbrn3Q-A.cjs.map +0 -1
  137. package/dist/validate-l8vLmwKA.js.map +0 -1
  138. package/src/utils/getIntro.ts +0 -1
@@ -4,26 +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
- PromiseManager,
20
- safeBuild,
21
- setup,
22
- } 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'
23
9
  import { version } from '../../package.json'
24
10
  import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
25
11
  import { setupLogger } from '../loggers/utils.ts'
26
12
  import { executeHooks } from '../utils/executeHooks.ts'
13
+ import { getConfigs } from '../utils/getConfig.ts'
27
14
  import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
28
15
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
29
16
  import { startWatcher } from '../utils/watcher.ts'
@@ -31,7 +18,7 @@ import { startWatcher } from '../utils/watcher.ts'
31
18
  type GenerateProps = {
32
19
  input?: string
33
20
  config: Config
34
- events: AsyncEventEmitter<KubbEvents>
21
+ hooks: AsyncEventEmitter<KubbHooks>
35
22
  logLevel: number
36
23
  }
37
24
 
@@ -39,7 +26,7 @@ type ToolMap = typeof formatters | typeof linters
39
26
 
40
27
  type RunToolPassOptions = {
41
28
  toolValue: string
42
- detect: () => Promise<string | undefined>
29
+ detect: () => Promise<string | null>
43
30
  toolMap: ToolMap
44
31
  /** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
45
32
  toolLabel: string
@@ -49,7 +36,7 @@ type RunToolPassOptions = {
49
36
  configName: string | undefined
50
37
  outputPath: string
51
38
  logLevel: number
52
- events: AsyncEventEmitter<KubbEvents>
39
+ hooks: AsyncEventEmitter<KubbHooks>
53
40
  onStart: () => Promise<void>
54
41
  onEnd: () => Promise<void>
55
42
  }
@@ -64,7 +51,7 @@ async function runToolPass({
64
51
  configName,
65
52
  outputPath,
66
53
  logLevel,
67
- events,
54
+ hooks,
68
55
  onStart,
69
56
  onEnd,
70
57
  }: RunToolPassOptions) {
@@ -74,13 +61,15 @@ async function runToolPass({
74
61
  if (resolvedTool === 'auto') {
75
62
  const detected = await detect()
76
63
  if (!detected) {
77
- await events.emit('warn', noToolMessage)
64
+ await hooks.emit('kubb:warn', noToolMessage)
78
65
  } else {
79
66
  resolvedTool = detected
80
- await events.emit('info', `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}`)
67
+ await hooks.emit('kubb:info', `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}`)
81
68
  }
82
69
  }
83
70
 
71
+ let toolError: Error | undefined
72
+
84
73
  if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
85
74
  const toolConfig = toolMap[resolvedTool as keyof ToolMap]
86
75
 
@@ -88,18 +77,18 @@ async function runToolPass({
88
77
  const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
89
78
 
90
79
  // Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
91
- // 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.
92
81
  const hookEndPromise = new Promise<void>((resolve, reject) => {
93
82
  const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
94
83
  if (id !== hookId) return
95
- events.off('hook:end', handler)
84
+ hooks.off('kubb:hook:end', handler)
96
85
  if (!success) {
97
86
  reject(error ?? new Error(`${toolConfig.errorMessage}`))
98
87
  return
99
88
  }
100
- events
89
+ hooks
101
90
  .emit(
102
- 'success',
91
+ 'kubb:success',
103
92
  [
104
93
  `${successPrefix} with ${styleText('dim', resolvedTool)}`,
105
94
  logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
@@ -111,10 +100,10 @@ async function runToolPass({
111
100
  .then(resolve)
112
101
  .catch(reject)
113
102
  }
114
- events.on('hook:end', handler)
103
+ hooks.on('kubb:hook:end', handler)
115
104
  })
116
105
 
117
- await events.emit('hook:start', {
106
+ await hooks.emit('kubb:hook:start', {
118
107
  id: hookId,
119
108
  command: toolConfig.command,
120
109
  args: toolConfig.args(outputPath),
@@ -122,59 +111,52 @@ async function runToolPass({
122
111
 
123
112
  await hookEndPromise
124
113
  } catch (caughtError) {
125
- const err = new Error(toolConfig.errorMessage)
126
- err.cause = caughtError
127
- await events.emit('error', err)
114
+ // Use the actual error from the hook. toolConfig.errorMessage (e.g. "Oxlint not found")
115
+ // is misleading when the binary was found and ran but exited with a non-zero code.
116
+ // runHook already emitted kubb:error for binary-not-found cases; here we surface the
117
+ // real reason (e.g. "Hook execute failed: oxlint --fix …") for non-zero exits.
118
+ const err = toError(caughtError)
119
+ await hooks.emit('kubb:error', err)
120
+ toolError = err
128
121
  }
129
122
  }
130
123
 
131
124
  await onEnd()
125
+
126
+ if (toolError) {
127
+ throw toolError
128
+ }
132
129
  }
133
130
 
134
- async function generate({ input, config: userConfig, events, logLevel }: GenerateProps): Promise<void> {
135
- const inputPath = input ?? ('path' in userConfig.input ? userConfig.input.path : undefined)
131
+ async function generate(options: GenerateProps): Promise<void> {
132
+ const { input, hooks, logLevel } = options
133
+
136
134
  const hrStart = process.hrtime()
135
+ const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
137
136
 
138
137
  const config: Config = {
139
- ...userConfig,
140
- root: userConfig.root || process.cwd(),
138
+ ...options.config,
141
139
  input: inputPath
142
140
  ? {
143
- ...userConfig.input,
141
+ ...options.config.input,
144
142
  path: inputPath,
145
143
  }
146
- : userConfig.input,
147
- output: {
148
- write: true,
149
- barrelType: 'named',
150
- extension: {
151
- '.ts': '.ts',
152
- },
153
- format: 'prettier',
154
- ...userConfig.output,
155
- },
156
- }
144
+ : options.config.input,
145
+ ...options.config.output,
146
+ } satisfies Config
157
147
 
158
- await events.emit('generation:start', config)
148
+ const kubb = createKubb(config, { hooks })
149
+ await kubb.setup()
159
150
 
160
- await events.emit('info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
151
+ await hooks.emit('kubb:generation:start', config)
161
152
 
162
- const { sources, fabric, pluginManager } = await setup({
163
- config,
164
- events,
165
- })
153
+ await hooks.emit('kubb:info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
166
154
 
167
- await events.emit('info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
155
+ await hooks.emit('kubb:info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
168
156
 
169
- const { files, failedPlugins, pluginTimings, error } = await safeBuild(
170
- {
171
- config,
172
- events,
173
- },
174
- { pluginManager, fabric, events, sources },
175
- )
157
+ const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
176
158
 
177
- await events.emit('info', 'Load summary')
159
+ await hooks.emit('kubb:info', 'Load summary')
178
160
 
179
161
  // Handle build failures (either from failed plugins or general errors)
180
162
 
@@ -189,12 +171,12 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
189
171
  ].filter(Boolean)
190
172
 
191
173
  for (const err of allErrors) {
192
- await events.emit('error', err)
174
+ await hooks.emit('kubb:error', err)
193
175
  }
194
176
 
195
- await events.emit('generation:end', config, files, sources)
177
+ await hooks.emit('kubb:generation:end', config, files, kubb.sources)
196
178
 
197
- await events.emit('generation:summary', config, {
179
+ await hooks.emit('kubb:generation:summary', config, {
198
180
  failedPlugins,
199
181
  filesCreated: files.length,
200
182
  status: 'failed',
@@ -206,7 +188,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
206
188
  buildTelemetryEvent({
207
189
  command: 'generate',
208
190
  kubbVersion: version,
209
- plugins: pluginManager.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
+ })),
210
195
  hrStart,
211
196
  filesCreated: files.length,
212
197
  status: 'failed',
@@ -216,8 +201,8 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
216
201
  process.exit(1)
217
202
  }
218
203
 
219
- await events.emit('success', 'Generation successfully', inputPath)
220
- await events.emit('generation:end', config, files, sources)
204
+ await hooks.emit('kubb:success', 'Generation successfully', inputPath)
205
+ await hooks.emit('kubb:generation:end', config, files, kubb.sources)
221
206
 
222
207
  const outputPath = path.resolve(config.root, config.output.path)
223
208
 
@@ -228,13 +213,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
228
213
  toolMap: formatters,
229
214
  toolLabel: 'formatter',
230
215
  successPrefix: 'Formatting',
231
- noToolMessage: 'No formatter found (biome, prettier, or oxfmt). Skipping formatting.',
216
+ noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
232
217
  configName: config.name,
233
218
  outputPath,
234
219
  logLevel,
235
- events,
236
- onStart: () => events.emit('format:start'),
237
- onEnd: () => events.emit('format:end'),
220
+ hooks,
221
+ onStart: () => hooks.emit('kubb:format:start'),
222
+ onEnd: () => hooks.emit('kubb:format:end'),
238
223
  })
239
224
  }
240
225
 
@@ -245,25 +230,25 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
245
230
  toolMap: linters,
246
231
  toolLabel: 'linter',
247
232
  successPrefix: 'Linting',
248
- noToolMessage: 'No linter found (biome, oxlint, or eslint). Skipping linting.',
233
+ noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
249
234
  configName: config.name,
250
235
  outputPath,
251
236
  logLevel,
252
- events,
253
- onStart: () => events.emit('lint:start'),
254
- onEnd: () => events.emit('lint:end'),
237
+ hooks,
238
+ onStart: () => hooks.emit('kubb:lint:start'),
239
+ onEnd: () => hooks.emit('kubb:lint:end'),
255
240
  })
256
241
  }
257
242
 
258
243
  if (config.hooks) {
259
- await events.emit('hooks:start')
260
- await executeHooks({ hooks: config.hooks, events })
244
+ await hooks.emit('kubb:hooks:start')
245
+ await executeHooks({ configHooks: config.hooks, hooks })
261
246
 
262
- await events.emit('hooks:end')
247
+ await hooks.emit('kubb:hooks:end')
263
248
  }
264
249
 
265
250
  // Only reached when there are no failures (process.exit(1) is called above otherwise)
266
- await events.emit('generation:summary', config, {
251
+ await hooks.emit('kubb:generation:summary', config, {
267
252
  failedPlugins,
268
253
  filesCreated: files.length,
269
254
  status: 'success',
@@ -274,7 +259,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
274
259
  const telemetryEvent = buildTelemetryEvent({
275
260
  command: 'generate',
276
261
  kubbVersion: version,
277
- plugins: pluginManager.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
262
+ plugins: Array.from(driver.plugins.values(), (p) => ({
263
+ name: p.name,
264
+ options: p.options as Record<string, unknown>,
265
+ })),
278
266
  hrStart,
279
267
  filesCreated: files.length,
280
268
  status: 'success',
@@ -292,10 +280,9 @@ type GenerateCommandOptions = {
292
280
 
293
281
  export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
294
282
  const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
295
- const events = new AsyncEventEmitterClass<KubbEvents>()
296
- const promiseManager = new PromiseManager()
283
+ const hooks = new AsyncEventEmitterClass<KubbHooks>()
297
284
 
298
- await setupLogger(events, { logLevel })
285
+ await setupLogger(hooks, { logLevel })
299
286
 
300
287
  await executeIfOnline(async () => {
301
288
  try {
@@ -304,7 +291,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
304
291
  const latestVersion = data.version
305
292
 
306
293
  if (latestVersion && version < latestVersion) {
307
- await events.emit('version:new', version, latestVersion)
294
+ await hooks.emit('kubb:version:new', version, latestVersion)
308
295
  }
309
296
  } catch {
310
297
  // Ignore network errors for version check
@@ -315,37 +302,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
315
302
  const result = await getCosmiConfig('kubb', configPath)
316
303
  const configs = await getConfigs(result.config, { input } as CLIOptions)
317
304
 
318
- await events.emit('config:start')
319
- await events.emit('info', 'Config loaded', path.relative(process.cwd(), result.filepath))
320
- await events.emit('success', 'Config loaded successfully', path.relative(process.cwd(), result.filepath))
321
- await events.emit('config:end', configs)
305
+ await hooks.emit('kubb:config:start')
306
+ await hooks.emit('kubb:info', 'Config loaded', path.relative(process.cwd(), result.filepath))
307
+ await hooks.emit('kubb:success', 'Config loaded successfully', path.relative(process.cwd(), result.filepath))
308
+ await hooks.emit('kubb:config:end', configs)
322
309
 
323
- await events.emit('lifecycle:start', version)
310
+ await hooks.emit('kubb:lifecycle:start', version)
324
311
 
325
- const promises = configs.map((config) => {
326
- return async () => {
327
- if (isInputPath(config) && watch) {
328
- await startWatcher([input || config.input.path], async (paths) => {
329
- // remove to avoid duplicate listeners after each change
330
- events.removeAll()
312
+ for (const config of configs) {
313
+ if (isInputPath(config) && watch) {
314
+ await startWatcher([input || config.input.path], async (paths) => {
315
+ // remove to avoid duplicate listeners after each change
316
+ hooks.removeAll()
331
317
 
332
- await generate({ input, config, logLevel, events })
318
+ await generate({ input, config, logLevel, hooks })
333
319
 
334
- clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
335
- })
336
-
337
- return
338
- }
339
-
340
- await generate({ input, config, logLevel, events })
320
+ clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
321
+ })
322
+ } else {
323
+ await generate({ input, config, logLevel, hooks })
341
324
  }
342
- })
343
-
344
- await promiseManager.run('seq', promises)
325
+ }
345
326
 
346
- await events.emit('lifecycle:end')
327
+ await hooks.emit('kubb:lifecycle:end')
347
328
  } catch (error) {
348
- await events.emit('error', toError(error))
329
+ await hooks.emit('kubb:error', toError(error))
349
330
  process.exit(1)
350
331
  }
351
332
  }
@@ -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!') +
@@ -12,7 +12,9 @@ type McpOptions = {
12
12
  export async function runMcp({ version }: McpOptions): Promise<void> {
13
13
  let mod: typeof McpModule
14
14
  try {
15
- mod = (await jiti.import('@kubb/mcp', { default: true })) as typeof McpModule
15
+ mod = (await jiti.import('@kubb/mcp', {
16
+ default: true,
17
+ })) as typeof McpModule
16
18
  } catch (_e) {
17
19
  console.error(`Import of '@kubb/mcp' is required to start the MCP server`)
18
20
  process.exit(1)
@@ -24,9 +26,23 @@ export async function runMcp({ version }: McpOptions): Promise<void> {
24
26
  console.log('⏳ Starting MCP server...')
25
27
  console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
26
28
  run()
27
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'success' }))
29
+ await sendTelemetry(
30
+ buildTelemetryEvent({
31
+ command: 'mcp',
32
+ kubbVersion: version,
33
+ hrStart,
34
+ status: 'success',
35
+ }),
36
+ )
28
37
  } catch (error) {
29
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'failed' }))
38
+ await sendTelemetry(
39
+ buildTelemetryEvent({
40
+ command: 'mcp',
41
+ kubbVersion: version,
42
+ hrStart,
43
+ status: 'failed',
44
+ }),
45
+ )
30
46
  console.error(getErrorMessage(error))
31
47
  }
32
48
  }
@@ -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, 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
27
  const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
28
28
  if (id !== hookId) return
29
- events.off('hook:end', handler)
29
+ hooks.off('kubb:hook:end', handler)
30
30
  if (!success) {
31
31
  reject(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', `${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
+ }