@kubb/cli 5.0.0-beta.4 → 5.0.0-beta.40

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 (155) hide show
  1. package/README.md +198 -36
  2. package/dist/agent-DQerNyd8.cjs +70 -0
  3. package/dist/agent-DQerNyd8.cjs.map +1 -0
  4. package/dist/agent-ZKJkTTGN.js +68 -0
  5. package/dist/agent-ZKJkTTGN.js.map +1 -0
  6. package/dist/{chunk--u3MIqq1.js → chunk-CRm0XQPb.js} +1 -0
  7. package/dist/constants-84a47qA-.js +35 -0
  8. package/dist/constants-84a47qA-.js.map +1 -0
  9. package/dist/constants-AHhyFH15.cjs +139 -0
  10. package/dist/constants-AHhyFH15.cjs.map +1 -0
  11. package/dist/constants-BtmponZ3.cjs +58 -0
  12. package/dist/constants-BtmponZ3.cjs.map +1 -0
  13. package/dist/constants-C94RKp3A.js +116 -0
  14. package/dist/constants-C94RKp3A.js.map +1 -0
  15. package/dist/{define-Bdn8j5VM.cjs → define-C4AB3POr.cjs} +2 -2
  16. package/dist/{define-Bdn8j5VM.cjs.map → define-C4AB3POr.cjs.map} +1 -1
  17. package/dist/{define-Ctii4bel.js → define-DNG1U8ha.js} +2 -2
  18. package/dist/{define-Ctii4bel.js.map → define-DNG1U8ha.js.map} +1 -1
  19. package/dist/{errors-CjPmyZHy.js → errors-CoxrNXaA.js} +2 -2
  20. package/dist/{errors-CjPmyZHy.js.map → errors-CoxrNXaA.js.map} +1 -1
  21. package/dist/{errors-CLCjoSg0.cjs → errors-DykI11xo.cjs} +2 -2
  22. package/dist/{errors-CLCjoSg0.cjs.map → errors-DykI11xo.cjs.map} +1 -1
  23. package/dist/generate-40x9PP4o.js +77 -0
  24. package/dist/generate-40x9PP4o.js.map +1 -0
  25. package/dist/generate-DQLvFw4z.cjs +76 -0
  26. package/dist/generate-DQLvFw4z.cjs.map +1 -0
  27. package/dist/index.cjs +23 -14
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +23 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/init-CT9RChdK.js +53 -0
  33. package/dist/init-CT9RChdK.js.map +1 -0
  34. package/dist/init-sEaUN7Dj.cjs +53 -0
  35. package/dist/init-sEaUN7Dj.cjs.map +1 -0
  36. package/dist/mcp-Siyb6fTT.cjs +39 -0
  37. package/dist/mcp-Siyb6fTT.cjs.map +1 -0
  38. package/dist/mcp-cjPrOeot.js +39 -0
  39. package/dist/mcp-cjPrOeot.js.map +1 -0
  40. package/dist/{package-BapVyQ-w.cjs → package-BJ6qam2Y.cjs} +2 -2
  41. package/dist/package-BJ6qam2Y.cjs.map +1 -0
  42. package/dist/package-CR5vEK4K.js +6 -0
  43. package/dist/package-CR5vEK4K.js.map +1 -0
  44. package/dist/{agent-sdYBBgrd.js → run-BQ3Qj0xB.js} +46 -43
  45. package/dist/run-BQ3Qj0xB.js.map +1 -0
  46. package/dist/run-BQzoaxjR.js +32 -0
  47. package/dist/run-BQzoaxjR.js.map +1 -0
  48. package/dist/run-CGf0KEts.js +51 -0
  49. package/dist/run-CGf0KEts.js.map +1 -0
  50. package/dist/{generate-CNrRLY4n.js → run-CHZKHTv0.js} +832 -828
  51. package/dist/run-CHZKHTv0.js.map +1 -0
  52. package/dist/{init-CZ5Xq2Hd.cjs → run-C_NMctua.cjs} +107 -149
  53. package/dist/run-C_NMctua.cjs.map +1 -0
  54. package/dist/{generate-B1Pa2ho-.cjs → run-CcQawFNK.cjs} +784 -780
  55. package/dist/run-CcQawFNK.cjs.map +1 -0
  56. package/dist/run-CkTpemme.cjs +52 -0
  57. package/dist/run-CkTpemme.cjs.map +1 -0
  58. package/dist/run-Cl4SrSob.cjs +33 -0
  59. package/dist/run-Cl4SrSob.cjs.map +1 -0
  60. package/dist/{agent-B4cAAab2.cjs → run-D-s2LdlW.cjs} +46 -43
  61. package/dist/run-D-s2LdlW.cjs.map +1 -0
  62. package/dist/{init-eNRlotJK.js → run-D8dCWepS.js} +107 -149
  63. package/dist/run-D8dCWepS.js.map +1 -0
  64. package/dist/{shell-DLzN4fRo.js → shell-BrqyJdB7.js} +2 -2
  65. package/dist/{shell-DLzN4fRo.js.map → shell-BrqyJdB7.js.map} +1 -1
  66. package/dist/{shell-475fQKaX.cjs → shell-Lh-vLWwH.cjs} +2 -2
  67. package/dist/{shell-475fQKaX.cjs.map → shell-Lh-vLWwH.cjs.map} +1 -1
  68. package/dist/validate-8pgfxUTy.js +26 -0
  69. package/dist/validate-8pgfxUTy.js.map +1 -0
  70. package/dist/validate-CstV7Pc2.cjs +26 -0
  71. package/dist/validate-CstV7Pc2.cjs.map +1 -0
  72. package/package.json +16 -15
  73. package/src/commands/agent/start.ts +10 -7
  74. package/src/commands/agent.ts +3 -1
  75. package/src/commands/generate.ts +21 -13
  76. package/src/commands/init.ts +34 -3
  77. package/src/commands/mcp.ts +28 -4
  78. package/src/commands/validate.ts +6 -4
  79. package/src/constants.ts +3 -74
  80. package/src/index.ts +6 -4
  81. package/src/loggers/clackLogger.ts +143 -181
  82. package/src/loggers/githubActionsLogger.ts +119 -121
  83. package/src/loggers/plainLogger.ts +50 -100
  84. package/src/loggers/types.ts +6 -0
  85. package/src/loggers/utils.ts +190 -18
  86. package/src/runners/agent/run.ts +113 -0
  87. package/src/runners/agent/utils.ts +98 -0
  88. package/src/runners/generate/run.ts +411 -0
  89. package/src/runners/generate/utils.ts +222 -0
  90. package/src/runners/init/run.ts +212 -0
  91. package/src/{utils/packageManager.ts → runners/init/utils.ts} +12 -2
  92. package/src/runners/mcp/run.ts +37 -0
  93. package/src/runners/validate/run.ts +63 -0
  94. package/dist/agent-B4cAAab2.cjs.map +0 -1
  95. package/dist/agent-CR6Z96og.js +0 -56
  96. package/dist/agent-CR6Z96og.js.map +0 -1
  97. package/dist/agent-Dmxzqg4d.cjs +0 -58
  98. package/dist/agent-Dmxzqg4d.cjs.map +0 -1
  99. package/dist/agent-sdYBBgrd.js.map +0 -1
  100. package/dist/constants-CnDXa1R6.cjs +0 -148
  101. package/dist/constants-CnDXa1R6.cjs.map +0 -1
  102. package/dist/constants-aL3CP_Wq.js +0 -95
  103. package/dist/constants-aL3CP_Wq.js.map +0 -1
  104. package/dist/generate-B1Pa2ho-.cjs.map +0 -1
  105. package/dist/generate-BDGOOsBM.cjs +0 -65
  106. package/dist/generate-BDGOOsBM.cjs.map +0 -1
  107. package/dist/generate-CNrRLY4n.js.map +0 -1
  108. package/dist/generate-DuhxPLGr.js +0 -66
  109. package/dist/generate-DuhxPLGr.js.map +0 -1
  110. package/dist/init-CZ5Xq2Hd.cjs.map +0 -1
  111. package/dist/init-CnZXHrbq.js +0 -25
  112. package/dist/init-CnZXHrbq.js.map +0 -1
  113. package/dist/init-NYJSZJSb.cjs +0 -25
  114. package/dist/init-NYJSZJSb.cjs.map +0 -1
  115. package/dist/init-eNRlotJK.js.map +0 -1
  116. package/dist/mcp-CYOgxB82.cjs +0 -47
  117. package/dist/mcp-CYOgxB82.cjs.map +0 -1
  118. package/dist/mcp-CdFWyrwi.cjs +0 -16
  119. package/dist/mcp-CdFWyrwi.cjs.map +0 -1
  120. package/dist/mcp-DhSxuDMD.js +0 -16
  121. package/dist/mcp-DhSxuDMD.js.map +0 -1
  122. package/dist/mcp-DmJm3TrU.js +0 -46
  123. package/dist/mcp-DmJm3TrU.js.map +0 -1
  124. package/dist/package-BapVyQ-w.cjs.map +0 -1
  125. package/dist/package-DyJE-qNq.js +0 -6
  126. package/dist/package-DyJE-qNq.js.map +0 -1
  127. package/dist/telemetry-DN95_2pF.cjs +0 -282
  128. package/dist/telemetry-DN95_2pF.cjs.map +0 -1
  129. package/dist/telemetry-LgT_sdPe.js +0 -245
  130. package/dist/telemetry-LgT_sdPe.js.map +0 -1
  131. package/dist/validate-C6npXzel.cjs +0 -25
  132. package/dist/validate-C6npXzel.cjs.map +0 -1
  133. package/dist/validate-kLJoT_hi.js +0 -33
  134. package/dist/validate-kLJoT_hi.js.map +0 -1
  135. package/dist/validate-n38Rh-Y7.js +0 -25
  136. package/dist/validate-n38Rh-Y7.js.map +0 -1
  137. package/dist/validate-yKKzqEZ5.cjs +0 -34
  138. package/dist/validate-yKKzqEZ5.cjs.map +0 -1
  139. package/src/loggers/fileSystemLogger.ts +0 -138
  140. package/src/runners/agent.ts +0 -155
  141. package/src/runners/generate.ts +0 -333
  142. package/src/runners/init.ts +0 -296
  143. package/src/runners/mcp.ts +0 -51
  144. package/src/runners/validate.ts +0 -39
  145. package/src/types.ts +0 -11
  146. package/src/utils/Writables.ts +0 -17
  147. package/src/utils/executeHooks.ts +0 -45
  148. package/src/utils/flags.ts +0 -9
  149. package/src/utils/getConfig.ts +0 -10
  150. package/src/utils/getCosmiConfig.ts +0 -80
  151. package/src/utils/getSummary.ts +0 -68
  152. package/src/utils/runHook.ts +0 -91
  153. package/src/utils/telemetry.ts +0 -273
  154. package/src/utils/watcher.ts +0 -19
  155. /package/dist/{chunk-ByKO4r7w.cjs → chunk-Bx3C2hgW.cjs} +0 -0
