@kubb/cli 4.29.0 → 4.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-BzD6f3mV.js → agent-CGYnHRU4.js} +2 -2
- package/dist/{agent-BzD6f3mV.js.map → agent-CGYnHRU4.js.map} +1 -1
- package/dist/{agent-NAhMf2ot.cjs → agent-kU5gLqqn.cjs} +2 -2
- package/dist/{agent-NAhMf2ot.cjs.map → agent-kU5gLqqn.cjs.map} +1 -1
- package/dist/{generate-CnKaIwc7.js → generate-DvoKuzJW.js} +31 -6
- package/dist/{generate-CnKaIwc7.js.map → generate-DvoKuzJW.js.map} +1 -1
- package/dist/{generate-C8gS4Z2F.cjs → generate-G4sRCtYd.cjs} +31 -6
- package/dist/generate-G4sRCtYd.cjs.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.js +7 -7
- package/dist/{init-B5qnw1XS.js → init-Cp7PS6R5.js} +3 -36
- package/dist/init-Cp7PS6R5.js.map +1 -0
- package/dist/{init-BDWQO7I8.cjs → init-XsLQVNk3.cjs} +5 -38
- package/dist/init-XsLQVNk3.cjs.map +1 -0
- package/dist/{mcp-Jboea6xH.js → mcp-CD0ZKRHG.js} +18 -2
- package/dist/mcp-CD0ZKRHG.js.map +1 -0
- package/dist/{mcp-97TXkJVX.cjs → mcp-DCjAEKzU.cjs} +20 -3
- package/dist/mcp-DCjAEKzU.cjs.map +1 -0
- package/dist/package-BYVzWUJV.js +6 -0
- package/dist/package-BYVzWUJV.js.map +1 -0
- package/dist/{package-oo3QhWS5.cjs → package-BqKkzL1A.cjs} +2 -2
- package/dist/package-BqKkzL1A.cjs.map +1 -0
- package/dist/{start-lrn1-P52.cjs → start-BN1ikd53.cjs} +17 -2
- package/dist/start-BN1ikd53.cjs.map +1 -0
- package/dist/{start-CYuU23PX.js → start-VN4IxBaM.js} +17 -2
- package/dist/{start-CYuU23PX.js.map → start-VN4IxBaM.js.map} +1 -1
- package/dist/telemetry-Ccka73zO.js +149 -0
- package/dist/telemetry-Ccka73zO.js.map +1 -0
- package/dist/telemetry-DxyJ2d4j.cjs +162 -0
- package/dist/telemetry-DxyJ2d4j.cjs.map +1 -0
- package/dist/{validate-BgYhe_55.cjs → validate-BbVY6zQM.cjs} +16 -1
- package/dist/validate-BbVY6zQM.cjs.map +1 -0
- package/dist/{validate-DOeZKiGx.js → validate-zO3bvm66.js} +16 -1
- package/dist/validate-zO3bvm66.js.map +1 -0
- package/package.json +8 -7
- package/src/commands/agent/start.ts +6 -1
- package/src/commands/generate.ts +3 -0
- package/src/commands/init.ts +2 -1
- package/src/commands/mcp.ts +7 -1
- package/src/commands/validate.ts +5 -0
- package/src/runners/generate.ts +28 -2
- package/src/utils/packageManager.ts +2 -60
- package/src/utils/telemetry.ts +278 -0
- package/dist/generate-C8gS4Z2F.cjs.map +0 -1
- package/dist/init-B5qnw1XS.js.map +0 -1
- package/dist/init-BDWQO7I8.cjs.map +0 -1
- package/dist/mcp-97TXkJVX.cjs.map +0 -1
- package/dist/mcp-Jboea6xH.js.map +0 -1
- package/dist/package-oo3QhWS5.cjs.map +0 -1
- package/dist/package-veMf5zNr.js +0 -6
- package/dist/package-veMf5zNr.js.map +0 -1
- package/dist/start-lrn1-P52.cjs.map +0 -1
- package/dist/validate-BgYhe_55.cjs.map +0 -1
- package/dist/validate-DOeZKiGx.js.map +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { t as __name } from "./chunk-DKWOrOAv.js";
|
|
2
|
+
import { t as version } from "./package-BYVzWUJV.js";
|
|
3
|
+
import { n as sendTelemetry, t as buildTelemetryEvent } from "./telemetry-Ccka73zO.js";
|
|
2
4
|
import { defineCommand, showUsage } from "citty";
|
|
3
5
|
import process from "node:process";
|
|
4
6
|
import { createJiti } from "jiti";
|
|
@@ -35,10 +37,23 @@ const command = defineCommand({
|
|
|
35
37
|
process.exit(1);
|
|
36
38
|
}
|
|
37
39
|
const { parse } = mod;
|
|
40
|
+
const hrStart = process.hrtime();
|
|
38
41
|
try {
|
|
39
42
|
await (await parse(args.input)).validate();
|
|
43
|
+
await sendTelemetry(buildTelemetryEvent({
|
|
44
|
+
command: "validate",
|
|
45
|
+
kubbVersion: version,
|
|
46
|
+
hrStart,
|
|
47
|
+
status: "success"
|
|
48
|
+
}));
|
|
40
49
|
console.log("✅ Validation success");
|
|
41
50
|
} catch (error) {
|
|
51
|
+
await sendTelemetry(buildTelemetryEvent({
|
|
52
|
+
command: "validate",
|
|
53
|
+
kubbVersion: version,
|
|
54
|
+
hrStart,
|
|
55
|
+
status: "failed"
|
|
56
|
+
}));
|
|
42
57
|
console.error("❌ Validation failed");
|
|
43
58
|
console.log(error?.message);
|
|
44
59
|
process.exit(1);
|
|
@@ -49,4 +64,4 @@ const command = defineCommand({
|
|
|
49
64
|
|
|
50
65
|
//#endregion
|
|
51
66
|
export { command as default };
|
|
52
|
-
//# sourceMappingURL=validate-
|
|
67
|
+
//# sourceMappingURL=validate-zO3bvm66.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-zO3bvm66.js","names":[],"sources":["../src/commands/validate.ts"],"sourcesContent":["import process from 'node:process'\nimport type { ArgsDef } from 'citty'\nimport { defineCommand, showUsage } from 'citty'\nimport { createJiti } from 'jiti'\nimport { version } from '../../package.json'\nimport { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'\n\nconst jiti = createJiti(import.meta.url, {\n sourceMaps: true,\n})\n\nconst args = {\n input: {\n type: 'string',\n description: 'Path to Swagger/OpenAPI file',\n alias: 'i',\n },\n help: {\n type: 'boolean',\n description: 'Show help',\n alias: 'h',\n default: false,\n },\n} as const satisfies ArgsDef\n\nconst command = defineCommand({\n meta: {\n name: 'validate',\n description: 'Validate a Swagger/OpenAPI file',\n },\n args,\n async run(commandContext) {\n const { args } = commandContext\n\n if (args.help) {\n return showUsage(command)\n }\n\n if (args.input) {\n let mod: any\n try {\n mod = await jiti.import('@kubb/oas', { default: true })\n } catch (_e) {\n console.error(`Import of '@kubb/oas' is required to do validation`)\n process.exit(1)\n }\n\n const { parse } = mod\n const hrStart = process.hrtime()\n try {\n const oas = await parse(args.input)\n await oas.validate()\n\n await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'success' }))\n console.log('✅ Validation success')\n } catch (error) {\n await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'failed' }))\n console.error('❌ Validation failed')\n console.log((error as Error)?.message)\n process.exit(1)\n }\n }\n },\n})\n\nexport default command\n"],"mappings":";;;;;;;;AAOA,MAAM,OAAO,WAAW,OAAO,KAAK,KAAK,EACvC,YAAY,MACb,CAAC;AAgBF,MAAM,UAAU,cAAc;CAC5B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAnBW;EACX,OAAO;GACL,MAAM;GACN,aAAa;GACb,OAAO;GACR;EACD,MAAM;GACJ,MAAM;GACN,aAAa;GACb,OAAO;GACP,SAAS;GACV;EACF;CAQC,MAAM,IAAI,gBAAgB;EACxB,MAAM,EAAE,SAAS;AAEjB,MAAI,KAAK,KACP,QAAO,UAAU,QAAQ;AAG3B,MAAI,KAAK,OAAO;GACd,IAAI;AACJ,OAAI;AACF,UAAM,MAAM,KAAK,OAAO,aAAa,EAAE,SAAS,MAAM,CAAC;YAChD,IAAI;AACX,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,KAAK,EAAE;;GAGjB,MAAM,EAAE,UAAU;GAClB,MAAM,UAAU,QAAQ,QAAQ;AAChC,OAAI;AAEF,WADY,MAAM,MAAM,KAAK,MAAM,EACzB,UAAU;AAEpB,UAAM,cAAc,oBAAoB;KAAE,SAAS;KAAY,aAAa;KAAS;KAAS,QAAQ;KAAW,CAAC,CAAC;AACnH,YAAQ,IAAI,uBAAuB;YAC5B,OAAO;AACd,UAAM,cAAc,oBAAoB;KAAE,SAAS;KAAY,aAAa;KAAS;KAAS,QAAQ;KAAU,CAAC,CAAC;AAClH,YAAQ,MAAM,sBAAsB;AACpC,YAAQ,IAAK,OAAiB,QAAQ;AACtC,YAAQ,KAAK,EAAE;;;;CAItB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.31.0",
|
|
4
4
|
"description": "Command-line interface for Kubb, enabling easy generation of TypeScript, React-Query, Zod, and other code from OpenAPI specifications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"dist",
|
|
43
43
|
"bin",
|
|
44
44
|
"!/**/**.test.**",
|
|
45
|
-
"!/**/__tests__/**"
|
|
45
|
+
"!/**/__tests__/**",
|
|
46
|
+
"!/**/__snapshots__/**"
|
|
46
47
|
],
|
|
47
48
|
"size-limit": [
|
|
48
49
|
{
|
|
@@ -55,16 +56,16 @@
|
|
|
55
56
|
"@clack/prompts": "^1.0.1",
|
|
56
57
|
"chokidar": "^5.0.0",
|
|
57
58
|
"citty": "^0.1.6",
|
|
58
|
-
"cosmiconfig": "^9.0.
|
|
59
|
+
"cosmiconfig": "^9.0.1",
|
|
59
60
|
"jiti": "2.5.1",
|
|
60
61
|
"tinyexec": "^1.0.2",
|
|
61
|
-
"@kubb/core": "4.
|
|
62
|
+
"@kubb/core": "4.31.0"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"source-map-support": "^0.5.21",
|
|
65
|
-
"@kubb/agent": "4.
|
|
66
|
-
"@kubb/mcp": "4.
|
|
67
|
-
"@kubb/oas": "4.
|
|
66
|
+
"@kubb/agent": "4.31.0",
|
|
67
|
+
"@kubb/mcp": "4.31.0",
|
|
68
|
+
"@kubb/oas": "4.31.0"
|
|
68
69
|
},
|
|
69
70
|
"engines": {
|
|
70
71
|
"node": ">=20"
|
|
@@ -6,6 +6,8 @@ import { styleText } from 'node:util'
|
|
|
6
6
|
import * as clack from '@clack/prompts'
|
|
7
7
|
import type { ArgsDef } from 'citty'
|
|
8
8
|
import { defineCommand } from 'citty'
|
|
9
|
+
import { version } from '../../../package.json'
|
|
10
|
+
import { buildTelemetryEvent, sendTelemetry } from '../../utils/telemetry.ts'
|
|
9
11
|
|
|
10
12
|
const args = {
|
|
11
13
|
config: {
|
|
@@ -122,6 +124,7 @@ const command = defineCommand({
|
|
|
122
124
|
args,
|
|
123
125
|
async run(commandContext) {
|
|
124
126
|
const { args } = commandContext
|
|
127
|
+
const hrStart = process.hrtime()
|
|
125
128
|
|
|
126
129
|
try {
|
|
127
130
|
const configPath = path.resolve(process.cwd(), args.config || 'kubb.config.ts')
|
|
@@ -131,8 +134,10 @@ const command = defineCommand({
|
|
|
131
134
|
const allowWrite = args['allow-write']
|
|
132
135
|
const allowAll = args['allow-all']
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
startServer({ port, host, configPath, noCache, allowWrite, allowAll })
|
|
138
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'agent', kubbVersion: version, hrStart, status: 'success' }))
|
|
135
139
|
} catch (error) {
|
|
140
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'agent', kubbVersion: version, hrStart, status: 'failed' }))
|
|
136
141
|
clack.log.error(styleText('red', 'Failed to start agent server'))
|
|
137
142
|
console.error(error)
|
|
138
143
|
process.exit(1)
|
package/src/commands/generate.ts
CHANGED
|
@@ -122,6 +122,9 @@ const command = defineCommand({
|
|
|
122
122
|
return async () => {
|
|
123
123
|
if (isInputPath(config) && args.watch) {
|
|
124
124
|
await startWatcher([input || config.input.path], async (paths) => {
|
|
125
|
+
// remove to avoid duplicate listeners after each change
|
|
126
|
+
events.removeAll()
|
|
127
|
+
|
|
125
128
|
await generate({
|
|
126
129
|
input,
|
|
127
130
|
config,
|
package/src/commands/init.ts
CHANGED
|
@@ -3,9 +3,10 @@ import path from 'node:path'
|
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
import { styleText } from 'node:util'
|
|
5
5
|
import * as clack from '@clack/prompts'
|
|
6
|
+
import { detectPackageManager, type PackageManagerInfo } from '@kubb/core'
|
|
6
7
|
import { defineCommand } from 'citty'
|
|
7
8
|
import { version } from '../../package.json'
|
|
8
|
-
import {
|
|
9
|
+
import { hasPackageJson, initPackageJson, installPackages } from '../utils/packageManager.ts'
|
|
9
10
|
|
|
10
11
|
type PluginOption = {
|
|
11
12
|
value: string
|
package/src/commands/mcp.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
1
2
|
import { styleText } from 'node:util'
|
|
2
3
|
import type { ArgsDef } from 'citty'
|
|
3
4
|
import { defineCommand, showUsage } from 'citty'
|
|
4
5
|
import { createJiti } from 'jiti'
|
|
6
|
+
import { version } from '../../package.json'
|
|
7
|
+
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
5
8
|
|
|
6
9
|
const jiti = createJiti(import.meta.url, {
|
|
7
10
|
sourceMaps: true,
|
|
@@ -38,11 +41,14 @@ const command = defineCommand({
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
const { run } = mod
|
|
44
|
+
const hrStart = process.hrtime()
|
|
41
45
|
try {
|
|
42
46
|
console.log('⏳ Starting MCP server...')
|
|
43
47
|
console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
|
|
44
|
-
|
|
48
|
+
run()
|
|
49
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'success' }))
|
|
45
50
|
} catch (error) {
|
|
51
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'failed' }))
|
|
46
52
|
console.error((error as Error)?.message)
|
|
47
53
|
}
|
|
48
54
|
},
|
package/src/commands/validate.ts
CHANGED
|
@@ -2,6 +2,8 @@ import process from 'node:process'
|
|
|
2
2
|
import type { ArgsDef } from 'citty'
|
|
3
3
|
import { defineCommand, showUsage } from 'citty'
|
|
4
4
|
import { createJiti } from 'jiti'
|
|
5
|
+
import { version } from '../../package.json'
|
|
6
|
+
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
5
7
|
|
|
6
8
|
const jiti = createJiti(import.meta.url, {
|
|
7
9
|
sourceMaps: true,
|
|
@@ -44,12 +46,15 @@ const command = defineCommand({
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
const { parse } = mod
|
|
49
|
+
const hrStart = process.hrtime()
|
|
47
50
|
try {
|
|
48
51
|
const oas = await parse(args.input)
|
|
49
52
|
await oas.validate()
|
|
50
53
|
|
|
54
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'success' }))
|
|
51
55
|
console.log('✅ Validation success')
|
|
52
56
|
} catch (error) {
|
|
57
|
+
await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'failed' }))
|
|
53
58
|
console.error('❌ Validation failed')
|
|
54
59
|
console.log((error as Error)?.message)
|
|
55
60
|
process.exit(1)
|
package/src/runners/generate.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { styleText } from 'node:util'
|
|
|
5
5
|
import { type Config, type KubbEvents, LogLevel, safeBuild, setup } from '@kubb/core'
|
|
6
6
|
import type { AsyncEventEmitter } from '@kubb/core/utils'
|
|
7
7
|
import { detectFormatter, detectLinter, formatters, linters } from '@kubb/core/utils'
|
|
8
|
+
import { version } from '../../package.json'
|
|
8
9
|
import { executeHooks } from '../utils/executeHooks.ts'
|
|
10
|
+
import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
|
|
9
11
|
|
|
10
12
|
type GenerateProps = {
|
|
11
13
|
input?: string
|
|
@@ -80,11 +82,22 @@ export async function generate({ input, config: userConfig, events, logLevel }:
|
|
|
80
82
|
await events.emit('generation:summary', config, {
|
|
81
83
|
failedPlugins,
|
|
82
84
|
filesCreated: files.length,
|
|
83
|
-
status:
|
|
85
|
+
status: 'failed',
|
|
84
86
|
hrStart,
|
|
85
87
|
pluginTimings: logLevel >= LogLevel.verbose ? pluginTimings : undefined,
|
|
86
88
|
})
|
|
87
89
|
|
|
90
|
+
await sendTelemetry(
|
|
91
|
+
buildTelemetryEvent({
|
|
92
|
+
command: 'generate',
|
|
93
|
+
kubbVersion: version,
|
|
94
|
+
plugins: pluginManager.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
|
|
95
|
+
hrStart,
|
|
96
|
+
filesCreated: files.length,
|
|
97
|
+
status: 'failed',
|
|
98
|
+
}),
|
|
99
|
+
)
|
|
100
|
+
|
|
88
101
|
process.exit(1)
|
|
89
102
|
}
|
|
90
103
|
|
|
@@ -194,11 +207,24 @@ export async function generate({ input, config: userConfig, events, logLevel }:
|
|
|
194
207
|
await events.emit('hooks:end')
|
|
195
208
|
}
|
|
196
209
|
|
|
210
|
+
const generationStatus = failedPlugins.size > 0 || error ? 'failed' : 'success'
|
|
211
|
+
|
|
197
212
|
await events.emit('generation:summary', config, {
|
|
198
213
|
failedPlugins,
|
|
199
214
|
filesCreated: files.length,
|
|
200
|
-
status:
|
|
215
|
+
status: generationStatus,
|
|
201
216
|
hrStart,
|
|
202
217
|
pluginTimings,
|
|
203
218
|
})
|
|
219
|
+
|
|
220
|
+
const telemetryEvent = buildTelemetryEvent({
|
|
221
|
+
command: 'generate',
|
|
222
|
+
kubbVersion: version,
|
|
223
|
+
plugins: pluginManager.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
|
|
224
|
+
hrStart,
|
|
225
|
+
filesCreated: files.length,
|
|
226
|
+
status: generationStatus,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
await sendTelemetry(telemetryEvent)
|
|
204
230
|
}
|
|
@@ -1,72 +1,14 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process'
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'
|
|
6
|
-
|
|
7
|
-
export interface PackageManagerInfo {
|
|
8
|
-
name: PackageManager
|
|
9
|
-
lockFile: string
|
|
10
|
-
installCommand: string[]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const packageManagers: Record<PackageManager, PackageManagerInfo> = {
|
|
14
|
-
pnpm: {
|
|
15
|
-
name: 'pnpm',
|
|
16
|
-
lockFile: 'pnpm-lock.yaml',
|
|
17
|
-
installCommand: ['add', '-D'],
|
|
18
|
-
},
|
|
19
|
-
yarn: {
|
|
20
|
-
name: 'yarn',
|
|
21
|
-
lockFile: 'yarn.lock',
|
|
22
|
-
installCommand: ['add', '-D'],
|
|
23
|
-
},
|
|
24
|
-
bun: {
|
|
25
|
-
name: 'bun',
|
|
26
|
-
lockFile: 'bun.lockb',
|
|
27
|
-
installCommand: ['add', '-d'],
|
|
28
|
-
},
|
|
29
|
-
npm: {
|
|
30
|
-
name: 'npm',
|
|
31
|
-
lockFile: 'package-lock.json',
|
|
32
|
-
installCommand: ['install', '--save-dev'],
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function detectPackageManager(cwd: string = process.cwd()): PackageManagerInfo {
|
|
37
|
-
// Check for packageManager field in package.json
|
|
38
|
-
const packageJsonPath = path.join(cwd, 'package.json')
|
|
39
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
40
|
-
try {
|
|
41
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
|
42
|
-
if (packageJson.packageManager) {
|
|
43
|
-
const [name] = packageJson.packageManager.split('@')
|
|
44
|
-
if (name in packageManagers) {
|
|
45
|
-
return packageManagers[name as PackageManager]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
// Continue to lock file detection
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check for lock files
|
|
54
|
-
for (const pm of Object.values(packageManagers)) {
|
|
55
|
-
if (fs.existsSync(path.join(cwd, pm.lockFile))) {
|
|
56
|
-
return pm
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Default to npm
|
|
61
|
-
return packageManagers.npm
|
|
62
|
-
}
|
|
4
|
+
import type { PackageManagerInfo, PackageManagerName } from '@kubb/core'
|
|
63
5
|
|
|
64
6
|
export function hasPackageJson(cwd: string = process.cwd()): boolean {
|
|
65
7
|
return fs.existsSync(path.join(cwd, 'package.json'))
|
|
66
8
|
}
|
|
67
9
|
|
|
68
10
|
export async function initPackageJson(cwd: string, packageManager: PackageManagerInfo): Promise<void> {
|
|
69
|
-
const commands: Record<
|
|
11
|
+
const commands: Record<PackageManagerName, string[]> = {
|
|
70
12
|
npm: ['init', '-y'],
|
|
71
13
|
pnpm: ['init'],
|
|
72
14
|
yarn: ['init', '-y'],
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { executeIfOnline } from '@kubb/core/utils'
|
|
5
|
+
|
|
6
|
+
const OTLP_ENDPOINT = 'https://otlp.kubb.dev'
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// OpenTelemetry OTLP JSON types
|
|
10
|
+
// https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto
|
|
11
|
+
// https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/common/v1/common.proto
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
type OtlpStringValue = { stringValue: string }
|
|
15
|
+
type OtlpBoolValue = { boolValue: boolean }
|
|
16
|
+
type OtlpIntValue = { intValue: number }
|
|
17
|
+
type OtlpDoubleValue = { doubleValue: number }
|
|
18
|
+
type OtlpBytesValue = { bytesValue: string }
|
|
19
|
+
type OtlpArrayValue = { arrayValue: { values: OtlpAnyValue[] } }
|
|
20
|
+
type OtlpKvListValue = { kvlistValue: { values: OtlpKeyValue[] } }
|
|
21
|
+
|
|
22
|
+
type OtlpAnyValue = OtlpStringValue | OtlpBoolValue | OtlpIntValue | OtlpDoubleValue | OtlpBytesValue | OtlpArrayValue | OtlpKvListValue
|
|
23
|
+
|
|
24
|
+
type OtlpKeyValue = {
|
|
25
|
+
key: string
|
|
26
|
+
value: OtlpAnyValue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type OtlpResource = {
|
|
30
|
+
attributes: OtlpKeyValue[]
|
|
31
|
+
droppedAttributesCount?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type OtlpInstrumentationScope = {
|
|
35
|
+
name: string
|
|
36
|
+
version?: string
|
|
37
|
+
attributes?: OtlpKeyValue[]
|
|
38
|
+
droppedAttributesCount?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto#L103 */
|
|
42
|
+
type OtlpSpanKind = 0 | 1 | 2 | 3 | 4 | 5
|
|
43
|
+
|
|
44
|
+
/** 0 = STATUS_CODE_UNSET, 1 = STATUS_CODE_OK, 2 = STATUS_CODE_ERROR */
|
|
45
|
+
type OtlpStatusCode = 0 | 1 | 2
|
|
46
|
+
|
|
47
|
+
type OtlpStatus = {
|
|
48
|
+
code: OtlpStatusCode
|
|
49
|
+
message?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type OtlpSpan = {
|
|
53
|
+
traceId: string
|
|
54
|
+
spanId: string
|
|
55
|
+
traceState?: string
|
|
56
|
+
parentSpanId?: string
|
|
57
|
+
name: string
|
|
58
|
+
kind: OtlpSpanKind
|
|
59
|
+
startTimeUnixNano: string
|
|
60
|
+
endTimeUnixNano: string
|
|
61
|
+
attributes?: OtlpKeyValue[]
|
|
62
|
+
droppedAttributesCount?: number
|
|
63
|
+
events?: OtlpSpanEvent[]
|
|
64
|
+
droppedEventsCount?: number
|
|
65
|
+
links?: OtlpSpanLink[]
|
|
66
|
+
droppedLinksCount?: number
|
|
67
|
+
status?: OtlpStatus
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type OtlpSpanEvent = {
|
|
71
|
+
timeUnixNano: string
|
|
72
|
+
name: string
|
|
73
|
+
attributes?: OtlpKeyValue[]
|
|
74
|
+
droppedAttributesCount?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type OtlpSpanLink = {
|
|
78
|
+
traceId: string
|
|
79
|
+
spanId: string
|
|
80
|
+
traceState?: string
|
|
81
|
+
attributes?: OtlpKeyValue[]
|
|
82
|
+
droppedAttributesCount?: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type OtlpScopeSpans = {
|
|
86
|
+
scope: OtlpInstrumentationScope
|
|
87
|
+
spans: OtlpSpan[]
|
|
88
|
+
schemaUrl?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type OtlpResourceSpans = {
|
|
92
|
+
resource: OtlpResource
|
|
93
|
+
scopeSpans: OtlpScopeSpans[]
|
|
94
|
+
schemaUrl?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Root payload sent to POST /v1/traces */
|
|
98
|
+
export type OtlpExportTraceServiceRequest = {
|
|
99
|
+
resourceSpans: OtlpResourceSpans[]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
export type TelemetryPlugin = {
|
|
105
|
+
name: string
|
|
106
|
+
options: Record<string, unknown>
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type TelemetryEvent = {
|
|
110
|
+
command: string
|
|
111
|
+
kubbVersion: string
|
|
112
|
+
nodeVersion: string
|
|
113
|
+
platform: string
|
|
114
|
+
ci: boolean
|
|
115
|
+
plugins: TelemetryPlugin[]
|
|
116
|
+
duration: number
|
|
117
|
+
filesCreated: number
|
|
118
|
+
status: 'success' | 'failed'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Detect whether the current process is running inside a CI environment by
|
|
123
|
+
* checking the well-known environment variables set by all major CI systems.
|
|
124
|
+
*/
|
|
125
|
+
export function isCi(): boolean {
|
|
126
|
+
return !!(
|
|
127
|
+
(
|
|
128
|
+
process.env['CI'] || // Generic (GitHub Actions, GitLab CI, CircleCI, Travis CI, etc.)
|
|
129
|
+
process.env['GITHUB_ACTIONS'] || // GitHub Actions
|
|
130
|
+
process.env['GITLAB_CI'] || // GitLab CI
|
|
131
|
+
process.env['BITBUCKET_BUILD_NUMBER'] || // Bitbucket Pipelines
|
|
132
|
+
process.env['JENKINS_URL'] || // Jenkins
|
|
133
|
+
process.env['CIRCLECI'] || // CircleCI
|
|
134
|
+
process.env['TRAVIS'] || // Travis CI
|
|
135
|
+
process.env['TEAMCITY_VERSION'] || // TeamCity
|
|
136
|
+
process.env['BUILDKITE'] || // Buildkite
|
|
137
|
+
process.env['TF_BUILD']
|
|
138
|
+
) // Azure Pipelines
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if telemetry is disabled via DO_NOT_TRACK or KUBB_DISABLE_TELEMETRY.
|
|
144
|
+
* Respects the standard DO_NOT_TRACK convention used across development tools.
|
|
145
|
+
*/
|
|
146
|
+
export function isTelemetryDisabled(): boolean {
|
|
147
|
+
return (
|
|
148
|
+
process.env['DO_NOT_TRACK'] === '1' ||
|
|
149
|
+
process.env['DO_NOT_TRACK'] === 'true' ||
|
|
150
|
+
process.env['KUBB_DISABLE_TELEMETRY'] === '1' ||
|
|
151
|
+
process.env['KUBB_DISABLE_TELEMETRY'] === 'true'
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Convert a TelemetryEvent into an OTLP-compatible JSON trace payload.
|
|
157
|
+
* See https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
|
|
158
|
+
*/
|
|
159
|
+
export function buildOtlpPayload(event: TelemetryEvent): OtlpExportTraceServiceRequest {
|
|
160
|
+
const traceId = randomBytes(16).toString('hex')
|
|
161
|
+
const spanId = randomBytes(8).toString('hex')
|
|
162
|
+
const endTimeNs = BigInt(Date.now()) * 1_000_000n
|
|
163
|
+
const startTimeNs = endTimeNs - BigInt(event.duration) * 1_000_000n
|
|
164
|
+
|
|
165
|
+
const attributes: OtlpKeyValue[] = [
|
|
166
|
+
{ key: 'kubb.command', value: { stringValue: event.command } },
|
|
167
|
+
{ key: 'kubb.version', value: { stringValue: event.kubbVersion } },
|
|
168
|
+
{ key: 'kubb.node_version', value: { stringValue: event.nodeVersion } },
|
|
169
|
+
{ key: 'kubb.platform', value: { stringValue: event.platform } },
|
|
170
|
+
{ key: 'kubb.ci', value: { boolValue: event.ci } },
|
|
171
|
+
{ key: 'kubb.files_created', value: { intValue: event.filesCreated } },
|
|
172
|
+
{ key: 'kubb.status', value: { stringValue: event.status } },
|
|
173
|
+
{
|
|
174
|
+
key: 'kubb.plugins',
|
|
175
|
+
value: {
|
|
176
|
+
arrayValue: {
|
|
177
|
+
values: event.plugins.map(
|
|
178
|
+
(p): OtlpKvListValue => ({
|
|
179
|
+
kvlistValue: {
|
|
180
|
+
values: [
|
|
181
|
+
{ key: 'name', value: { stringValue: p.name } },
|
|
182
|
+
{ key: 'options', value: { stringValue: JSON.stringify(p.options) } },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
}),
|
|
186
|
+
),
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
resourceSpans: [
|
|
194
|
+
{
|
|
195
|
+
resource: {
|
|
196
|
+
attributes: [
|
|
197
|
+
{ key: 'service.name', value: { stringValue: 'kubb-cli' } },
|
|
198
|
+
{ key: 'service.version', value: { stringValue: event.kubbVersion } },
|
|
199
|
+
{ key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
scopeSpans: [
|
|
203
|
+
{
|
|
204
|
+
scope: { name: 'kubb-cli', version: event.kubbVersion },
|
|
205
|
+
spans: [
|
|
206
|
+
{
|
|
207
|
+
traceId,
|
|
208
|
+
spanId,
|
|
209
|
+
name: event.command,
|
|
210
|
+
kind: 1 satisfies OtlpSpanKind,
|
|
211
|
+
startTimeUnixNano: String(startTimeNs),
|
|
212
|
+
endTimeUnixNano: String(endTimeNs),
|
|
213
|
+
attributes,
|
|
214
|
+
status: { code: (event.status === 'success' ? 1 : 2) satisfies OtlpStatusCode },
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Send an anonymous telemetry event to the Kubb OTLP endpoint.
|
|
226
|
+
* Respects DO_NOT_TRACK and KUBB_DISABLE_TELEMETRY environment variables.
|
|
227
|
+
* Fails silently to never interrupt the generation process.
|
|
228
|
+
*/
|
|
229
|
+
export async function sendTelemetry(event: TelemetryEvent): Promise<void> {
|
|
230
|
+
if (isTelemetryDisabled()) {
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
await executeIfOnline(async () => {
|
|
235
|
+
try {
|
|
236
|
+
await fetch(`${OTLP_ENDPOINT}/v1/traces`, {
|
|
237
|
+
method: 'POST',
|
|
238
|
+
headers: {
|
|
239
|
+
'Content-Type': 'application/json',
|
|
240
|
+
'Kubb-Telemetry-Version': '1',
|
|
241
|
+
'Kubb-Telemetry-Source': 'kubb-cli',
|
|
242
|
+
},
|
|
243
|
+
body: JSON.stringify(buildOtlpPayload(event)),
|
|
244
|
+
signal: AbortSignal.timeout(5_000),
|
|
245
|
+
})
|
|
246
|
+
} catch (_e) {
|
|
247
|
+
// Fail silently – telemetry must never break the CLI
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Build an anonymous telemetry payload from a completed generation run.
|
|
254
|
+
* No file paths, OpenAPI specs, or secrets are included.
|
|
255
|
+
*/
|
|
256
|
+
export function buildTelemetryEvent(options: {
|
|
257
|
+
command: 'generate' | 'mcp' | 'validate' | 'agent'
|
|
258
|
+
kubbVersion: string
|
|
259
|
+
plugins?: TelemetryPlugin[]
|
|
260
|
+
hrStart: [number, number]
|
|
261
|
+
filesCreated?: number
|
|
262
|
+
status: 'success' | 'failed'
|
|
263
|
+
}): TelemetryEvent {
|
|
264
|
+
const [seconds, nanoseconds] = process.hrtime(options.hrStart)
|
|
265
|
+
const duration = Math.round(seconds * 1000 + nanoseconds / 1e6)
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
command: options.command,
|
|
269
|
+
kubbVersion: options.kubbVersion,
|
|
270
|
+
nodeVersion: process.versions.node.split('.')[0] ?? 'unknown',
|
|
271
|
+
platform: os.platform(),
|
|
272
|
+
ci: isCi(),
|
|
273
|
+
plugins: options.plugins ?? [],
|
|
274
|
+
duration,
|
|
275
|
+
filesCreated: options.filesCreated ?? 0,
|
|
276
|
+
status: options.status,
|
|
277
|
+
}
|
|
278
|
+
}
|