@kubb/cli 5.11.3 → 5.11.5
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/{Telemetry-DHf64JZ0.js → Telemetry-BlAR8vxu.js} +3 -31
- package/dist/Telemetry-BlAR8vxu.js.map +1 -0
- package/dist/{Telemetry-DyPhPm0f.cjs → Telemetry-CF1YNdR-.cjs} +2 -42
- package/dist/Telemetry-CF1YNdR-.cjs.map +1 -0
- package/dist/{background-qThnqeWn.cjs → background-CaZZul1n.cjs} +5 -4
- package/dist/background-CaZZul1n.cjs.map +1 -0
- package/dist/{background-Doe7_Wwo.js → background-DKyMPeHm.js} +7 -5
- package/dist/background-DKyMPeHm.js.map +1 -0
- package/dist/{background-5cHVRJLj.js → background-DUHqYiuQ.js} +1 -1
- package/dist/config-Bh00Hwpw.js +132 -0
- package/dist/config-Bh00Hwpw.js.map +1 -0
- package/dist/config-D63Gum4F.cjs +169 -0
- package/dist/config-D63Gum4F.cjs.map +1 -0
- package/dist/config-iVdOcpro.js +1 -0
- package/dist/credentials-CZK6zPLv.js.map +1 -1
- package/dist/credentials-DDaKqtJn.cjs.map +1 -1
- package/dist/errors-Cl69p_vI.cjs +43 -0
- package/dist/errors-Cl69p_vI.cjs.map +1 -0
- package/dist/errors-DmC83ALX.js +32 -0
- package/dist/errors-DmC83ALX.js.map +1 -0
- package/dist/index.cjs +874 -309
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +864 -299
- package/dist/index.js.map +1 -1
- package/dist/registry-CfYNeyOc.cjs +272 -0
- package/dist/registry-CfYNeyOc.cjs.map +1 -0
- package/dist/registry-D688-8EH.js +200 -0
- package/dist/registry-D688-8EH.js.map +1 -0
- package/dist/reporters-BHyW4d5Q.js +817 -0
- package/dist/reporters-BHyW4d5Q.js.map +1 -0
- package/dist/reporters-DrQJfnU2.cjs +829 -0
- package/dist/reporters-DrQJfnU2.cjs.map +1 -0
- package/dist/run-B4SKmzAG.js +1 -0
- package/dist/{run-_I3J6qxb.cjs → run-BldagXO-.cjs} +4 -3
- package/dist/{run-_I3J6qxb.cjs.map → run-BldagXO-.cjs.map} +1 -1
- package/dist/{run-CPDiH3Hm.js → run-C1FHgzNR.js} +3 -2
- package/dist/{run-CPDiH3Hm.js.map → run-C1FHgzNR.js.map} +1 -1
- package/dist/run-CYHpe0V_.cjs +227 -0
- package/dist/run-CYHpe0V_.cjs.map +1 -0
- package/dist/run-CaJbZO-f.cjs +94 -0
- package/dist/run-CaJbZO-f.cjs.map +1 -0
- package/dist/run-Cj-HJxYW.js +93 -0
- package/dist/run-Cj-HJxYW.js.map +1 -0
- package/dist/{run-D3JYO5Ha.cjs → run-DkBJkKor.cjs} +172 -76
- package/dist/run-DkBJkKor.cjs.map +1 -0
- package/dist/run-S3xKq4bj.js +224 -0
- package/dist/run-S3xKq4bj.js.map +1 -0
- package/dist/{run-DfCOkPNm.js → run-vBNko5TH.js} +166 -70
- package/dist/run-vBNko5TH.js.map +1 -0
- package/dist/{snapshot-DpaFwPrR.cjs → snapshot-CsIJRKEL.cjs} +51 -64
- package/dist/snapshot-CsIJRKEL.cjs.map +1 -0
- package/dist/{snapshot-C2znYElk.js → snapshot-Cvo94y5d.js} +49 -62
- package/dist/snapshot-Cvo94y5d.js.map +1 -0
- package/dist/status-C-NPN63r.cjs +78 -0
- package/dist/status-C-NPN63r.cjs.map +1 -0
- package/dist/status-DTsZxa5Y.js +77 -0
- package/dist/status-DTsZxa5Y.js.map +1 -0
- package/dist/studioWorker.js +2 -2
- package/dist/summary-C22oNZu_.cjs +46 -0
- package/dist/summary-C22oNZu_.cjs.map +1 -0
- package/dist/summary-F11HQput.js +41 -0
- package/dist/summary-F11HQput.js.map +1 -0
- package/dist/text-28DwO_5A.cjs +341 -0
- package/dist/text-28DwO_5A.cjs.map +1 -0
- package/dist/text-BThr2r0T.js +239 -0
- package/dist/text-BThr2r0T.js.map +1 -0
- package/dist/utils-BLXM2UkN.cjs +727 -0
- package/dist/utils-BLXM2UkN.cjs.map +1 -0
- package/dist/utils-Cv3YUnSi.js +612 -0
- package/dist/utils-Cv3YUnSi.js.map +1 -0
- package/package.json +7 -7
- package/dist/Telemetry-DHf64JZ0.js.map +0 -1
- package/dist/Telemetry-DyPhPm0f.cjs.map +0 -1
- package/dist/background-Doe7_Wwo.js.map +0 -1
- package/dist/background-qThnqeWn.cjs.map +0 -1
- package/dist/config-DJgo6i9_.cjs +0 -113
- package/dist/config-DJgo6i9_.cjs.map +0 -1
- package/dist/config-PG9NSJHL.js +0 -95
- package/dist/config-PG9NSJHL.js.map +0 -1
- package/dist/output-B4fjaKge.cjs +0 -1076
- package/dist/output-B4fjaKge.cjs.map +0 -1
- package/dist/output-B9XbFYXT.js +0 -937
- package/dist/output-B9XbFYXT.js.map +0 -1
- package/dist/registry-CUs-pcSk.js +0 -115
- package/dist/registry-CUs-pcSk.js.map +0 -1
- package/dist/registry-t78Mn8DF.cjs +0 -157
- package/dist/registry-t78Mn8DF.cjs.map +0 -1
- package/dist/run-BtNq-0P2.js +0 -39
- package/dist/run-BtNq-0P2.js.map +0 -1
- package/dist/run-CPhZVSrZ.js +0 -343
- package/dist/run-CPhZVSrZ.js.map +0 -1
- package/dist/run-D3JYO5Ha.cjs.map +0 -1
- package/dist/run-DfCOkPNm.js.map +0 -1
- package/dist/run-DvdBqT9o.cjs +0 -345
- package/dist/run-DvdBqT9o.cjs.map +0 -1
- package/dist/run-EyyX3vvP.js +0 -1
- package/dist/run-pM4T492F.cjs +0 -40
- package/dist/run-pM4T492F.cjs.map +0 -1
- package/dist/snapshot-C2znYElk.js.map +0 -1
- package/dist/snapshot-DpaFwPrR.cjs.map +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-CPE0t6oh.cjs");
|
|
2
|
+
const require_errors = require("./errors-Cl69p_vI.cjs");
|
|
3
|
+
const require_utils = require("./utils-BLXM2UkN.cjs");
|
|
4
|
+
const require_text = require("./text-28DwO_5A.cjs");
|
|
5
|
+
let node_util = require("node:util");
|
|
6
|
+
let node_process = require("node:process");
|
|
7
|
+
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
8
|
+
let node_timers_promises = require("node:timers/promises");
|
|
9
|
+
let _kubb_core = require("@kubb/core");
|
|
10
|
+
let verkit = require("verkit");
|
|
11
|
+
//#region src/ui/mascot.ts
|
|
12
|
+
function bannerIntro(version, expression, command) {
|
|
13
|
+
return require_utils.getIntro({
|
|
14
|
+
title: "Turn your API spec into type-safe code",
|
|
15
|
+
description: `kubb ${command}`,
|
|
16
|
+
version,
|
|
17
|
+
expression
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/** Whether the mascot can be drawn: a rich, 256-color terminal wide enough that no line wraps and throws off a redraw. */
|
|
21
|
+
function canDrawMascot(version, command) {
|
|
22
|
+
if (!require_text.isRichOutput() || !process.stdout.hasColors(256)) return false;
|
|
23
|
+
return (0, node_util.stripVTControlCharacters)(bannerIntro(version, "open", command)).split("\n").every((line) => line.length <= process.stdout.columns);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Whether a command opens with the mascot. Only the long-running ones do: `generate`, a Studio
|
|
27
|
+
* session or pairing, and `add` or `update` while they ask questions. Quick commands print only
|
|
28
|
+
* their result.
|
|
29
|
+
*/
|
|
30
|
+
function wantsMascot({ commandPath, values }) {
|
|
31
|
+
const [command, subCommand] = commandPath;
|
|
32
|
+
if (values.help || values.version) return false;
|
|
33
|
+
if (command === "studio") return subCommand === void 0 || subCommand === "connect" || subCommand === "login";
|
|
34
|
+
if (command === "add" || command === "update" || command === "init") return require_text.isInteractiveRun({
|
|
35
|
+
yes: values.yes,
|
|
36
|
+
dryRun: values.dryRun ?? values["dry-run"]
|
|
37
|
+
});
|
|
38
|
+
return command === void 0 || command === "generate";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The still Kubb mascot with the version and the running command, such as `studio connect`, or a
|
|
42
|
+
* single version line where the terminal cannot draw it. Commands print it as their header; it
|
|
43
|
+
* draws at once, with no wake-up frames.
|
|
44
|
+
*/
|
|
45
|
+
function getBanner(version, command) {
|
|
46
|
+
return canDrawMascot(version, command) ? `\n${bannerIntro(version, "open", command)}` : `Kubb CLI v${version}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Opens `kubb generate` with the mascot playing its wake-up frames in place, which takes under a
|
|
50
|
+
* second and overlaps the config load. Elsewhere the still {@link getBanner} is the header.
|
|
51
|
+
*/
|
|
52
|
+
async function logBanner(version) {
|
|
53
|
+
if (!canDrawMascot(version, "generate")) {
|
|
54
|
+
console.log(getBanner(version, "generate"));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.log("");
|
|
58
|
+
for (const [index, [expression, ms]] of require_utils.wakeUp.entries()) {
|
|
59
|
+
const frame = bannerIntro(version, expression, "generate");
|
|
60
|
+
if (index > 0) process.stdout.moveCursor(0, -frame.split("\n").length);
|
|
61
|
+
console.log(frame);
|
|
62
|
+
await (0, node_timers_promises.setTimeout)(ms);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/utils/registry.ts
|
|
67
|
+
const DEFAULT_REGISTRY = "https://kubb.dev/registry";
|
|
68
|
+
const SLUG = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
|
69
|
+
const ID = new RegExp(`^${SLUG}(\\/${SLUG})?$`);
|
|
70
|
+
/** The registry base URL: `KUBB_REGISTRY`, or kubb.dev. */
|
|
71
|
+
function registryUrl() {
|
|
72
|
+
const url = node_process.default.env.KUBB_REGISTRY || DEFAULT_REGISTRY;
|
|
73
|
+
if (!/^https?:\/\//.test(url)) throw new _kubb_core.Diagnostics.Error({
|
|
74
|
+
code: _kubb_core.Diagnostics.code.unknown,
|
|
75
|
+
severity: "error",
|
|
76
|
+
message: `KUBB_REGISTRY must be an http(s) URL, got "${url}".`,
|
|
77
|
+
help: "Set `KUBB_REGISTRY` to an http(s) URL, or unset it to use https://kubb.dev/registry."
|
|
78
|
+
});
|
|
79
|
+
return url.replace(/\/+$/, "");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Turns `owner/slug`, or a bare `slug` meaning `kubb-labs/<slug>`, into a registry id.
|
|
83
|
+
*
|
|
84
|
+
* @throws when the id has another shape, so it never reaches a URL unchecked.
|
|
85
|
+
*/
|
|
86
|
+
function toRegistryId(input) {
|
|
87
|
+
if (!ID.test(input)) throw new _kubb_core.Diagnostics.Error({
|
|
88
|
+
code: _kubb_core.Diagnostics.code.unknown,
|
|
89
|
+
severity: "error",
|
|
90
|
+
message: `"${input}" is not a registry id. Use owner/slug, such as kubb-labs/plugin-zod, or a slug, such as plugin-zod.`
|
|
91
|
+
});
|
|
92
|
+
return input.includes("/") ? input : `kubb-labs/${input}`;
|
|
93
|
+
}
|
|
94
|
+
/** The shortest id `kubb add` and `kubb update` accept: the slug for an official extension, else `owner/slug`. */
|
|
95
|
+
function toShortId(id) {
|
|
96
|
+
return id.startsWith("kubb-labs/") ? id.slice(10) : id;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* GETs `url` as JSON. A non-2xx answer or a body that is not JSON returns no `body`.
|
|
100
|
+
* A network error throws with the URL.
|
|
101
|
+
*/
|
|
102
|
+
async function fetchJSON(url) {
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await globalThis.fetch(url, { headers: { accept: "application/json" } });
|
|
106
|
+
} catch (cause) {
|
|
107
|
+
throw new _kubb_core.Diagnostics.Error({
|
|
108
|
+
code: _kubb_core.Diagnostics.code.inputUnreachable,
|
|
109
|
+
severity: "error",
|
|
110
|
+
message: `Could not read ${url}: ${require_errors.getErrorMessage(cause)}`,
|
|
111
|
+
help: "Check your network connection and that the registry is reachable.",
|
|
112
|
+
cause: require_errors.toError(cause)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!response.ok) return { status: response.status };
|
|
116
|
+
return {
|
|
117
|
+
status: response.status,
|
|
118
|
+
body: await response.json().catch(() => void 0)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Fetches the registry index, leaving out entries that fail validation. */
|
|
122
|
+
async function fetchRegistryIndex({ registry }) {
|
|
123
|
+
const { status, body } = await fetchJSON(`${registry}/index.json`);
|
|
124
|
+
const index = body;
|
|
125
|
+
if (status !== 200 || index?.version !== 1 || !Array.isArray(index.extensions)) throw new _kubb_core.Diagnostics.Error({
|
|
126
|
+
code: _kubb_core.Diagnostics.code.inputRequestFailed,
|
|
127
|
+
severity: "error",
|
|
128
|
+
message: `The registry at ${registry} sent an invalid index (HTTP ${status}).`,
|
|
129
|
+
help: "Try again later, or check that `KUBB_REGISTRY` points at a Kubb registry."
|
|
130
|
+
});
|
|
131
|
+
return index.extensions.filter(_kubb_core.isRegistryEntry);
|
|
132
|
+
}
|
|
133
|
+
/** What the package manager installs: `<package>@<version>`, or a GitHub release's commit, so the install cannot move with the tag. */
|
|
134
|
+
function installSpec(artifact) {
|
|
135
|
+
return artifact.kind === "github" ? `github:${artifact.owner}/${artifact.repo}#${artifact.commit}` : `${artifact.package}@${artifact.version}`;
|
|
136
|
+
}
|
|
137
|
+
/** Why the running Kubb or Node.js version cannot use this extension, or `undefined` when it can. */
|
|
138
|
+
function incompatibility(manifest) {
|
|
139
|
+
try {
|
|
140
|
+
(0, _kubb_core.checkCompatibility)({
|
|
141
|
+
name: manifest.name,
|
|
142
|
+
compatibility: manifest.compatibility
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return require_errors.getErrorMessage(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Whether `latest` is newer than `installed`. An unknown installed version counts as outdated, and an
|
|
151
|
+
* invalid `latest` never does.
|
|
152
|
+
*/
|
|
153
|
+
function isOutdated({ installed, latest }) {
|
|
154
|
+
if (!(0, verkit.isValid)(latest)) return false;
|
|
155
|
+
return !installed || !(0, verkit.isValid)(installed) || (0, verkit.isLessThan)(installed, latest);
|
|
156
|
+
}
|
|
157
|
+
/** `major`, `minor`, or `patch` from `installed` to `latest`, or `undefined` when either is unknown or invalid. */
|
|
158
|
+
function bumpOf({ installed, latest }) {
|
|
159
|
+
if (!installed || !(0, verkit.isValid)(installed) || !(0, verkit.isValid)(latest)) return void 0;
|
|
160
|
+
const kind = (0, verkit.difference)(installed, latest);
|
|
161
|
+
if (!kind) return void 0;
|
|
162
|
+
if (kind.endsWith("major")) return "major";
|
|
163
|
+
return kind.endsWith("minor") ? "minor" : "patch";
|
|
164
|
+
}
|
|
165
|
+
function editDistance(a, b) {
|
|
166
|
+
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
167
|
+
for (let i = 1; i <= a.length; i++) {
|
|
168
|
+
let previous = row[0];
|
|
169
|
+
row[0] = i;
|
|
170
|
+
for (let j = 1; j <= b.length; j++) {
|
|
171
|
+
const current = row[j];
|
|
172
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
173
|
+
previous = current;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return row[b.length];
|
|
177
|
+
}
|
|
178
|
+
/** Words every extension shares, so they never make two ids look alike. */
|
|
179
|
+
const COMMON_TOKENS = /* @__PURE__ */ new Set([
|
|
180
|
+
"kubb",
|
|
181
|
+
"plugin",
|
|
182
|
+
"adapter",
|
|
183
|
+
"parser"
|
|
184
|
+
]);
|
|
185
|
+
/**
|
|
186
|
+
* The ids closest to a mistyped `input`, best first, at most three: those within a few edits of it,
|
|
187
|
+
* or sharing a word such as `zod` in any order. Compares slugs, so the owner may be left out.
|
|
188
|
+
*/
|
|
189
|
+
function closestIds({ input, ids }) {
|
|
190
|
+
const slug = (id) => id.slice(id.indexOf("/") + 1);
|
|
191
|
+
const tokens = new Set(slug(input).split("-").filter((token) => !COMMON_TOKENS.has(token)));
|
|
192
|
+
return ids.map((id) => ({
|
|
193
|
+
id,
|
|
194
|
+
distance: editDistance(slug(input), slug(id)),
|
|
195
|
+
shared: slug(id).split("-").some((token) => tokens.has(token))
|
|
196
|
+
})).filter(({ distance, shared }) => shared || distance <= Math.max(2, Math.floor(slug(input).length / 4))).sort((a, b) => Number(b.shared) - Number(a.shared) || a.distance - b.distance).slice(0, 3).map(({ id }) => id);
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
Object.defineProperty(exports, "bumpOf", {
|
|
200
|
+
enumerable: true,
|
|
201
|
+
get: function() {
|
|
202
|
+
return bumpOf;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
Object.defineProperty(exports, "closestIds", {
|
|
206
|
+
enumerable: true,
|
|
207
|
+
get: function() {
|
|
208
|
+
return closestIds;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
Object.defineProperty(exports, "fetchRegistryIndex", {
|
|
212
|
+
enumerable: true,
|
|
213
|
+
get: function() {
|
|
214
|
+
return fetchRegistryIndex;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
Object.defineProperty(exports, "getBanner", {
|
|
218
|
+
enumerable: true,
|
|
219
|
+
get: function() {
|
|
220
|
+
return getBanner;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
Object.defineProperty(exports, "incompatibility", {
|
|
224
|
+
enumerable: true,
|
|
225
|
+
get: function() {
|
|
226
|
+
return incompatibility;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
Object.defineProperty(exports, "installSpec", {
|
|
230
|
+
enumerable: true,
|
|
231
|
+
get: function() {
|
|
232
|
+
return installSpec;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
Object.defineProperty(exports, "isOutdated", {
|
|
236
|
+
enumerable: true,
|
|
237
|
+
get: function() {
|
|
238
|
+
return isOutdated;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
Object.defineProperty(exports, "logBanner", {
|
|
242
|
+
enumerable: true,
|
|
243
|
+
get: function() {
|
|
244
|
+
return logBanner;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
Object.defineProperty(exports, "registryUrl", {
|
|
248
|
+
enumerable: true,
|
|
249
|
+
get: function() {
|
|
250
|
+
return registryUrl;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
Object.defineProperty(exports, "toRegistryId", {
|
|
254
|
+
enumerable: true,
|
|
255
|
+
get: function() {
|
|
256
|
+
return toRegistryId;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
Object.defineProperty(exports, "toShortId", {
|
|
260
|
+
enumerable: true,
|
|
261
|
+
get: function() {
|
|
262
|
+
return toShortId;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
Object.defineProperty(exports, "wantsMascot", {
|
|
266
|
+
enumerable: true,
|
|
267
|
+
get: function() {
|
|
268
|
+
return wantsMascot;
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
//# sourceMappingURL=registry-CfYNeyOc.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-CfYNeyOc.cjs","names":["getIntro","isRichOutput","stripVTControlCharacters","isInteractiveRun","wakeUp","sleep","process","Diagnostics","getErrorMessage","toError","isRegistryEntry","isValid","isLessThan","difference"],"sources":["../src/ui/mascot.ts","../src/utils/registry.ts"],"sourcesContent":["import { setTimeout as sleep } from 'node:timers/promises'\nimport { stripVTControlCharacters } from 'node:util'\nimport { isRichOutput } from '../utils/env.ts'\nimport { type Expression, getIntro, wakeUp } from '../loggers/banner.ts'\nimport { isInteractiveRun } from './intro.ts'\n\nfunction bannerIntro(version: string, expression: Expression, command: string): string {\n return getIntro({ title: 'Turn your API spec into type-safe code', description: `kubb ${command}`, version, expression })\n}\n\n/** Whether the mascot can be drawn: a rich, 256-color terminal wide enough that no line wraps and throws off a redraw. */\nfunction canDrawMascot(version: string, command: string): boolean {\n if (!isRichOutput() || !process.stdout.hasColors(256)) return false\n\n return stripVTControlCharacters(bannerIntro(version, 'open', command))\n .split('\\n')\n .every((line) => line.length <= process.stdout.columns)\n}\n\n/**\n * Whether a command opens with the mascot. Only the long-running ones do: `generate`, a Studio\n * session or pairing, and `add` or `update` while they ask questions. Quick commands print only\n * their result.\n */\nexport function wantsMascot({ commandPath, values }: { commandPath: ReadonlyArray<string>; values: Record<string, unknown> }): boolean {\n const [command, subCommand] = commandPath\n if (values.help || values.version) return false\n if (command === 'studio') return subCommand === undefined || subCommand === 'connect' || subCommand === 'login'\n if (command === 'add' || command === 'update' || command === 'init') return isInteractiveRun({ yes: values.yes, dryRun: values.dryRun ?? values['dry-run'] })\n\n return command === undefined || command === 'generate'\n}\n\n/**\n * The still Kubb mascot with the version and the running command, such as `studio connect`, or a\n * single version line where the terminal cannot draw it. Commands print it as their header; it\n * draws at once, with no wake-up frames.\n */\nexport function getBanner(version: string, command: string): string {\n return canDrawMascot(version, command) ? `\\n${bannerIntro(version, 'open', command)}` : `Kubb CLI v${version}`\n}\n\n/**\n * Opens `kubb generate` with the mascot playing its wake-up frames in place, which takes under a\n * second and overlaps the config load. Elsewhere the still {@link getBanner} is the header.\n */\nexport async function logBanner(version: string): Promise<void> {\n if (!canDrawMascot(version, 'generate')) {\n console.log(getBanner(version, 'generate'))\n\n return\n }\n\n console.log('')\n for (const [index, [expression, ms]] of wakeUp.entries()) {\n const frame = bannerIntro(version, expression, 'generate')\n if (index > 0) process.stdout.moveCursor(0, -frame.split('\\n').length)\n console.log(frame)\n await sleep(ms)\n }\n}\n","import process from 'node:process'\nimport { getErrorMessage, toError } from '@internals/utils'\nimport { checkCompatibility, Diagnostics, type ExtensionManifest, isRegistryEntry, type RegistryArtifact, type RegistryEntry } from '@kubb/core'\nimport { difference, isLessThan, isValid } from 'verkit'\n\nconst DEFAULT_REGISTRY = 'https://kubb.dev/registry'\n\nconst SLUG = '[a-z0-9]+(?:-[a-z0-9]+)*'\nconst ID = new RegExp(`^${SLUG}(\\\\/${SLUG})?$`)\n\n/** The registry base URL: `KUBB_REGISTRY`, or kubb.dev. */\nexport function registryUrl(): string {\n const url = process.env.KUBB_REGISTRY || DEFAULT_REGISTRY\n if (!/^https?:\\/\\//.test(url))\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: `KUBB_REGISTRY must be an http(s) URL, got \"${url}\".`,\n help: 'Set `KUBB_REGISTRY` to an http(s) URL, or unset it to use https://kubb.dev/registry.',\n })\n return url.replace(/\\/+$/, '')\n}\n\n/**\n * Turns `owner/slug`, or a bare `slug` meaning `kubb-labs/<slug>`, into a registry id.\n *\n * @throws when the id has another shape, so it never reaches a URL unchecked.\n */\nexport function toRegistryId(input: string): string {\n if (!ID.test(input))\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: `\"${input}\" is not a registry id. Use owner/slug, such as kubb-labs/plugin-zod, or a slug, such as plugin-zod.`,\n })\n return input.includes('/') ? input : `kubb-labs/${input}`\n}\n\n/** The shortest id `kubb add` and `kubb update` accept: the slug for an official extension, else `owner/slug`. */\nexport function toShortId(id: string): string {\n return id.startsWith('kubb-labs/') ? id.slice('kubb-labs/'.length) : id\n}\n\n/**\n * GETs `url` as JSON. A non-2xx answer or a body that is not JSON returns no `body`.\n * A network error throws with the URL.\n */\nasync function fetchJSON(url: string): Promise<{ status: number; body?: unknown }> {\n let response: Response\n try {\n response = await globalThis.fetch(url, { headers: { accept: 'application/json' } })\n } catch (cause) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.inputUnreachable,\n severity: 'error',\n message: `Could not read ${url}: ${getErrorMessage(cause)}`,\n help: 'Check your network connection and that the registry is reachable.',\n cause: toError(cause),\n })\n }\n if (!response.ok) return { status: response.status }\n return { status: response.status, body: await response.json().catch(() => undefined) }\n}\n\n/** Fetches the registry index, leaving out entries that fail validation. */\nexport async function fetchRegistryIndex({ registry }: { registry: string }): Promise<Array<RegistryEntry>> {\n const { status, body } = await fetchJSON(`${registry}/index.json`)\n const index = body as { version?: unknown; extensions?: unknown } | null | undefined\n if (status !== 200 || index?.version !== 1 || !Array.isArray(index.extensions)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.inputRequestFailed,\n severity: 'error',\n message: `The registry at ${registry} sent an invalid index (HTTP ${status}).`,\n help: 'Try again later, or check that `KUBB_REGISTRY` points at a Kubb registry.',\n })\n }\n return index.extensions.filter(isRegistryEntry)\n}\n\n/** What the package manager installs: `<package>@<version>`, or a GitHub release's commit, so the install cannot move with the tag. */\nexport function installSpec(artifact: RegistryArtifact): string {\n return artifact.kind === 'github' ? `github:${artifact.owner}/${artifact.repo}#${artifact.commit}` : `${artifact.package}@${artifact.version}`\n}\n\n/** Why the running Kubb or Node.js version cannot use this extension, or `undefined` when it can. */\nexport function incompatibility(manifest: ExtensionManifest): string | undefined {\n try {\n checkCompatibility({ name: manifest.name, compatibility: manifest.compatibility })\n return undefined\n } catch (error) {\n return getErrorMessage(error)\n }\n}\n\n/**\n * Whether `latest` is newer than `installed`. An unknown installed version counts as outdated, and an\n * invalid `latest` never does.\n */\nexport function isOutdated({ installed, latest }: { installed: string | undefined; latest: string }): boolean {\n if (!isValid(latest)) return false\n return !installed || !isValid(installed) || isLessThan(installed, latest)\n}\n\n/** `major`, `minor`, or `patch` from `installed` to `latest`, or `undefined` when either is unknown or invalid. */\nexport function bumpOf({ installed, latest }: { installed: string | undefined; latest: string }): 'major' | 'minor' | 'patch' | undefined {\n if (!installed || !isValid(installed) || !isValid(latest)) return undefined\n const kind = difference(installed, latest)\n if (!kind) return undefined\n if (kind.endsWith('major')) return 'major'\n return kind.endsWith('minor') ? 'minor' : 'patch'\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, index) => index)\n for (let i = 1; i <= a.length; i++) {\n let previous = row[0]!\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const current = row[j]!\n row[j] = Math.min(row[j]! + 1, row[j - 1]! + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1))\n previous = current\n }\n }\n return row[b.length]!\n}\n\n/** Words every extension shares, so they never make two ids look alike. */\nconst COMMON_TOKENS = new Set(['kubb', 'plugin', 'adapter', 'parser'])\n\n/**\n * The ids closest to a mistyped `input`, best first, at most three: those within a few edits of it,\n * or sharing a word such as `zod` in any order. Compares slugs, so the owner may be left out.\n */\nexport function closestIds({ input, ids }: { input: string; ids: Array<string> }): Array<string> {\n const slug = (id: string) => id.slice(id.indexOf('/') + 1)\n const tokens = new Set(\n slug(input)\n .split('-')\n .filter((token) => !COMMON_TOKENS.has(token)),\n )\n return ids\n .map((id) => ({\n id,\n distance: editDistance(slug(input), slug(id)),\n shared: slug(id)\n .split('-')\n .some((token) => tokens.has(token)),\n }))\n .filter(({ distance, shared }) => shared || distance <= Math.max(2, Math.floor(slug(input).length / 4)))\n .sort((a, b) => Number(b.shared) - Number(a.shared) || a.distance - b.distance)\n .slice(0, 3)\n .map(({ id }) => id)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
+
import { n as toError, t as getErrorMessage } from "./errors-DmC83ALX.js";
|
|
3
|
+
import { h as wakeUp, m as getIntro } from "./utils-Cv3YUnSi.js";
|
|
4
|
+
import { _ as isRichOutput, r as isInteractiveRun } from "./text-BThr2r0T.js";
|
|
5
|
+
import { stripVTControlCharacters } from "node:util";
|
|
6
|
+
import process$1 from "node:process";
|
|
7
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
|
+
import { Diagnostics, checkCompatibility, isRegistryEntry } from "@kubb/core";
|
|
9
|
+
import { difference, isLessThan, isValid } from "verkit";
|
|
10
|
+
//#region src/ui/mascot.ts
|
|
11
|
+
function bannerIntro(version, expression, command) {
|
|
12
|
+
return getIntro({
|
|
13
|
+
title: "Turn your API spec into type-safe code",
|
|
14
|
+
description: `kubb ${command}`,
|
|
15
|
+
version,
|
|
16
|
+
expression
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** Whether the mascot can be drawn: a rich, 256-color terminal wide enough that no line wraps and throws off a redraw. */
|
|
20
|
+
function canDrawMascot(version, command) {
|
|
21
|
+
if (!isRichOutput() || !process.stdout.hasColors(256)) return false;
|
|
22
|
+
return stripVTControlCharacters(bannerIntro(version, "open", command)).split("\n").every((line) => line.length <= process.stdout.columns);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether a command opens with the mascot. Only the long-running ones do: `generate`, a Studio
|
|
26
|
+
* session or pairing, and `add` or `update` while they ask questions. Quick commands print only
|
|
27
|
+
* their result.
|
|
28
|
+
*/
|
|
29
|
+
function wantsMascot({ commandPath, values }) {
|
|
30
|
+
const [command, subCommand] = commandPath;
|
|
31
|
+
if (values.help || values.version) return false;
|
|
32
|
+
if (command === "studio") return subCommand === void 0 || subCommand === "connect" || subCommand === "login";
|
|
33
|
+
if (command === "add" || command === "update" || command === "init") return isInteractiveRun({
|
|
34
|
+
yes: values.yes,
|
|
35
|
+
dryRun: values.dryRun ?? values["dry-run"]
|
|
36
|
+
});
|
|
37
|
+
return command === void 0 || command === "generate";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The still Kubb mascot with the version and the running command, such as `studio connect`, or a
|
|
41
|
+
* single version line where the terminal cannot draw it. Commands print it as their header; it
|
|
42
|
+
* draws at once, with no wake-up frames.
|
|
43
|
+
*/
|
|
44
|
+
function getBanner(version, command) {
|
|
45
|
+
return canDrawMascot(version, command) ? `\n${bannerIntro(version, "open", command)}` : `Kubb CLI v${version}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Opens `kubb generate` with the mascot playing its wake-up frames in place, which takes under a
|
|
49
|
+
* second and overlaps the config load. Elsewhere the still {@link getBanner} is the header.
|
|
50
|
+
*/
|
|
51
|
+
async function logBanner(version) {
|
|
52
|
+
if (!canDrawMascot(version, "generate")) {
|
|
53
|
+
console.log(getBanner(version, "generate"));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log("");
|
|
57
|
+
for (const [index, [expression, ms]] of wakeUp.entries()) {
|
|
58
|
+
const frame = bannerIntro(version, expression, "generate");
|
|
59
|
+
if (index > 0) process.stdout.moveCursor(0, -frame.split("\n").length);
|
|
60
|
+
console.log(frame);
|
|
61
|
+
await setTimeout(ms);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/utils/registry.ts
|
|
66
|
+
const DEFAULT_REGISTRY = "https://kubb.dev/registry";
|
|
67
|
+
const SLUG = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
|
68
|
+
const ID = new RegExp(`^${SLUG}(\\/${SLUG})?$`);
|
|
69
|
+
/** The registry base URL: `KUBB_REGISTRY`, or kubb.dev. */
|
|
70
|
+
function registryUrl() {
|
|
71
|
+
const url = process$1.env.KUBB_REGISTRY || DEFAULT_REGISTRY;
|
|
72
|
+
if (!/^https?:\/\//.test(url)) throw new Diagnostics.Error({
|
|
73
|
+
code: Diagnostics.code.unknown,
|
|
74
|
+
severity: "error",
|
|
75
|
+
message: `KUBB_REGISTRY must be an http(s) URL, got "${url}".`,
|
|
76
|
+
help: "Set `KUBB_REGISTRY` to an http(s) URL, or unset it to use https://kubb.dev/registry."
|
|
77
|
+
});
|
|
78
|
+
return url.replace(/\/+$/, "");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Turns `owner/slug`, or a bare `slug` meaning `kubb-labs/<slug>`, into a registry id.
|
|
82
|
+
*
|
|
83
|
+
* @throws when the id has another shape, so it never reaches a URL unchecked.
|
|
84
|
+
*/
|
|
85
|
+
function toRegistryId(input) {
|
|
86
|
+
if (!ID.test(input)) throw new Diagnostics.Error({
|
|
87
|
+
code: Diagnostics.code.unknown,
|
|
88
|
+
severity: "error",
|
|
89
|
+
message: `"${input}" is not a registry id. Use owner/slug, such as kubb-labs/plugin-zod, or a slug, such as plugin-zod.`
|
|
90
|
+
});
|
|
91
|
+
return input.includes("/") ? input : `kubb-labs/${input}`;
|
|
92
|
+
}
|
|
93
|
+
/** The shortest id `kubb add` and `kubb update` accept: the slug for an official extension, else `owner/slug`. */
|
|
94
|
+
function toShortId(id) {
|
|
95
|
+
return id.startsWith("kubb-labs/") ? id.slice(10) : id;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* GETs `url` as JSON. A non-2xx answer or a body that is not JSON returns no `body`.
|
|
99
|
+
* A network error throws with the URL.
|
|
100
|
+
*/
|
|
101
|
+
async function fetchJSON(url) {
|
|
102
|
+
let response;
|
|
103
|
+
try {
|
|
104
|
+
response = await globalThis.fetch(url, { headers: { accept: "application/json" } });
|
|
105
|
+
} catch (cause) {
|
|
106
|
+
throw new Diagnostics.Error({
|
|
107
|
+
code: Diagnostics.code.inputUnreachable,
|
|
108
|
+
severity: "error",
|
|
109
|
+
message: `Could not read ${url}: ${getErrorMessage(cause)}`,
|
|
110
|
+
help: "Check your network connection and that the registry is reachable.",
|
|
111
|
+
cause: toError(cause)
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (!response.ok) return { status: response.status };
|
|
115
|
+
return {
|
|
116
|
+
status: response.status,
|
|
117
|
+
body: await response.json().catch(() => void 0)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Fetches the registry index, leaving out entries that fail validation. */
|
|
121
|
+
async function fetchRegistryIndex({ registry }) {
|
|
122
|
+
const { status, body } = await fetchJSON(`${registry}/index.json`);
|
|
123
|
+
const index = body;
|
|
124
|
+
if (status !== 200 || index?.version !== 1 || !Array.isArray(index.extensions)) throw new Diagnostics.Error({
|
|
125
|
+
code: Diagnostics.code.inputRequestFailed,
|
|
126
|
+
severity: "error",
|
|
127
|
+
message: `The registry at ${registry} sent an invalid index (HTTP ${status}).`,
|
|
128
|
+
help: "Try again later, or check that `KUBB_REGISTRY` points at a Kubb registry."
|
|
129
|
+
});
|
|
130
|
+
return index.extensions.filter(isRegistryEntry);
|
|
131
|
+
}
|
|
132
|
+
/** What the package manager installs: `<package>@<version>`, or a GitHub release's commit, so the install cannot move with the tag. */
|
|
133
|
+
function installSpec(artifact) {
|
|
134
|
+
return artifact.kind === "github" ? `github:${artifact.owner}/${artifact.repo}#${artifact.commit}` : `${artifact.package}@${artifact.version}`;
|
|
135
|
+
}
|
|
136
|
+
/** Why the running Kubb or Node.js version cannot use this extension, or `undefined` when it can. */
|
|
137
|
+
function incompatibility(manifest) {
|
|
138
|
+
try {
|
|
139
|
+
checkCompatibility({
|
|
140
|
+
name: manifest.name,
|
|
141
|
+
compatibility: manifest.compatibility
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return getErrorMessage(error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Whether `latest` is newer than `installed`. An unknown installed version counts as outdated, and an
|
|
150
|
+
* invalid `latest` never does.
|
|
151
|
+
*/
|
|
152
|
+
function isOutdated({ installed, latest }) {
|
|
153
|
+
if (!isValid(latest)) return false;
|
|
154
|
+
return !installed || !isValid(installed) || isLessThan(installed, latest);
|
|
155
|
+
}
|
|
156
|
+
/** `major`, `minor`, or `patch` from `installed` to `latest`, or `undefined` when either is unknown or invalid. */
|
|
157
|
+
function bumpOf({ installed, latest }) {
|
|
158
|
+
if (!installed || !isValid(installed) || !isValid(latest)) return void 0;
|
|
159
|
+
const kind = difference(installed, latest);
|
|
160
|
+
if (!kind) return void 0;
|
|
161
|
+
if (kind.endsWith("major")) return "major";
|
|
162
|
+
return kind.endsWith("minor") ? "minor" : "patch";
|
|
163
|
+
}
|
|
164
|
+
function editDistance(a, b) {
|
|
165
|
+
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
166
|
+
for (let i = 1; i <= a.length; i++) {
|
|
167
|
+
let previous = row[0];
|
|
168
|
+
row[0] = i;
|
|
169
|
+
for (let j = 1; j <= b.length; j++) {
|
|
170
|
+
const current = row[j];
|
|
171
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
172
|
+
previous = current;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return row[b.length];
|
|
176
|
+
}
|
|
177
|
+
/** Words every extension shares, so they never make two ids look alike. */
|
|
178
|
+
const COMMON_TOKENS = /* @__PURE__ */ new Set([
|
|
179
|
+
"kubb",
|
|
180
|
+
"plugin",
|
|
181
|
+
"adapter",
|
|
182
|
+
"parser"
|
|
183
|
+
]);
|
|
184
|
+
/**
|
|
185
|
+
* The ids closest to a mistyped `input`, best first, at most three: those within a few edits of it,
|
|
186
|
+
* or sharing a word such as `zod` in any order. Compares slugs, so the owner may be left out.
|
|
187
|
+
*/
|
|
188
|
+
function closestIds({ input, ids }) {
|
|
189
|
+
const slug = (id) => id.slice(id.indexOf("/") + 1);
|
|
190
|
+
const tokens = new Set(slug(input).split("-").filter((token) => !COMMON_TOKENS.has(token)));
|
|
191
|
+
return ids.map((id) => ({
|
|
192
|
+
id,
|
|
193
|
+
distance: editDistance(slug(input), slug(id)),
|
|
194
|
+
shared: slug(id).split("-").some((token) => tokens.has(token))
|
|
195
|
+
})).filter(({ distance, shared }) => shared || distance <= Math.max(2, Math.floor(slug(input).length / 4))).sort((a, b) => Number(b.shared) - Number(a.shared) || a.distance - b.distance).slice(0, 3).map(({ id }) => id);
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
export { installSpec as a, toRegistryId as c, logBanner as d, wantsMascot as f, incompatibility as i, toShortId as l, closestIds as n, isOutdated as o, fetchRegistryIndex as r, registryUrl as s, bumpOf as t, getBanner as u };
|
|
199
|
+
|
|
200
|
+
//# sourceMappingURL=registry-D688-8EH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-D688-8EH.js","names":["sleep","process"],"sources":["../src/ui/mascot.ts","../src/utils/registry.ts"],"sourcesContent":["import { setTimeout as sleep } from 'node:timers/promises'\nimport { stripVTControlCharacters } from 'node:util'\nimport { isRichOutput } from '../utils/env.ts'\nimport { type Expression, getIntro, wakeUp } from '../loggers/banner.ts'\nimport { isInteractiveRun } from './intro.ts'\n\nfunction bannerIntro(version: string, expression: Expression, command: string): string {\n return getIntro({ title: 'Turn your API spec into type-safe code', description: `kubb ${command}`, version, expression })\n}\n\n/** Whether the mascot can be drawn: a rich, 256-color terminal wide enough that no line wraps and throws off a redraw. */\nfunction canDrawMascot(version: string, command: string): boolean {\n if (!isRichOutput() || !process.stdout.hasColors(256)) return false\n\n return stripVTControlCharacters(bannerIntro(version, 'open', command))\n .split('\\n')\n .every((line) => line.length <= process.stdout.columns)\n}\n\n/**\n * Whether a command opens with the mascot. Only the long-running ones do: `generate`, a Studio\n * session or pairing, and `add` or `update` while they ask questions. Quick commands print only\n * their result.\n */\nexport function wantsMascot({ commandPath, values }: { commandPath: ReadonlyArray<string>; values: Record<string, unknown> }): boolean {\n const [command, subCommand] = commandPath\n if (values.help || values.version) return false\n if (command === 'studio') return subCommand === undefined || subCommand === 'connect' || subCommand === 'login'\n if (command === 'add' || command === 'update' || command === 'init') return isInteractiveRun({ yes: values.yes, dryRun: values.dryRun ?? values['dry-run'] })\n\n return command === undefined || command === 'generate'\n}\n\n/**\n * The still Kubb mascot with the version and the running command, such as `studio connect`, or a\n * single version line where the terminal cannot draw it. Commands print it as their header; it\n * draws at once, with no wake-up frames.\n */\nexport function getBanner(version: string, command: string): string {\n return canDrawMascot(version, command) ? `\\n${bannerIntro(version, 'open', command)}` : `Kubb CLI v${version}`\n}\n\n/**\n * Opens `kubb generate` with the mascot playing its wake-up frames in place, which takes under a\n * second and overlaps the config load. Elsewhere the still {@link getBanner} is the header.\n */\nexport async function logBanner(version: string): Promise<void> {\n if (!canDrawMascot(version, 'generate')) {\n console.log(getBanner(version, 'generate'))\n\n return\n }\n\n console.log('')\n for (const [index, [expression, ms]] of wakeUp.entries()) {\n const frame = bannerIntro(version, expression, 'generate')\n if (index > 0) process.stdout.moveCursor(0, -frame.split('\\n').length)\n console.log(frame)\n await sleep(ms)\n }\n}\n","import process from 'node:process'\nimport { getErrorMessage, toError } from '@internals/utils'\nimport { checkCompatibility, Diagnostics, type ExtensionManifest, isRegistryEntry, type RegistryArtifact, type RegistryEntry } from '@kubb/core'\nimport { difference, isLessThan, isValid } from 'verkit'\n\nconst DEFAULT_REGISTRY = 'https://kubb.dev/registry'\n\nconst SLUG = '[a-z0-9]+(?:-[a-z0-9]+)*'\nconst ID = new RegExp(`^${SLUG}(\\\\/${SLUG})?$`)\n\n/** The registry base URL: `KUBB_REGISTRY`, or kubb.dev. */\nexport function registryUrl(): string {\n const url = process.env.KUBB_REGISTRY || DEFAULT_REGISTRY\n if (!/^https?:\\/\\//.test(url))\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: `KUBB_REGISTRY must be an http(s) URL, got \"${url}\".`,\n help: 'Set `KUBB_REGISTRY` to an http(s) URL, or unset it to use https://kubb.dev/registry.',\n })\n return url.replace(/\\/+$/, '')\n}\n\n/**\n * Turns `owner/slug`, or a bare `slug` meaning `kubb-labs/<slug>`, into a registry id.\n *\n * @throws when the id has another shape, so it never reaches a URL unchecked.\n */\nexport function toRegistryId(input: string): string {\n if (!ID.test(input))\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: `\"${input}\" is not a registry id. Use owner/slug, such as kubb-labs/plugin-zod, or a slug, such as plugin-zod.`,\n })\n return input.includes('/') ? input : `kubb-labs/${input}`\n}\n\n/** The shortest id `kubb add` and `kubb update` accept: the slug for an official extension, else `owner/slug`. */\nexport function toShortId(id: string): string {\n return id.startsWith('kubb-labs/') ? id.slice('kubb-labs/'.length) : id\n}\n\n/**\n * GETs `url` as JSON. A non-2xx answer or a body that is not JSON returns no `body`.\n * A network error throws with the URL.\n */\nasync function fetchJSON(url: string): Promise<{ status: number; body?: unknown }> {\n let response: Response\n try {\n response = await globalThis.fetch(url, { headers: { accept: 'application/json' } })\n } catch (cause) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.inputUnreachable,\n severity: 'error',\n message: `Could not read ${url}: ${getErrorMessage(cause)}`,\n help: 'Check your network connection and that the registry is reachable.',\n cause: toError(cause),\n })\n }\n if (!response.ok) return { status: response.status }\n return { status: response.status, body: await response.json().catch(() => undefined) }\n}\n\n/** Fetches the registry index, leaving out entries that fail validation. */\nexport async function fetchRegistryIndex({ registry }: { registry: string }): Promise<Array<RegistryEntry>> {\n const { status, body } = await fetchJSON(`${registry}/index.json`)\n const index = body as { version?: unknown; extensions?: unknown } | null | undefined\n if (status !== 200 || index?.version !== 1 || !Array.isArray(index.extensions)) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.inputRequestFailed,\n severity: 'error',\n message: `The registry at ${registry} sent an invalid index (HTTP ${status}).`,\n help: 'Try again later, or check that `KUBB_REGISTRY` points at a Kubb registry.',\n })\n }\n return index.extensions.filter(isRegistryEntry)\n}\n\n/** What the package manager installs: `<package>@<version>`, or a GitHub release's commit, so the install cannot move with the tag. */\nexport function installSpec(artifact: RegistryArtifact): string {\n return artifact.kind === 'github' ? `github:${artifact.owner}/${artifact.repo}#${artifact.commit}` : `${artifact.package}@${artifact.version}`\n}\n\n/** Why the running Kubb or Node.js version cannot use this extension, or `undefined` when it can. */\nexport function incompatibility(manifest: ExtensionManifest): string | undefined {\n try {\n checkCompatibility({ name: manifest.name, compatibility: manifest.compatibility })\n return undefined\n } catch (error) {\n return getErrorMessage(error)\n }\n}\n\n/**\n * Whether `latest` is newer than `installed`. An unknown installed version counts as outdated, and an\n * invalid `latest` never does.\n */\nexport function isOutdated({ installed, latest }: { installed: string | undefined; latest: string }): boolean {\n if (!isValid(latest)) return false\n return !installed || !isValid(installed) || isLessThan(installed, latest)\n}\n\n/** `major`, `minor`, or `patch` from `installed` to `latest`, or `undefined` when either is unknown or invalid. */\nexport function bumpOf({ installed, latest }: { installed: string | undefined; latest: string }): 'major' | 'minor' | 'patch' | undefined {\n if (!installed || !isValid(installed) || !isValid(latest)) return undefined\n const kind = difference(installed, latest)\n if (!kind) return undefined\n if (kind.endsWith('major')) return 'major'\n return kind.endsWith('minor') ? 'minor' : 'patch'\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, index) => index)\n for (let i = 1; i <= a.length; i++) {\n let previous = row[0]!\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const current = row[j]!\n row[j] = Math.min(row[j]! + 1, row[j - 1]! + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1))\n previous = current\n }\n }\n return row[b.length]!\n}\n\n/** Words every extension shares, so they never make two ids look alike. */\nconst COMMON_TOKENS = new Set(['kubb', 'plugin', 'adapter', 'parser'])\n\n/**\n * The ids closest to a mistyped `input`, best first, at most three: those within a few edits of it,\n * or sharing a word such as `zod` in any order. Compares slugs, so the owner may be left out.\n */\nexport function closestIds({ input, ids }: { input: string; ids: Array<string> }): Array<string> {\n const slug = (id: string) => id.slice(id.indexOf('/') + 1)\n const tokens = new Set(\n slug(input)\n .split('-')\n .filter((token) => !COMMON_TOKENS.has(token)),\n )\n return ids\n .map((id) => ({\n id,\n distance: editDistance(slug(input), slug(id)),\n shared: slug(id)\n .split('-')\n .some((token) => tokens.has(token)),\n }))\n .filter(({ distance, shared }) => shared || distance <= Math.max(2, Math.floor(slug(input).length / 4)))\n .sort((a, b) => Number(b.shared) - Number(a.shared) || a.distance - b.distance)\n .slice(0, 3)\n .map(({ id }) => id)\n}\n"],"mappings":"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"}
|