@@ -1,39 +0,0 @@
1
- import process from 'node:process'
2
- import { getErrorMessage } from '@internals/utils'
3
- import { parseDocument, validateDocument } from '@kubb/adapter-oas'
4
- import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
5
-
6
- type ValidateOptions = {
7
- input: string
8
- version: string
9
- }
10
-
11
- export async function runValidate({ input, version }: ValidateOptions): Promise<void> {
12
- const hrStart = process.hrtime()
13
- try {
14
- const document = await parseDocument(input)
15
- await validateDocument(document, { throwOnError: true })
16
-
17
- await sendTelemetry(
18
- buildTelemetryEvent({
19
- command: 'validate',
20
- kubbVersion: version,
21
- hrStart,
22
- status: 'success',
23
- }),
24
- )
25
- console.log('✅ Validation success')
26
- } catch (error) {
27
- await sendTelemetry(
28
- buildTelemetryEvent({
29
- command: 'validate',
30
- kubbVersion: version,
31
- hrStart,
32
- status: 'failed',
33
- }),
34
- )
35
- console.error('❌ Validation failed')
36
- console.error(getErrorMessage(error))
37
- process.exit(1)
38
- }
39
- }
package/src/types.ts DELETED
@@ -1,11 +0,0 @@
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
@@ -1,17 +0,0 @@
1
- import type { WritableOptions } from 'node:stream'
2
- import { Writable } from 'node:stream'
3
- import { styleText } from 'node:util'
4
- import type * as clack from '@clack/prompts'
5
-
6
- export class ClackWritable extends Writable {
7
- taskLog: ReturnType<typeof clack.taskLog>
8
- constructor(taskLog: ReturnType<typeof clack.taskLog>, opts?: WritableOptions) {
9
- super(opts)
10
-
11
- this.taskLog = taskLog
12
- }
13
- _write(chunk: Buffer, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
14
- this.taskLog.message(`${styleText('dim', chunk.toString())}`)
15
- callback()
16
- }
17
- }
@@ -1,45 +0,0 @@
1
- import { createHash } from 'node:crypto'
2
- import { styleText } from 'node:util'
3
- import type { AsyncEventEmitter } from '@internals/utils'
4
- import { tokenize } from '@internals/utils'
5
- import type { Config, KubbHookEndContext, KubbHooks } from '@kubb/core'
6
-
7
- type ExecutingHooksProps = {
8
- configHooks: NonNullable<Config['hooks']>
9
- hooks: AsyncEventEmitter<KubbHooks>
10
- }
11
-
12
- export async function executeHooks({ configHooks, hooks }: ExecutingHooksProps): Promise<void> {
13
- const commands = Array.isArray(configHooks.done) ? configHooks.done : [configHooks.done].filter(Boolean)
14
-
15
- for (const command of commands) {
16
- const [cmd, ...args] = tokenize(command)
17
-
18
- if (!cmd) {
19
- continue
20
- }
21
-
22
- const hookId = createHash('sha256').update(command).digest('hex')
23
-
24
- // Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
25
- // where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
26
- const hookEndPromise = new Promise<void>((resolve, reject) => {
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
- return
33
- }
34
- hooks
35
- .emit('kubb:success', { message: `${styleText('dim', command)} successfully executed` })
36
- .then(resolve)
37
- .catch(reject)
38
- }
39
- hooks.on('kubb:hook:end', handler)
40
- })
41
-
42
- await hooks.emit('kubb:hook:start', { id: hookId, command: cmd, args })
43
- await hookEndPromise
44
- }
45
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Type guard that confirms `value` is a member of `set`. Avoids type assertions with `Set<T extends string>`.
3
- */
4
- export function isFlag<T extends string>(set: ReadonlySet<T>, value: string): value is T {
5
- for (const flag of set) {
6
- if (flag === value) return true
7
- }
8
- return false
9
- }
@@ -1,10 +0,0 @@
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
- }
@@ -1,80 +0,0 @@
1
- import type { Config } from '@kubb/core'
2
- import { cosmiconfig } from 'cosmiconfig'
3
- import { unrun } from 'unrun'
4
-
5
- type CosmiconfigResult = {
6
- filepath: string
7
- isEmpty?: boolean
8
- config: Config
9
- }
10
-
11
- const unrunInputOptions = {
12
- transform: {
13
- jsx: {
14
- runtime: 'automatic' as const,
15
- importSource: '@kubb/renderer-jsx',
16
- },
17
- },
18
- }
19
-
20
- const tsLoader = async (configFile: string) => {
21
- const { module } = await unrun({
22
- path: configFile,
23
- inputOptions: unrunInputOptions,
24
- })
25
- return module
26
- }
27
-
28
- export async function getCosmiConfig(moduleName: string, config?: string): Promise<CosmiconfigResult> {
29
- let result: CosmiconfigResult
30
- const searchPlaces = [
31
- 'package.json',
32
- `.${moduleName}rc`,
33
- `.${moduleName}rc.json`,
34
- `.${moduleName}rc.yaml`,
35
- `.${moduleName}rc.yml`,
36
-
37
- `.${moduleName}rc.ts`,
38
- `.${moduleName}rc.mts`,
39
- `.${moduleName}rc.cts`,
40
- `.${moduleName}rc.js`,
41
- `.${moduleName}rc.mjs`,
42
- `.${moduleName}rc.cjs`,
43
-
44
- `${moduleName}.config.ts`,
45
- `${moduleName}.config.mts`,
46
- `${moduleName}.config.cts`,
47
- `${moduleName}.config.js`,
48
- `${moduleName}.config.mjs`,
49
- `${moduleName}.config.cjs`,
50
- ]
51
- const explorer = cosmiconfig(moduleName, {
52
- cache: false,
53
- searchPlaces: [
54
- ...searchPlaces.map((searchPlace) => {
55
- return `.config/${searchPlace}`
56
- }),
57
- ...searchPlaces.map((searchPlace) => {
58
- return `configs/${searchPlace}`
59
- }),
60
- ...searchPlaces,
61
- ],
62
- loaders: {
63
- '.ts': tsLoader,
64
- '.mts': tsLoader,
65
- '.cts': tsLoader,
66
- },
67
- })
68
-
69
- try {
70
- result = config ? ((await explorer.load(config)) as CosmiconfigResult) : ((await explorer.search()) as CosmiconfigResult)
71
- } catch (error) {
72
- throw new Error('Config failed loading', { cause: error })
73
- }
74
-
75
- if (result?.isEmpty || !result || !result.config) {
76
- throw new Error('Config not defined, create a kubb.config.js or pass through your config with the option --config')
77
- }
78
-
79
- return result as CosmiconfigResult
80
- }
@@ -1,68 +0,0 @@
1
- import path from 'node:path'
2
- import { styleText } from 'node:util'
3
- import { formatHrtime, randomCliColor } from '@internals/utils'
4
- import type { Config, Plugin } from '@kubb/core'
5
- import { SUMMARY_MAX_BAR_LENGTH, SUMMARY_TIME_SCALE_DIVISOR } from '../constants.ts'
6
-
7
- type SummaryProps = {
8
- failedPlugins: Set<{ plugin: Plugin; error: Error }>
9
- status: 'success' | 'failed'
10
- hrStart: [number, number]
11
- filesCreated: number
12
- config: Config
13
- pluginTimings?: Map<string, number>
14
- }
15
-
16
- export function getSummary({ failedPlugins, filesCreated, status, hrStart, config, pluginTimings }: SummaryProps): string[] {
17
- const duration = formatHrtime(hrStart)
18
-
19
- const pluginsCount = config.plugins?.length ?? 0
20
- const successCount = pluginsCount - failedPlugins.size
21
-
22
- const meta = {
23
- plugins:
24
- status === 'success'
25
- ? `${styleText('green', `${successCount} successful`)}, ${pluginsCount} total`
26
- : `${styleText('green', `${successCount} successful`)}, ${styleText('red', `${failedPlugins.size} failed`)}, ${pluginsCount} total`,
27
- pluginsFailed: status === 'failed' ? [...failedPlugins].map(({ plugin }) => randomCliColor(plugin.name)).join(', ') : undefined,
28
- filesCreated,
29
- time: styleText('green', duration),
30
- output: path.resolve(config.root, config.output.path),
31
- } as const
32
-
33
- const labels = {
34
- plugins: 'Plugins:',
35
- failed: 'Failed:',
36
- generated: 'Generated:',
37
- pluginTimings: 'Plugin Timings:',
38
- output: 'Output:',
39
- }
40
- const maxLength = Math.max(0, ...[...Object.values(labels), ...(pluginTimings ? Array.from(pluginTimings.keys()) : [])].map((s) => s.length))
41
-
42
- const summaryLines: string[] = []
43
- summaryLines.push(`${labels.plugins.padEnd(maxLength + 2)} ${meta.plugins}`)
44
-
45
- if (meta.pluginsFailed) {
46
- summaryLines.push(`${labels.failed.padEnd(maxLength + 2)} ${meta.pluginsFailed}`)
47
- }
48
-
49
- summaryLines.push(`${labels.generated.padEnd(maxLength + 2)} ${meta.filesCreated} files in ${meta.time}`)
50
-
51
- if (pluginTimings && pluginTimings.size > 0) {
52
- const sortedTimings = Array.from(pluginTimings.entries()).sort((a, b) => b[1] - a[1])
53
-
54
- summaryLines.push(`${labels.pluginTimings}`)
55
-
56
- sortedTimings.forEach(([name, time]) => {
57
- const timeStr = time >= 1000 ? `${(time / 1000).toFixed(2)}s` : `${Math.round(time)}ms`
58
- const barLength = Math.min(Math.ceil(time / SUMMARY_TIME_SCALE_DIVISOR), SUMMARY_MAX_BAR_LENGTH)
59
- const bar = styleText('dim', '█'.repeat(barLength))
60
-
61
- summaryLines.push(`${styleText('dim', '•')} ${name.padEnd(maxLength + 1)}${bar} ${timeStr}`)
62
- })
63
- }
64
-
65
- summaryLines.push(`${labels.output.padEnd(maxLength + 2)} ${meta.output}`)
66
-
67
- return summaryLines
68
- }
@@ -1,91 +0,0 @@
1
- import type { AsyncEventEmitter } from '@internals/utils'
2
- import { toError } from '@internals/utils'
3
- import type { KubbHooks } from '@kubb/core'
4
- import { NonZeroExitError, x } from 'tinyexec'
5
-
6
- type HookOutputSink = {
7
- /** Called for each streamed stdout line while the hook runs (optional). */
8
- onLine?: (line: string) => void
9
- /** Called on stderr after failure (optional). */
10
- onStderr?: (text: string) => void
11
- /** Called on stdout after failure (optional). */
12
- onStdout?: (text: string) => void
13
- }
14
-
15
- type RunHookOptions = {
16
- id: string
17
- command: string
18
- args?: readonly string[]
19
- commandWithArgs: string
20
- context: AsyncEventEmitter<KubbHooks>
21
- /** When true the process output is streamed line-by-line via onLine. */
22
- stream?: boolean
23
- sink?: HookOutputSink
24
- }
25
-
26
- /**
27
- * Executes a hook command, emits debug and completion events, and forwards output to an optional sink.
28
- */
29
- export async function runHook({ id, command, args, commandWithArgs, context, stream = false, sink }: RunHookOptions): Promise<void> {
30
- try {
31
- const proc = x(command, [...(args ?? [])], {
32
- nodeOptions: { detached: process.platform !== 'win32' },
33
- throwOnError: true,
34
- })
35
-
36
- if (stream && sink?.onLine) {
37
- for await (const line of proc) {
38
- sink.onLine(line)
39
- }
40
- }
41
-
42
- const result = await proc
43
-
44
- await context.emit('kubb:debug', {
45
- date: new Date(),
46
- logs: [result.stdout.trimEnd()],
47
- })
48
-
49
- await context.emit('kubb:hook:end', {
50
- command,
51
- args,
52
- id,
53
- success: true,
54
- error: null,
55
- })
56
- } catch (err) {
57
- if (!(err instanceof NonZeroExitError)) {
58
- await context.emit('kubb:hook:end', {
59
- command,
60
- args,
61
- id,
62
- success: false,
63
- error: toError(err),
64
- })
65
- await context.emit('kubb:error', { error: toError(err) })
66
- return
67
- }
68
-
69
- const stderr = err.output?.stderr ?? ''
70
- const stdout = err.output?.stdout ?? ''
71
-
72
- await context.emit('kubb:debug', {
73
- date: new Date(),
74
- logs: [stdout, stderr].filter(Boolean),
75
- })
76
-
77
- if (stderr) sink?.onStderr?.(stderr)
78
- if (stdout) sink?.onStdout?.(stdout)
79
-
80
- const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`)
81
-
82
- await context.emit('kubb:hook:end', {
83
- command,
84
- args,
85
- id,
86
- success: false,
87
- error: errorMessage,
88
- })
89
- await context.emit('kubb:error', { error: errorMessage })
90
- }
91
- }
@@ -1,273 +0,0 @@
1
- import { randomBytes } from 'node:crypto'
2
- import os from 'node:os'
3
- import process from 'node:process'
4
- import { executeIfOnline, isCIEnvironment } from '@internals/utils'
5
- import { OTLP_ENDPOINT } from '../constants.ts'
6
-
7
- // OpenTelemetry OTLP JSON types
8
- // https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto
9
- // https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/common/v1/common.proto
10
-
11
- type OtlpStringValue = { stringValue: string }
12
- type OtlpBoolValue = { boolValue: boolean }
13
- type OtlpIntValue = { intValue: number }
14
- type OtlpDoubleValue = { doubleValue: number }
15
- type OtlpBytesValue = { bytesValue: string }
16
- type OtlpArrayValue = { arrayValue: { values: OtlpAnyValue[] } }
17
- type OtlpKvListValue = { kvlistValue: { values: OtlpKeyValue[] } }
18
-
19
- type OtlpAnyValue = OtlpStringValue | OtlpBoolValue | OtlpIntValue | OtlpDoubleValue | OtlpBytesValue | OtlpArrayValue | OtlpKvListValue
20
-
21
- type OtlpKeyValue = {
22
- key: string
23
- value: OtlpAnyValue
24
- }
25
-
26
- type OtlpResource = {
27
- attributes: OtlpKeyValue[]
28
- droppedAttributesCount?: number
29
- }
30
-
31
- type OtlpInstrumentationScope = {
32
- name: string
33
- version?: string
34
- attributes?: OtlpKeyValue[]
35
- droppedAttributesCount?: number
36
- }
37
-
38
- /** https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto#L103 */
39
- type OtlpSpanKind = 0 | 1 | 2 | 3 | 4 | 5
40
-
41
- /** 0 = STATUS_CODE_UNSET, 1 = STATUS_CODE_OK, 2 = STATUS_CODE_ERROR */
42
- type OtlpStatusCode = 0 | 1 | 2
43
-
44
- type OtlpStatus = {
45
- code: OtlpStatusCode
46
- message?: string
47
- }
48
-
49
- type OtlpSpan = {
50
- traceId: string
51
- spanId: string
52
- traceState?: string
53
- parentSpanId?: string
54
- name: string
55
- kind: OtlpSpanKind
56
- startTimeUnixNano: string
57
- endTimeUnixNano: string
58
- attributes?: OtlpKeyValue[]
59
- droppedAttributesCount?: number
60
- events?: OtlpSpanEvent[]
61
- droppedEventsCount?: number
62
- links?: OtlpSpanLink[]
63
- droppedLinksCount?: number
64
- status?: OtlpStatus
65
- }
66
-
67
- type OtlpSpanEvent = {
68
- timeUnixNano: string
69
- name: string
70
- attributes?: OtlpKeyValue[]
71
- droppedAttributesCount?: number
72
- }
73
-
74
- type OtlpSpanLink = {
75
- traceId: string
76
- spanId: string
77
- traceState?: string
78
- attributes?: OtlpKeyValue[]
79
- droppedAttributesCount?: number
80
- }
81
-
82
- type OtlpScopeSpans = {
83
- scope: OtlpInstrumentationScope
84
- spans: OtlpSpan[]
85
- schemaUrl?: string
86
- }
87
-
88
- type OtlpResourceSpans = {
89
- resource: OtlpResource
90
- scopeSpans: OtlpScopeSpans[]
91
- schemaUrl?: string
92
- }
93
-
94
- /** Root payload sent to POST /v1/traces */
95
- type OtlpExportTraceServiceRequest = {
96
- resourceSpans: OtlpResourceSpans[]
97
- }
98
-
99
- export type TelemetryPlugin = {
100
- name: string
101
- options: Record<string, unknown>
102
- }
103
-
104
- type TelemetryEvent = {
105
- command: string
106
- kubbVersion: string
107
- nodeVersion: string
108
- platform: string
109
- ci: boolean
110
- plugins: TelemetryPlugin[]
111
- duration: number
112
- filesCreated: number
113
- status: 'success' | 'failed'
114
- }
115
-
116
- /**
117
- * Detect whether the current process is running inside a CI environment.
118
- * Delegates to the canonical isCIEnvironment() from envDetection.
119
- */
120
- export function isCi(): boolean {
121
- return isCIEnvironment()
122
- }
123
-
124
- /**
125
- * Check if telemetry is disabled via DO_NOT_TRACK or KUBB_DISABLE_TELEMETRY.
126
- * Respects the standard DO_NOT_TRACK convention used across development tools.
127
- */
128
- export function isTelemetryDisabled(): boolean {
129
- return (
130
- process.env['DO_NOT_TRACK'] === '1' ||
131
- process.env['DO_NOT_TRACK'] === 'true' ||
132
- process.env['KUBB_DISABLE_TELEMETRY'] === '1' ||
133
- process.env['KUBB_DISABLE_TELEMETRY'] === 'true'
134
- )
135
- }
136
-
137
- /**
138
- * Convert a TelemetryEvent into an OTLP-compatible JSON trace payload.
139
- * See https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
140
- */
141
- export function buildOtlpPayload(event: TelemetryEvent): OtlpExportTraceServiceRequest {
142
- const traceId = randomBytes(16).toString('hex')
143
- const spanId = randomBytes(8).toString('hex')
144
- const endTimeNs = BigInt(Date.now()) * 1_000_000n
145
- const startTimeNs = endTimeNs - BigInt(event.duration) * 1_000_000n
146
-
147
- const attributes: OtlpKeyValue[] = [
148
- { key: 'kubb.command', value: { stringValue: event.command } },
149
- { key: 'kubb.version', value: { stringValue: event.kubbVersion } },
150
- { key: 'kubb.node_version', value: { stringValue: event.nodeVersion } },
151
- { key: 'kubb.platform', value: { stringValue: event.platform } },
152
- { key: 'kubb.ci', value: { boolValue: event.ci } },
153
- { key: 'kubb.files_created', value: { intValue: event.filesCreated } },
154
- { key: 'kubb.status', value: { stringValue: event.status } },
155
- {
156
- key: 'kubb.plugins',
157
- value: {
158
- arrayValue: {
159
- values: event.plugins.map(
160
- (p): OtlpKvListValue => ({
161
- kvlistValue: {
162
- values: [
163
- { key: 'name', value: { stringValue: p.name } },
164
- {
165
- key: 'options',
166
- value: {
167
- stringValue: JSON.stringify({
168
- ...p.options,
169
- usedEnumNames: undefined,
170
- }),
171
- },
172
- },
173
- ],
174
- },
175
- }),
176
- ),
177
- },
178
- },
179
- },
180
- ]
181
-
182
- return {
183
- resourceSpans: [
184
- {
185
- resource: {
186
- attributes: [
187
- { key: 'service.name', value: { stringValue: 'kubb-cli' } },
188
- {
189
- key: 'service.version',
190
- value: { stringValue: event.kubbVersion },
191
- },
192
- { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },
193
- ],
194
- },
195
- scopeSpans: [
196
- {
197
- scope: { name: 'kubb-cli', version: event.kubbVersion },
198
- spans: [
199
- {
200
- traceId,
201
- spanId,
202
- name: event.command,
203
- kind: 1 satisfies OtlpSpanKind,
204
- startTimeUnixNano: String(startTimeNs),
205
- endTimeUnixNano: String(endTimeNs),
206
- attributes,
207
- status: {
208
- code: (event.status === 'success' ? 1 : 2) satisfies OtlpStatusCode,
209
- },
210
- },
211
- ],
212
- },
213
- ],
214
- },
215
- ],
216
- }
217
- }
218
-
219
- /**
220
- * Send an anonymous telemetry event to the Kubb OTLP endpoint.
221
- * Respects DO_NOT_TRACK and KUBB_DISABLE_TELEMETRY environment variables.
222
- * Fails silently to never interrupt the generation process.
223
- */
224
- export async function sendTelemetry(event: TelemetryEvent): Promise<void> {
225
- if (isTelemetryDisabled()) {
226
- return
227
- }
228
-
229
- await executeIfOnline(async () => {
230
- try {
231
- await fetch(`${OTLP_ENDPOINT}/v1/traces`, {
232
- method: 'POST',
233
- headers: {
234
- 'Content-Type': 'application/json',
235
- 'Kubb-Telemetry-Version': '1',
236
- 'Kubb-Telemetry-Source': 'kubb-cli',
237
- },
238
- body: JSON.stringify(buildOtlpPayload(event)),
239
- signal: AbortSignal.timeout(5_000),
240
- })
241
- } catch (_e) {
242
- // Fail silently – telemetry must never break the CLI
243
- }
244
- })
245
- }
246
-
247
- /**
248
- * Build an anonymous telemetry payload from a completed generation run.
249
- * No file paths, OpenAPI specs, or secrets are included.
250
- */
251
- export function buildTelemetryEvent(options: {
252
- command: 'generate' | 'mcp' | 'validate' | 'agent'
253
- kubbVersion: string
254
- plugins?: TelemetryPlugin[]
255
- hrStart: [number, number]
256
- filesCreated?: number
257
- status: 'success' | 'failed'
258
- }): TelemetryEvent {
259
- const [seconds, nanoseconds] = process.hrtime(options.hrStart)
260
- const duration = Math.round(seconds * 1000 + nanoseconds / 1e6)
261
-
262
- return {
263
- command: options.command,
264
- kubbVersion: options.kubbVersion,
265
- nodeVersion: process.versions.node.split('.')[0] as string,
266
- platform: os.platform(),
267
- ci: isCi(),
268
- plugins: options.plugins ?? [],
269
- duration,
270
- filesCreated: options.filesCreated ?? 0,
271
- status: options.status,
272
- }
273
- }
@@ -1,19 +0,0 @@
1
- import { styleText } from 'node:util'
2
- import { WATCHER_IGNORED_PATHS } from '../constants.ts'
3
-
4
- export async function startWatcher(path: string[], cb: (path: string[]) => Promise<void>): Promise<void> {
5
- const { watch } = await import('chokidar')
6
- const watcher = watch(path, {
7
- ignorePermissionErrors: true,
8
- ignored: WATCHER_IGNORED_PATHS,
9
- })
10
- watcher.on('all', async (type, file) => {
11
- console.log(styleText('yellow', styleText('bold', `Change detected: ${type} ${file}`)))
12
-
13
- try {
14
- await cb(path)
15
- } catch (_e) {
16
- console.log(styleText('red', 'Watcher failed'))
17
- }
18
- })
19
- }
File without changes