@kubb/cli 5.14.6 → 5.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/kubb.js +2 -1
- package/dist/{Telemetry-B5V_ufmv.cjs → Telemetry-B52pKz9i.cjs} +14 -118
- package/dist/Telemetry-B52pKz9i.cjs.map +1 -0
- package/dist/{Telemetry-DrnGartf.js → Telemetry-C7Qwy2s7.js} +12 -86
- package/dist/Telemetry-C7Qwy2s7.js.map +1 -0
- package/dist/{background-DlK0by5c.js → background-BJCuXYuB.js} +5 -6
- package/dist/{background-DlK0by5c.js.map → background-BJCuXYuB.js.map} +1 -1
- package/dist/{background-CdPT1eaq.cjs → background-CK2gQtTU.cjs} +5 -6
- package/dist/{background-CdPT1eaq.cjs.map → background-CK2gQtTU.cjs.map} +1 -1
- package/dist/{background-C6XHpEo7.js → background-D4mZCg-4.js} +1 -1
- package/dist/banner-BdTTDFkG.js +140 -0
- package/dist/banner-BdTTDFkG.js.map +1 -0
- package/dist/banner-wJFk-L-c.cjs +163 -0
- package/dist/banner-wJFk-L-c.cjs.map +1 -0
- package/dist/config-BL3HnFhb.js +1 -0
- package/dist/config-BWj9jBVj.cjs +62 -0
- package/dist/config-BWj9jBVj.cjs.map +1 -0
- package/dist/{config-CNqgDSu3.js → config-CQTxXvIG.js} +6 -52
- package/dist/config-CQTxXvIG.js.map +1 -0
- package/dist/{config-BG_Nr2CZ.cjs → config-CRrmj88c.cjs} +15 -62
- package/dist/config-CRrmj88c.cjs.map +1 -0
- package/dist/config-Uk3rbZfb.js +49 -0
- package/dist/config-Uk3rbZfb.js.map +1 -0
- package/dist/configFile-C0DIXNMD.js +993 -0
- package/dist/configFile-C0DIXNMD.js.map +1 -0
- package/dist/configFile-CoEpHdXG.cjs +1028 -0
- package/dist/configFile-CoEpHdXG.cjs.map +1 -0
- package/dist/{credentials-C8tjAhTo.js → credentials-B77iJx9H.js} +1 -1
- package/dist/{credentials-DDaKqtJn.cjs → credentials-DEsGwcGD.cjs} +3 -4
- package/dist/{credentials-DDaKqtJn.cjs.map → credentials-DEsGwcGD.cjs.map} +1 -1
- package/dist/{credentials-CZK6zPLv.js → credentials-osgjujE_.js} +3 -4
- package/dist/{credentials-CZK6zPLv.js.map → credentials-osgjujE_.js.map} +1 -1
- package/dist/env-C3xHRMez.js +1 -0
- package/dist/env-eu3XyKqJ.cjs +55 -0
- package/dist/env-eu3XyKqJ.cjs.map +1 -0
- package/dist/env-rv-VgSGN.js +34 -0
- package/dist/env-rv-VgSGN.js.map +1 -0
- package/dist/{errors-Cl69p_vI.cjs → errors-BponpdjN.cjs} +3 -4
- package/dist/{errors-Cl69p_vI.cjs.map → errors-BponpdjN.cjs.map} +1 -1
- package/dist/{errors-DmC83ALX.js → errors-wzqmMpMo.js} +3 -4
- package/dist/{errors-DmC83ALX.js.map → errors-wzqmMpMo.js.map} +1 -1
- package/dist/index.cjs +37 -2719
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +35 -2713
- package/dist/index.js.map +1 -1
- package/dist/installWithSpinner-DpnpxHq5.js +49 -0
- package/dist/installWithSpinner-DpnpxHq5.js.map +1 -0
- package/dist/installWithSpinner-ONGfIVUJ.cjs +56 -0
- package/dist/installWithSpinner-ONGfIVUJ.cjs.map +1 -0
- package/dist/{text-CL6N6yPS.js → intro-9PB8sE8z.js} +8 -52
- package/dist/intro-9PB8sE8z.js.map +1 -0
- package/dist/{text-DrsG1EDN.cjs → intro-YPaf1Imr.cjs} +12 -80
- package/dist/intro-YPaf1Imr.cjs.map +1 -0
- package/dist/isCIEnvironment-D6xtAAZ2.js +14 -0
- package/dist/isCIEnvironment-D6xtAAZ2.js.map +1 -0
- package/dist/isCIEnvironment-DiVOMI5Q.cjs +20 -0
- package/dist/isCIEnvironment-DiVOMI5Q.cjs.map +1 -0
- package/dist/mascot--q1xjKw1.js +63 -0
- package/dist/mascot--q1xjKw1.js.map +1 -0
- package/dist/mascot-JSA0j3Q_.cjs +80 -0
- package/dist/mascot-JSA0j3Q_.cjs.map +1 -0
- package/dist/mcp-CcseJJwh.cjs +20 -0
- package/dist/mcp-CcseJJwh.cjs.map +1 -0
- package/dist/mcp-DUTsS8tR.js +21 -0
- package/dist/mcp-DUTsS8tR.js.map +1 -0
- package/dist/package-B1hUUjio.cjs +10 -0
- package/dist/package-B1hUUjio.cjs.map +1 -0
- package/dist/package-MYPMnnTX.js +5 -0
- package/dist/package-MYPMnnTX.js.map +1 -0
- package/dist/{registry-DOGpflct.js → registry-B3eYk4LN.js} +7 -67
- package/dist/registry-B3eYk4LN.js.map +1 -0
- package/dist/{registry-BzBp6aZI.cjs → registry-YE1pFWL-.cjs} +4 -82
- package/dist/registry-YE1pFWL-.cjs.map +1 -0
- package/dist/{reporters-GzXlc4CK.cjs → reporters-2cgjvXwR.cjs} +32 -40
- package/dist/{reporters-GzXlc4CK.cjs.map → reporters-2cgjvXwR.cjs.map} +1 -1
- package/dist/{reporters-mYHIUZSx.js → reporters-BM430HdQ.js} +19 -27
- package/dist/{reporters-mYHIUZSx.js.map → reporters-BM430HdQ.js.map} +1 -1
- package/dist/{rolldown-runtime-CPE0t6oh.cjs → rolldown-runtime-C3-Zg-KD.cjs} +1 -2
- package/dist/{rolldown-runtime-CRm0XQPb.js → rolldown-runtime-xo6NMXYd.js} +1 -2
- package/dist/run-1H9TGT8v.js +77 -0
- package/dist/run-1H9TGT8v.js.map +1 -0
- package/dist/run-BYP_t9dR.cjs +324 -0
- package/dist/run-BYP_t9dR.cjs.map +1 -0
- package/dist/run-Bq6gcfkS.js +1 -0
- package/dist/run-BrM7-NSV.js +181 -0
- package/dist/run-BrM7-NSV.js.map +1 -0
- package/dist/run-BsrNfFIl.js +228 -0
- package/dist/run-BsrNfFIl.js.map +1 -0
- package/dist/{run-l8IiOR_c.js → run-CLiHI99L.js} +9 -9
- package/dist/{run-l8IiOR_c.js.map → run-CLiHI99L.js.map} +1 -1
- package/dist/{run-C8Prej7s.cjs → run-CQORv7Gq.cjs} +44 -37
- package/dist/run-CQORv7Gq.cjs.map +1 -0
- package/dist/{run-EJtgdIin.js → run-CXVhxFfC.js} +31 -23
- package/dist/run-CXVhxFfC.js.map +1 -0
- package/dist/run-CrM4jsix.cjs +80 -0
- package/dist/run-CrM4jsix.cjs.map +1 -0
- package/dist/run-CvFakE3B.cjs +185 -0
- package/dist/run-CvFakE3B.cjs.map +1 -0
- package/dist/{run-DC_xH5H3.cjs → run-CyzKy-0p.cjs} +49 -41
- package/dist/run-CyzKy-0p.cjs.map +1 -0
- package/dist/run-D-nn9-Wq.js +319 -0
- package/dist/run-D-nn9-Wq.js.map +1 -0
- package/dist/run-DBnSbwtU.cjs +389 -0
- package/dist/run-DBnSbwtU.cjs.map +1 -0
- package/dist/{run-j1g5si5R.js → run-DTqMrPS9.js} +5 -6
- package/dist/{run-j1g5si5R.js.map → run-DTqMrPS9.js.map} +1 -1
- package/dist/{run-9fs2T2Je.js → run-DUwX6Icx.js} +26 -20
- package/dist/run-DUwX6Icx.js.map +1 -0
- package/dist/run-FEiyv6_U.js +385 -0
- package/dist/run-FEiyv6_U.js.map +1 -0
- package/dist/run-LY_UAYyM.cjs +233 -0
- package/dist/run-LY_UAYyM.cjs.map +1 -0
- package/dist/{run-sjzj0ir-.cjs → run-dMTJfs4_.cjs} +10 -10
- package/dist/{run-sjzj0ir-.cjs.map → run-dMTJfs4_.cjs.map} +1 -1
- package/dist/{run-CypFtkVE.cjs → run-uPA5s3rI.cjs} +5 -6
- package/dist/{run-CypFtkVE.cjs.map → run-uPA5s3rI.cjs.map} +1 -1
- package/dist/shared-C0EtrdUm.cjs +123 -0
- package/dist/shared-C0EtrdUm.cjs.map +1 -0
- package/dist/shared-Ct9stKpp.js +94 -0
- package/dist/shared-Ct9stKpp.js.map +1 -0
- package/dist/{snapshot-laSRD0z1.cjs → snapshot-CPF9zyZj.cjs} +13 -14
- package/dist/{snapshot-laSRD0z1.cjs.map → snapshot-CPF9zyZj.cjs.map} +1 -1
- package/dist/{snapshot-BJWnL6hE.js → snapshot-CiV08NJr.js} +10 -11
- package/dist/{snapshot-BJWnL6hE.js.map → snapshot-CiV08NJr.js.map} +1 -1
- package/dist/spinner-BZaln7O8.js +160 -0
- package/dist/spinner-BZaln7O8.js.map +1 -0
- package/dist/spinner-CfB0sIPy.cjs +208 -0
- package/dist/spinner-CfB0sIPy.cjs.map +1 -0
- package/dist/{status-DFEdbBPu.cjs → status-C066YA-4.cjs} +7 -8
- package/dist/{status-DFEdbBPu.cjs.map → status-C066YA-4.cjs.map} +1 -1
- package/dist/{status-BjmXLRm2.js → status-Dj8tivag.js} +6 -7
- package/dist/{status-BjmXLRm2.js.map → status-Dj8tivag.js.map} +1 -1
- package/dist/studio-B4XsXgKj.js +215 -0
- package/dist/studio-B4XsXgKj.js.map +1 -0
- package/dist/studio-D9_-mKee.cjs +218 -0
- package/dist/studio-D9_-mKee.cjs.map +1 -0
- package/dist/studioWorker.js +4 -5
- package/dist/{summary-DDnI70tn.js → summary-CbmQKA8l.js} +5 -6
- package/dist/{summary-DDnI70tn.js.map → summary-CbmQKA8l.js.map} +1 -1
- package/dist/{summary-1DCwzWEU.cjs → summary-Clvl5Hxs.cjs} +7 -8
- package/dist/{summary-1DCwzWEU.cjs.map → summary-Clvl5Hxs.cjs.map} +1 -1
- package/dist/table-BlRe9YTZ.cjs +15 -0
- package/dist/table-BlRe9YTZ.cjs.map +1 -0
- package/dist/table-CR7iZJGo.js +10 -0
- package/dist/table-CR7iZJGo.js.map +1 -0
- package/dist/text-BmiI4s6N.js +13 -0
- package/dist/text-BmiI4s6N.js.map +1 -0
- package/dist/text-Ep3AulAS.cjs +24 -0
- package/dist/text-Ep3AulAS.cjs.map +1 -0
- package/dist/time-3BKoci3G.cjs +82 -0
- package/dist/time-3BKoci3G.cjs.map +1 -0
- package/dist/time-C-wU7VhC.js +65 -0
- package/dist/time-C-wU7VhC.js.map +1 -0
- package/dist/utils-BdibacYq.js +244 -0
- package/dist/utils-BdibacYq.js.map +1 -0
- package/dist/utils-CO89mxRU.cjs +290 -0
- package/dist/utils-CO89mxRU.cjs.map +1 -0
- package/dist/utils-CbJ6hS_t.js +222 -0
- package/dist/utils-CbJ6hS_t.js.map +1 -0
- package/dist/utils-CcARPB45.cjs +286 -0
- package/dist/utils-CcARPB45.cjs.map +1 -0
- package/dist/validate-B3tLWYJU.cjs +27 -0
- package/dist/validate-B3tLWYJU.cjs.map +1 -0
- package/dist/validate-C5fyNF4v.js +28 -0
- package/dist/validate-C5fyNF4v.js.map +1 -0
- package/package.json +7 -7
- package/dist/Telemetry-B5V_ufmv.cjs.map +0 -1
- package/dist/Telemetry-DrnGartf.js.map +0 -1
- package/dist/config-BG_Nr2CZ.cjs.map +0 -1
- package/dist/config-CNqgDSu3.js.map +0 -1
- package/dist/config-DWDpFSn1.js +0 -1
- package/dist/registry-BzBp6aZI.cjs.map +0 -1
- package/dist/registry-DOGpflct.js.map +0 -1
- package/dist/run-9fs2T2Je.js.map +0 -1
- package/dist/run-C8Prej7s.cjs.map +0 -1
- package/dist/run-CXVme-aS.js +0 -1
- package/dist/run-DC_xH5H3.cjs.map +0 -1
- package/dist/run-EJtgdIin.js.map +0 -1
- package/dist/text-CL6N6yPS.js.map +0 -1
- package/dist/text-DrsG1EDN.cjs.map +0 -1
- package/dist/utils-C3vTpUje.cjs +0 -645
- package/dist/utils-C3vTpUje.cjs.map +0 -1
- package/dist/utils-CIc8YFtr.js +0 -536
- package/dist/utils-CIc8YFtr.js.map +0 -1
package/bin/kubb.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// Mapping 35 bundles costs 10 to 30 ms per run, so only debug runs pay for readable stack traces.
|
|
4
|
+
if (['1', 'true'].includes(process.env.KUBB_DEBUG ?? '')) process.setSourceMapsEnabled?.(true)
|
|
4
5
|
process.title = 'Kubb'
|
|
5
6
|
|
|
6
7
|
try {
|
|
@@ -1,87 +1,14 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C3-Zg-KD.cjs");
|
|
2
|
+
const require_isCIEnvironment = require("./isCIEnvironment-DiVOMI5Q.cjs");
|
|
3
|
+
const require_time = require("./time-3BKoci3G.cjs");
|
|
4
|
+
const require_package = require("./package-B1hUUjio.cjs");
|
|
2
5
|
let node_crypto = require("node:crypto");
|
|
3
6
|
let node_os = require("node:os");
|
|
4
7
|
node_os = require_rolldown_runtime.__toESM(node_os, 1);
|
|
5
8
|
let node_process = require("node:process");
|
|
6
9
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
7
10
|
let gunshi_agent = require("gunshi/agent");
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Returns `true` when a known CI provider has set its environment variable.
|
|
11
|
-
* `CI=false` and `CI=0` count as not CI, so a user can opt out locally.
|
|
12
|
-
*/
|
|
13
|
-
function isCIEnvironment() {
|
|
14
|
-
const ci = node_process.default.env.CI;
|
|
15
|
-
return !!(ci && ci !== "false" && ci !== "0" || node_process.default.env.GITHUB_ACTIONS || node_process.default.env.GITLAB_CI || node_process.default.env.BITBUCKET_BUILD_NUMBER || node_process.default.env.JENKINS_URL || node_process.default.env.CIRCLECI || node_process.default.env.TRAVIS || node_process.default.env.TEAMCITY_VERSION || node_process.default.env.BUILDKITE || node_process.default.env.TF_BUILD);
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region ../../internals/utils/src/runtime.ts
|
|
19
|
-
const isBun = typeof Bun !== "undefined";
|
|
20
|
-
const isDeno = "Deno" in globalThis;
|
|
21
|
-
/**
|
|
22
|
-
* The JavaScript runtime executing the current process: its name, version and whether it is Bun.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```ts
|
|
26
|
-
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
const runtime = {
|
|
30
|
-
isBun,
|
|
31
|
-
...detect()
|
|
32
|
-
};
|
|
33
|
-
function detect() {
|
|
34
|
-
if (isBun) return {
|
|
35
|
-
name: "bun",
|
|
36
|
-
version: process.versions.bun ?? ""
|
|
37
|
-
};
|
|
38
|
-
if (isDeno) return {
|
|
39
|
-
name: "deno",
|
|
40
|
-
version: globalThis.Deno?.version?.deno ?? ""
|
|
41
|
-
};
|
|
42
|
-
return {
|
|
43
|
-
name: "node",
|
|
44
|
-
version: process.versions?.node ?? ""
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region ../../internals/utils/src/time.ts
|
|
49
|
-
/**
|
|
50
|
-
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
51
|
-
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* const start = process.hrtime()
|
|
56
|
-
* doWork()
|
|
57
|
-
* getElapsedMs(start) // 42.35
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
function getElapsedMs(hrStart) {
|
|
61
|
-
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
62
|
-
const ms = seconds * 1e3 + nanoseconds / 1e6;
|
|
63
|
-
return Math.round(ms * 100) / 100;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```ts
|
|
70
|
-
* formatMs(250) // '250ms'
|
|
71
|
-
* formatMs(1500) // '1.50s'
|
|
72
|
-
* formatMs(90000) // '1m 30.0s'
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
function formatMs(ms) {
|
|
76
|
-
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
77
|
-
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
78
|
-
return `${Math.round(ms)}ms`;
|
|
79
|
-
}
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region package.json
|
|
82
|
-
var version = "5.14.6";
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/Telemetry.ts
|
|
11
|
+
// src/Telemetry.ts
|
|
85
12
|
/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */
|
|
86
13
|
const OTLP_ENDPOINT = "https://otlp.kubb.dev";
|
|
87
14
|
/**
|
|
@@ -94,7 +21,7 @@ function isDisabled() {
|
|
|
94
21
|
function trackRun({ command, hrStart }) {
|
|
95
22
|
return (result) => sendTelemetry(buildOtlpPayload({
|
|
96
23
|
command,
|
|
97
|
-
duration: Math.round(getElapsedMs(hrStart)),
|
|
24
|
+
duration: Math.round(require_time.getElapsedMs(hrStart)),
|
|
98
25
|
...result
|
|
99
26
|
}));
|
|
100
27
|
}
|
|
@@ -118,7 +45,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
118
45
|
},
|
|
119
46
|
{
|
|
120
47
|
key: "kubb.version",
|
|
121
|
-
value: { stringValue: version }
|
|
48
|
+
value: { stringValue: require_package.version }
|
|
122
49
|
},
|
|
123
50
|
{
|
|
124
51
|
key: "kubb.node_version",
|
|
@@ -126,11 +53,11 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
126
53
|
},
|
|
127
54
|
{
|
|
128
55
|
key: "kubb.runtime",
|
|
129
|
-
value: { stringValue: runtime.name }
|
|
56
|
+
value: { stringValue: require_time.runtime.name }
|
|
130
57
|
},
|
|
131
58
|
{
|
|
132
59
|
key: "kubb.runtime_version",
|
|
133
|
-
value: { stringValue: runtime.version.split(".")[0] }
|
|
60
|
+
value: { stringValue: require_time.runtime.version.split(".")[0] }
|
|
134
61
|
},
|
|
135
62
|
{
|
|
136
63
|
key: "kubb.platform",
|
|
@@ -138,7 +65,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
138
65
|
},
|
|
139
66
|
{
|
|
140
67
|
key: "kubb.ci",
|
|
141
|
-
value: { boolValue: isCIEnvironment() }
|
|
68
|
+
value: { boolValue: require_isCIEnvironment.isCIEnvironment() }
|
|
142
69
|
},
|
|
143
70
|
{
|
|
144
71
|
key: "kubb.tty",
|
|
@@ -190,7 +117,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
190
117
|
},
|
|
191
118
|
{
|
|
192
119
|
key: "service.version",
|
|
193
|
-
value: { stringValue: version }
|
|
120
|
+
value: { stringValue: require_package.version }
|
|
194
121
|
},
|
|
195
122
|
{
|
|
196
123
|
key: "telemetry.sdk.language",
|
|
@@ -200,7 +127,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
200
127
|
scopeSpans: [{
|
|
201
128
|
scope: {
|
|
202
129
|
name: "kubb-core",
|
|
203
|
-
version
|
|
130
|
+
version: require_package.version
|
|
204
131
|
},
|
|
205
132
|
spans: [{
|
|
206
133
|
traceId,
|
|
@@ -231,52 +158,21 @@ async function sendTelemetry(payload) {
|
|
|
231
158
|
"Kubb-Telemetry-Source": "kubb-cli"
|
|
232
159
|
},
|
|
233
160
|
body: JSON.stringify(payload),
|
|
234
|
-
signal: AbortSignal.timeout(
|
|
161
|
+
signal: AbortSignal.timeout(1e3)
|
|
235
162
|
});
|
|
236
163
|
} catch (_e) {}
|
|
237
164
|
}
|
|
238
|
-
//#endregion
|
|
239
|
-
Object.defineProperty(exports, "formatMs", {
|
|
240
|
-
enumerable: true,
|
|
241
|
-
get: function() {
|
|
242
|
-
return formatMs;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
Object.defineProperty(exports, "getElapsedMs", {
|
|
246
|
-
enumerable: true,
|
|
247
|
-
get: function() {
|
|
248
|
-
return getElapsedMs;
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
Object.defineProperty(exports, "isCIEnvironment", {
|
|
252
|
-
enumerable: true,
|
|
253
|
-
get: function() {
|
|
254
|
-
return isCIEnvironment;
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
165
|
Object.defineProperty(exports, "isDisabled", {
|
|
258
166
|
enumerable: true,
|
|
259
167
|
get: function() {
|
|
260
168
|
return isDisabled;
|
|
261
169
|
}
|
|
262
170
|
});
|
|
263
|
-
Object.defineProperty(exports, "runtime", {
|
|
264
|
-
enumerable: true,
|
|
265
|
-
get: function() {
|
|
266
|
-
return runtime;
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
171
|
Object.defineProperty(exports, "trackRun", {
|
|
270
172
|
enumerable: true,
|
|
271
173
|
get: function() {
|
|
272
174
|
return trackRun;
|
|
273
175
|
}
|
|
274
176
|
});
|
|
275
|
-
Object.defineProperty(exports, "version", {
|
|
276
|
-
enumerable: true,
|
|
277
|
-
get: function() {
|
|
278
|
-
return version;
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
177
|
|
|
282
|
-
//# sourceMappingURL=Telemetry-
|
|
178
|
+
//# sourceMappingURL=Telemetry-B52pKz9i.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry-B52pKz9i.cjs","names":["process","getElapsedMs","randomBytes","getAgentProfile","version","runtime","os","isCIEnvironment"],"sources":["../src/Telemetry.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n // Short, since an unfinished request keeps the process alive after the run.\n signal: AbortSignal.timeout(1_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,92 +1,19 @@
|
|
|
1
|
-
import "./rolldown-runtime-
|
|
1
|
+
import "./rolldown-runtime-xo6NMXYd.js";
|
|
2
|
+
import { t as isCIEnvironment } from "./isCIEnvironment-D6xtAAZ2.js";
|
|
3
|
+
import { n as getElapsedMs, r as runtime } from "./time-C-wU7VhC.js";
|
|
4
|
+
import { t as version } from "./package-MYPMnnTX.js";
|
|
2
5
|
import { randomBytes } from "node:crypto";
|
|
3
6
|
import os from "node:os";
|
|
4
|
-
import process
|
|
7
|
+
import process from "node:process";
|
|
5
8
|
import { getAgentProfile } from "gunshi/agent";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Returns `true` when a known CI provider has set its environment variable.
|
|
9
|
-
* `CI=false` and `CI=0` count as not CI, so a user can opt out locally.
|
|
10
|
-
*/
|
|
11
|
-
function isCIEnvironment() {
|
|
12
|
-
const ci = process$1.env.CI;
|
|
13
|
-
return !!(ci && ci !== "false" && ci !== "0" || process$1.env.GITHUB_ACTIONS || process$1.env.GITLAB_CI || process$1.env.BITBUCKET_BUILD_NUMBER || process$1.env.JENKINS_URL || process$1.env.CIRCLECI || process$1.env.TRAVIS || process$1.env.TEAMCITY_VERSION || process$1.env.BUILDKITE || process$1.env.TF_BUILD);
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region ../../internals/utils/src/runtime.ts
|
|
17
|
-
const isBun = typeof Bun !== "undefined";
|
|
18
|
-
const isDeno = "Deno" in globalThis;
|
|
19
|
-
/**
|
|
20
|
-
* The JavaScript runtime executing the current process: its name, version and whether it is Bun.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
const runtime = {
|
|
28
|
-
isBun,
|
|
29
|
-
...detect()
|
|
30
|
-
};
|
|
31
|
-
function detect() {
|
|
32
|
-
if (isBun) return {
|
|
33
|
-
name: "bun",
|
|
34
|
-
version: process.versions.bun ?? ""
|
|
35
|
-
};
|
|
36
|
-
if (isDeno) return {
|
|
37
|
-
name: "deno",
|
|
38
|
-
version: globalThis.Deno?.version?.deno ?? ""
|
|
39
|
-
};
|
|
40
|
-
return {
|
|
41
|
-
name: "node",
|
|
42
|
-
version: process.versions?.node ?? ""
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region ../../internals/utils/src/time.ts
|
|
47
|
-
/**
|
|
48
|
-
* Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.
|
|
49
|
-
* Rounds to 2 decimal places for sub-millisecond precision without noise.
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```ts
|
|
53
|
-
* const start = process.hrtime()
|
|
54
|
-
* doWork()
|
|
55
|
-
* getElapsedMs(start) // 42.35
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
function getElapsedMs(hrStart) {
|
|
59
|
-
const [seconds, nanoseconds] = process.hrtime(hrStart);
|
|
60
|
-
const ms = seconds * 1e3 + nanoseconds / 1e6;
|
|
61
|
-
return Math.round(ms * 100) / 100;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```ts
|
|
68
|
-
* formatMs(250) // '250ms'
|
|
69
|
-
* formatMs(1500) // '1.50s'
|
|
70
|
-
* formatMs(90000) // '1m 30.0s'
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
function formatMs(ms) {
|
|
74
|
-
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
75
|
-
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
76
|
-
return `${Math.round(ms)}ms`;
|
|
77
|
-
}
|
|
78
|
-
//#endregion
|
|
79
|
-
//#region package.json
|
|
80
|
-
var version = "5.14.6";
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/Telemetry.ts
|
|
9
|
+
// src/Telemetry.ts
|
|
83
10
|
/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */
|
|
84
11
|
const OTLP_ENDPOINT = "https://otlp.kubb.dev";
|
|
85
12
|
/**
|
|
86
13
|
* Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.
|
|
87
14
|
*/
|
|
88
15
|
function isDisabled() {
|
|
89
|
-
return ["DO_NOT_TRACK", "KUBB_DISABLE_TELEMETRY"].some((name) => ["1", "true"].includes(process
|
|
16
|
+
return ["DO_NOT_TRACK", "KUBB_DISABLE_TELEMETRY"].some((name) => ["1", "true"].includes(process.env[name] ?? ""));
|
|
90
17
|
}
|
|
91
18
|
/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */
|
|
92
19
|
function trackRun({ command, hrStart }) {
|
|
@@ -120,7 +47,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
120
47
|
},
|
|
121
48
|
{
|
|
122
49
|
key: "kubb.node_version",
|
|
123
|
-
value: { stringValue: process
|
|
50
|
+
value: { stringValue: process.versions.node.split(".")[0] }
|
|
124
51
|
},
|
|
125
52
|
{
|
|
126
53
|
key: "kubb.runtime",
|
|
@@ -140,7 +67,7 @@ function buildOtlpPayload({ command, duration, status, plugins = [], filesCreate
|
|
|
140
67
|
},
|
|
141
68
|
{
|
|
142
69
|
key: "kubb.tty",
|
|
143
|
-
value: { boolValue: process
|
|
70
|
+
value: { boolValue: process.stdin.isTTY === true }
|
|
144
71
|
},
|
|
145
72
|
...agent.isAgent && agent.name ? [{
|
|
146
73
|
key: "kubb.agent",
|
|
@@ -229,11 +156,10 @@ async function sendTelemetry(payload) {
|
|
|
229
156
|
"Kubb-Telemetry-Source": "kubb-cli"
|
|
230
157
|
},
|
|
231
158
|
body: JSON.stringify(payload),
|
|
232
|
-
signal: AbortSignal.timeout(
|
|
159
|
+
signal: AbortSignal.timeout(1e3)
|
|
233
160
|
});
|
|
234
161
|
} catch (_e) {}
|
|
235
162
|
}
|
|
236
|
-
|
|
237
|
-
export { getElapsedMs as a, formatMs as i, trackRun as n, runtime as o, version as r, isCIEnvironment as s, isDisabled as t };
|
|
163
|
+
export { trackRun as n, isDisabled as t };
|
|
238
164
|
|
|
239
|
-
//# sourceMappingURL=Telemetry-
|
|
165
|
+
//# sourceMappingURL=Telemetry-C7Qwy2s7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Telemetry-C7Qwy2s7.js","names":[],"sources":["../src/Telemetry.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n // Short, since an unfinished request keeps the process alive after the run.\n signal: AbortSignal.timeout(1_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./rolldown-runtime-
|
|
2
|
-
import { o as prepareConnection } from "./run-
|
|
3
|
-
import { i as getProjectKubbHome } from "./credentials-
|
|
1
|
+
import "./rolldown-runtime-xo6NMXYd.js";
|
|
2
|
+
import { o as prepareConnection } from "./run-DUwX6Icx.js";
|
|
3
|
+
import { i as getProjectKubbHome } from "./credentials-osgjujE_.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { hash, randomBytes } from "node:crypto";
|
|
6
6
|
import { x } from "tinyexec";
|
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
10
10
|
import { Diagnostics } from "@kubb/core";
|
|
11
11
|
import { connect, createServer } from "node:net";
|
|
12
|
-
|
|
12
|
+
// src/runners/studio/background.ts
|
|
13
13
|
const states = [
|
|
14
14
|
"starting",
|
|
15
15
|
"connected",
|
|
@@ -269,7 +269,6 @@ async function startWorker(options) {
|
|
|
269
269
|
throw error;
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
|
-
//#endregion
|
|
273
272
|
export { stopWorker as a, startWorker as i, getWorkerStatus as n, serveWorker as r, getWorkerLogPath as t };
|
|
274
273
|
|
|
275
|
-
//# sourceMappingURL=background-
|
|
274
|
+
//# sourceMappingURL=background-BJCuXYuB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-DlK0by5c.js","names":["connectSocket","delay"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"background-BJCuXYuB.js","names":["connectSocket","delay"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,MAAM,SAA6B;CAAC;CAAY;CAAa;CAAgB;CAA2B;AAAS;AACjH,MAAa,yBAAyB,KAAK,KAAK,mBAAmB,GAAG,YAAY;AAClF,MAAM,kBAAkB,KAAK,KAAK,mBAAmB,GAAG,aAAa;AAErE,eAAe,aAA2C;CACxD,IAAI;EACF,MAAM,QAAiB,KAAK,MAAM,MAAM,SAAS,UAAU,GAAG,MAAM,CAAC;EACrE,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;EAChD,MAAM,SAAS;EACf,OAAO,OAAO,OAAO,OAAO,YAC1B,iBAAiB,KAAK,OAAO,EAAE,KAC/B,OAAO,UAAU,OAAO,GAAG,MAC1B,OAAO,OAAO,KAAK,KACpB,OAAO,UAAU,OAAO,IAAI,MAC3B,OAAO,QAAQ,KAAK,MACpB,OAAO,QAAQ,MAAM,SACtB,OAAO,SAAS,OAAO,KAAoB,IACxC,SACD;CACN,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,QAAQ,KAAsB;CACrC,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,OAAO;EACd,OAAQ,MAAgC,SAAS;CACnD;AACF;;AAGA,SAAS,cAAc,QAAsB,SAAmD;CAC9F,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,SAASA,QAAc;GAAE,MAAM;GAAa,MAAM,OAAO;EAAK,CAAC;EACrE,IAAI,WAAW;EACf,OAAO,WAAW,WAAa,OAAO,wBAAQ,IAAI,MAAM,0BAA0B,CAAC,CAAC;EACpF,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,KAAK,iBAAiB,OAAO,MAAM,KAAK,UAAU;GAAE,IAAI,OAAO;GAAI;EAAQ,CAAC,IAAI,IAAI,CAAC;EAC5F,OAAO,GAAG,SAAS,UAAkB;GACnC,YAAY,MAAM,SAAS;GAC3B,IAAI,SAAS,SAAS,MAAM,OAAO,wBAAQ,IAAI,MAAM,yBAAyB,CAAC;EACjF,CAAC;EACD,OAAO,KAAK,aAAa;GACvB,IAAI;IACF,MAAM,WAAW,KAAK,MAAM,QAAQ;IACpC,IAAI,SAAS,OAAO,OAAO,MAAM,SAAS,QAAQ,OAAO,OAAO,CAAC,OAAO,SAAS,SAAS,KAAK,GAC7F,MAAM,IAAI,YAAY,MAAM;KAAE,MAAM,YAAY,KAAK;KAAe,UAAU;KAAS,SAAS;IAA0B,CAAC;IAC7H,QAAQ,QAAQ;GAClB,SAAS,OAAO;IACd,OAAO,KAAK;GACd;EACF,CAAC;CACH,CAAC;AACH;AAEA,eAAsB,kBAAuG;CAC3H,MAAM,SAAS,MAAM,WAAW;CAChC,IAAI,CAAC,QAAQ,OAAO;EAAE,SAAS;EAAO,OAAO;CAAU;CACvD,IAAI;EACF,MAAM,WAAW,MAAM,cAAc,QAAQ,QAAQ;EACrD,OAAO;GAAE,SAAS;GAAM,OAAO,SAAS;GAAO,KAAK,SAAS;GAAK,WAAW,OAAO,SAAS,SAAS,SAAS,IAAI,SAAS,YAAY,KAAA;EAAU;CACpJ,QAAQ;EACN,OAAO;GAAE,SAAS;GAAO,OAAO,OAAO,UAAU,4BAA4B,OAAO,QAAQ;EAAU;CACxG;AACF;AAEA,eAAsB,aAA4B;CAChD,MAAM,SAAS,MAAM,WAAW;CAChC,IAAI,CAAC,QAAQ;CACb,IAAI;EACF,MAAM,cAAc,QAAQ,MAAM;CACpC,SAAS,OAAO;EACd,IACG,MAAgC,SAAS,kBAC1C,QAAQ,OAAO,GAAG,KAClB,OAAO,UAAU,aACjB,OAAO,UAAU,2BAEjB,MAAM,IAAI,YAAY,MAAM;GAC1B,MAAM,YAAY,KAAK;GACvB,UAAU;GACV,SAAS;GACT,MAAM;EACR,CAAC;EAEH;CACF;CACA,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,IAAI;GACF,MAAM,cAAc,QAAQ,QAAQ;EACtC,QAAQ;GACN;EACF;EACA,MAAMC,aAAM,GAAG;CACjB;CACA,MAAM,IAAI,YAAY,MAAM;EAC1B,MAAM,YAAY,KAAK;EACvB,UAAU;EACV,SAAS;CACX,CAAC;AACH;;AAGA,eAAsB,YAAY,UAA2B;CAC3D,MAAM,SAAuB;EAAE,IAAI,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;EAAG,KAAK,QAAQ;EAAK,MAAM;EAAG,OAAO;EAAY,WAAW,KAAK,IAAI;CAAE;CACxI,MAAM,SAAS,cAAc,WAAW;EACtC,IAAI,QAAQ;EACZ,OAAO,WAAW,WAAa,OAAO,QAAQ,CAAC;EAC/C,OAAO,GAAG,eAAe,CAAC,CAAC;EAC3B,OAAO,GAAG,SAAS,UAAkB;GACnC,SAAS,MAAM,SAAS;GACxB,IAAI,MAAM,SAAS,MAAM;IACvB,OAAO,QAAQ;IACf;GACF;GACA,IAAI,CAAC,MAAM,SAAS,IAAI,GAAG;GAC3B,IAAI;IACF,MAAM,UAAU,KAAK,MAAM,KAAK;IAChC,IAAI,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC,UAAU,MAAM,CAAC,CAAC,SAAS,OAAO,QAAQ,OAAO,CAAC,GAAG;KACrF,OAAO,QAAQ;KACf;IACF;IACA,OAAO,IAAI,KAAK,UAAU,MAAM,CAAC;IACjC,IAAI,QAAQ,YAAY,QAAQ,SAAS,MAAM;GACjD,QAAQ;IACN,OAAO,QAAQ;GACjB;EACF,CAAC;CACH,CAAC;CAED,MAAM,OAAO,MAAU,OAAO,SAAS,KAAK,UAAU,mBAAmB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;CAC/F,IAAI;EACF,MAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,OAAO,KAAK,SAAS,MAAM;GAC3B,OAAO,OAAO,MAAM,aAAa,OAAO;EAC1C,CAAC;CACH,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,cAAc,MAAM;EAElE,MAAMA,aAAM,GAAG;EACf,MAAM,WAAW,MAAM,WAAW;EAClC,IAAI,UAAU,SAAS,MACrB,IAAI;GACF,OAAO,EAAE,UAAU,MAAM,cAAc,UAAU,QAAQ,EAAE;EAC7D,QAAQ,CAER;EAEF,MAAM,IAAI,YAAY,MAAM;GAC1B,MAAM,YAAY,KAAK;GACvB,UAAU;GACV,SAAS;EACX,CAAC;CACH;CACA,OAAO,OAAO;CACd,IAAI,SAAS,QAAQ,QAAQ;CAC7B,MAAM,aAAa;EACjB,MAAM,QAAQ,KAAK,UAAU,MAAM;EACnC,SAAS,OAAO,KAAK,YAAY;GAC/B,MAAM,YAAY,GAAG,UAAU,EAAE,GAAG,OAAO;GAC3C,MAAM,UAAU,WAAW,OAAO,EAAE,MAAM,IAAM,CAAC;GACjD,MAAM,OAAO,WAAW,UAAU,CAAC;EACrC,CAAC;EACD,OAAO;CACT;CACA,IAAI;EACF,MAAM,KAAK;CACb,SAAS,OAAO;EACd,OAAO,MAAM;EACb,MAAM;CACR;CACA,OAAO;EACL,MAAM,MAAM,OAAoB;GAC9B,OAAO,QAAQ;GACf,MAAM,KAAK;EACb;EACA,MAAM,QAAQ;GACZ,IAAI;IACF,IAAI,OAAO,UAAU,2BAA2B;KAC9C,OAAO,QAAQ;KACf,MAAM,KAAK;IACb;GACF,UAAU;IACR,MAAM,IAAI,SAAe,SAAS,WAAW,OAAO,OAAO,UAAW,QAAQ,OAAO,KAAK,IAAI,QAAQ,CAAE,CAAC;GAC3G;EACF;CACF;AACF;AAIA,eAAsB,YAAY,SAAuC;CACvE,MAAM,UAAU,MAAM,gBAAgB;CACtC,IAAI,QAAQ,SAAS;EACnB,QAAQ,IAAI,oBAAoB,QAAQ,MAAM,QAAQ,QAAQ,IAAI,GAAG;EACrE;CACF;CACA,MAAM,WAAW,MAAM,kBAAkB,OAAO;CAChD,MAAM,QAAQ,KAAK,KAAK,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,CAAC,QAAQ,wBAAwB,CAAC,GAAG,QAAQ,iBAAiB;CACjI,MAAM,QAAQ,EAAE,QAAQ,UAAU,CAAC,GAAG,QAAQ,UAAU,KAAK,GAAG,EAC9D,aAAa;EAAE,KAAK,QAAQ,IAAI;EAAG,UAAU;EAAM,OAAO;GAAC;GAAU;GAAU;GAAU;EAAK;CAAE,EAClG,CAAC,CAAC,CAAC;CACH,IAAI,CAAC,OACH,MAAM,IAAI,YAAY,MAAM;EAC1B,MAAM,YAAY,KAAK;EACvB,UAAU;EACV,SAAS;EACT,MAAM;CACR,CAAC;CACH,IAAI;EAyBF,MAAM,IAxBc,SAAe,SAAS,WAAW;GACrD,MAAM,QAAQ,iBAAiB,qBAAK,IAAI,MAAM,qCAAqC,CAAC,GAAG,GAAM;GAC7F,MAAM,QAAQ,UAAkB;IAC9B,aAAa,KAAK;IAClB,MAAM,eAAe,SAAS,MAAM;IACpC,MAAM,eAAe,QAAQ,MAAM;IACnC,MAAM,eAAe,WAAW,KAAK;IACrC,IAAI,OAAO,OAAO,KAAK;SAClB,QAAQ;GACf;GACA,MAAM,UAAU,UAAiB,KAAK,KAAK;GAC3C,MAAM,eAAe,qBAAK,IAAI,MAAM,kDAAkD,iBAAiB,GAAG,CAAC;GAC3G,MAAM,KAAK,SAAS,MAAM;GAC1B,MAAM,KAAK,QAAQ,MAAM;GACzB,MAAM,SAAS,YAAqB;IAClC,IAAI,CAAC,WAAW,OAAO,YAAY,YAAY,EAAE,aAAa,YAAY,QAAQ,YAAY,MAC5F,OAAO,qBAAK,IAAI,MAAM,iCAAiC,CAAC;IAC1D,KAAK;GACP;GACA,MAAM,KAAK,WAAW,KAAK;GAC3B,MAAM,KAAK;IAAE,SAAS;KAAE,GAAG;KAAS,UAAU;IAAM;IAAG;GAAS,IAA2B,UAAU;IACnG,IAAI,OAAO,KAAK,KAAK;GACvB,CAAC;EACH,CACY;EACZ,IAAI,MAAM,WAAW,MAAM,WAAW;EACtC,MAAM,MAAM;EACZ,MAAM,SAAS,MAAM,gBAAgB;EACrC,QAAQ,IAAI,qBAAqB,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,IAAI,WAAW,iBAAiB,GAAG;CAC/G,SAAS,OAAO;EAEd,MAAM,KAAK,SAAS;EACpB,MAAM;CACR;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
2
|
-
const require_credentials = require("./credentials-
|
|
3
|
-
const require_run = require("./run-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C3-Zg-KD.cjs");
|
|
2
|
+
const require_credentials = require("./credentials-DEsGwcGD.cjs");
|
|
3
|
+
const require_run = require("./run-CQORv7Gq.cjs");
|
|
4
4
|
require("node:crypto");
|
|
5
5
|
let tinyexec = require("tinyexec");
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -10,7 +10,7 @@ let node_timers_promises = require("node:timers/promises");
|
|
|
10
10
|
let _kubb_core = require("@kubb/core");
|
|
11
11
|
let node_module = require("node:module");
|
|
12
12
|
let node_net = require("node:net");
|
|
13
|
-
|
|
13
|
+
// src/runners/studio/background.ts
|
|
14
14
|
const states = [
|
|
15
15
|
"starting",
|
|
16
16
|
"connected",
|
|
@@ -184,10 +184,9 @@ async function startWorker(options) {
|
|
|
184
184
|
throw error;
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
-
//#endregion
|
|
188
187
|
exports.getWorkerLogPath = getWorkerLogPath;
|
|
189
188
|
exports.getWorkerStatus = getWorkerStatus;
|
|
190
189
|
exports.startWorker = startWorker;
|
|
191
190
|
exports.stopWorker = stopWorker;
|
|
192
191
|
|
|
193
|
-
//# sourceMappingURL=background-
|
|
192
|
+
//# sourceMappingURL=background-CK2gQtTU.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-CdPT1eaq.cjs","names":["path","getProjectKubbHome","readFile","connectSocket","Diagnostics","delay","prepareConnection","createRequire","x"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"background-CK2gQtTU.cjs","names":["path","getProjectKubbHome","readFile","connectSocket","Diagnostics","delay","prepareConnection","createRequire","x"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as getWorkerLogPath, n as getWorkerStatus, i as startWorker, a as stopWorker } from "./background-
|
|
1
|
+
export { t as getWorkerLogPath, n as getWorkerStatus, i as startWorker, a as stopWorker } from "./background-BJCuXYuB.js";
|