@kubb/core 5.11.6 → 5.12.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/dist/{Hookable-CvStaYYq.js → Hookable-D3jKB944.js} +220 -10
- package/dist/Hookable-D3jKB944.js.map +1 -0
- package/dist/{createAdapter-DtX_MKL-.d.ts → createAdapter-DFXvZLZC.d.ts} +138 -131
- package/dist/index.cjs +218 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +169 -135
- package/dist/index.d.ts +33 -6
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/package.json +2 -2
- package/dist/Hookable-CvStaYYq.js.map +0 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { satisfies } from "verkit";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import process$1 from "node:process";
|
|
5
|
+
import { x } from "tinyexec";
|
|
6
|
+
import { glob, mkdir, mkdtemp, readFile, rm, rm as rm$1, writeFile } from "node:fs/promises";
|
|
7
|
+
import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
8
|
import { existsSync as existsSync$1 } from "node:fs";
|
|
7
9
|
import { stripVTControlCharacters, styleText } from "node:util";
|
|
8
10
|
import { ast, collectImportedRefNames, collectUsedSchemaNames, extractStringsFromNodes } from "@kubb/ast";
|
|
11
|
+
import dns from "node:dns";
|
|
12
|
+
import net from "node:net";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
9
14
|
//#region \0rolldown/runtime.js
|
|
10
15
|
var __defProp = Object.defineProperty;
|
|
11
16
|
var __name = (target, value) => __defProp(target, "name", {
|
|
@@ -14,7 +19,7 @@ var __name = (target, value) => __defProp(target, "name", {
|
|
|
14
19
|
});
|
|
15
20
|
//#endregion
|
|
16
21
|
//#region package.json
|
|
17
|
-
var version = "5.
|
|
22
|
+
var version = "5.12.1";
|
|
18
23
|
//#endregion
|
|
19
24
|
//#region ../../internals/utils/src/casing.ts
|
|
20
25
|
/**
|
|
@@ -364,6 +369,12 @@ const diagnosticCatalog = {
|
|
|
364
369
|
cause: "A URL set as `input` (or reached through a `$ref`) never answered, so the request failed before a status came back. A refused connection, an unknown host, an expired certificate, and a timeout all land here.",
|
|
365
370
|
fix: "Check that the host is running and reachable from this machine. For a local server, start it and confirm the port matches the one in `input`."
|
|
366
371
|
},
|
|
372
|
+
gitInputFailed: {
|
|
373
|
+
code: "KUBB_GIT_INPUT_FAILED",
|
|
374
|
+
title: "Git input failed",
|
|
375
|
+
cause: "A git `input` (`git+<repo>#<ref>:<path>`) is malformed, or `git` could not fetch it: git is not installed, the repository or ref does not exist, or the credentials were refused.",
|
|
376
|
+
fix: "Write the input as `git+<repo>#<ref>:<path>`. Run `git fetch --depth 1 <repo> <ref>` yourself to see the git error, and set up a credential helper or SSH key for a private repository."
|
|
377
|
+
},
|
|
367
378
|
inputRequired: {
|
|
368
379
|
code: "KUBB_INPUT_REQUIRED",
|
|
369
380
|
title: "Input required",
|
|
@@ -1234,18 +1245,209 @@ function definePlugin(definition) {
|
|
|
1234
1245
|
});
|
|
1235
1246
|
}
|
|
1236
1247
|
//#endregion
|
|
1248
|
+
//#region src/publicAddress.ts
|
|
1249
|
+
function createBlockList() {
|
|
1250
|
+
const list = new net.BlockList();
|
|
1251
|
+
for (const [address, prefix] of [
|
|
1252
|
+
["0.0.0.0", 8],
|
|
1253
|
+
["10.0.0.0", 8],
|
|
1254
|
+
["100.64.0.0", 10],
|
|
1255
|
+
["127.0.0.0", 8],
|
|
1256
|
+
["169.254.0.0", 16],
|
|
1257
|
+
["172.16.0.0", 12],
|
|
1258
|
+
["192.0.0.0", 24],
|
|
1259
|
+
["192.168.0.0", 16],
|
|
1260
|
+
["198.18.0.0", 15],
|
|
1261
|
+
["224.0.0.0", 3]
|
|
1262
|
+
]) list.addSubnet(address, prefix, "ipv4");
|
|
1263
|
+
for (const [address, prefix] of [
|
|
1264
|
+
["::", 128],
|
|
1265
|
+
["::1", 128],
|
|
1266
|
+
["64:ff9b::", 96],
|
|
1267
|
+
["fc00::", 7],
|
|
1268
|
+
["fe80::", 10],
|
|
1269
|
+
["ff00::", 8]
|
|
1270
|
+
]) list.addSubnet(address, prefix, "ipv6");
|
|
1271
|
+
return list;
|
|
1272
|
+
}
|
|
1273
|
+
const privateAddresses = createBlockList();
|
|
1274
|
+
/**
|
|
1275
|
+
* Whether `address` is a routable public IP. Loopback, private, link-local, cloud metadata, and
|
|
1276
|
+
* multicast ranges are not, and an IPv4-mapped IPv6 address counts as the IPv4 address it wraps.
|
|
1277
|
+
* Pass `blocked` to replace the ranges.
|
|
1278
|
+
*/
|
|
1279
|
+
function isPublicAddress(address, blocked = privateAddresses) {
|
|
1280
|
+
const family = net.isIP(address);
|
|
1281
|
+
return family !== 0 && !blocked.check(address, family === 4 ? "ipv4" : "ipv6");
|
|
1282
|
+
}
|
|
1283
|
+
//#endregion
|
|
1284
|
+
//#region src/gitInput.ts
|
|
1285
|
+
/** Matches a git `input`: the `git+` prefix npm uses for git dependencies. */
|
|
1286
|
+
const gitInputRegExp = /^git\+/i;
|
|
1287
|
+
function gitError(message, cause) {
|
|
1288
|
+
return new Diagnostics.Error({
|
|
1289
|
+
code: Diagnostics.code.gitInputFailed,
|
|
1290
|
+
severity: "error",
|
|
1291
|
+
message,
|
|
1292
|
+
help: "Write a git `input` as `git+<repo>#<ref>:<path>`, for example `git+https://github.com/org/api.git#main:specs/openapi.yaml`. Check that `git` is installed and that `git fetch` can reach the repository with your credentials.",
|
|
1293
|
+
location: { kind: "config" },
|
|
1294
|
+
cause: cause === void 0 ? void 0 : toError(cause)
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
/** The repository URL without a user or password, safe to print. */
|
|
1298
|
+
function redact(repo) {
|
|
1299
|
+
if (!URL.canParse(repo)) return repo;
|
|
1300
|
+
const url = new URL(repo);
|
|
1301
|
+
url.username = "";
|
|
1302
|
+
url.password = "";
|
|
1303
|
+
return url.href;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Splits `git+<repo>#<ref>:<path>` into its parts. Git refs cannot contain `:`, so the first one
|
|
1307
|
+
* in the fragment ends the ref. Throws `KUBB_GIT_INPUT_FAILED` when the fragment or path is missing.
|
|
1308
|
+
*/
|
|
1309
|
+
function parseGitInput(input) {
|
|
1310
|
+
const hashIndex = input.indexOf("#");
|
|
1311
|
+
const repo = input.slice(4, hashIndex === -1 ? void 0 : hashIndex);
|
|
1312
|
+
const fragment = hashIndex === -1 ? "" : input.slice(hashIndex + 1);
|
|
1313
|
+
const colonIndex = fragment.indexOf(":");
|
|
1314
|
+
const ref = colonIndex === -1 ? void 0 : fragment.slice(0, colonIndex);
|
|
1315
|
+
const path = colonIndex === -1 ? fragment : fragment.slice(colonIndex + 1);
|
|
1316
|
+
if (!repo || !path || ref === "") throw gitError(`The git \`input\` for ${redact(repo)} has no repository, ref, or spec path after \`#\`.`);
|
|
1317
|
+
return {
|
|
1318
|
+
repo,
|
|
1319
|
+
ref,
|
|
1320
|
+
path
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* The `git -c` settings that confine a fetch to https and to `addresses`: the connection goes to
|
|
1325
|
+
* the addresses already checked, whatever the host answers to a second lookup, and a redirect to
|
|
1326
|
+
* another host is an error. Needs git 2.37 for the pinning.
|
|
1327
|
+
*
|
|
1328
|
+
* @internal
|
|
1329
|
+
*/
|
|
1330
|
+
function publicFetchConfig({ host, port, addresses }) {
|
|
1331
|
+
const pinned = addresses.map((address) => net.isIPv6(address) ? `[${address}]` : address).join(",");
|
|
1332
|
+
const config = [
|
|
1333
|
+
"http.followRedirects=false",
|
|
1334
|
+
"protocol.allow=never",
|
|
1335
|
+
"protocol.https.allow=always"
|
|
1336
|
+
];
|
|
1337
|
+
if (!net.isIP(host)) config.push(`http.curloptResolve=${host}:${port}:${pinned}`);
|
|
1338
|
+
return config.flatMap((setting) => ["-c", setting]);
|
|
1339
|
+
}
|
|
1340
|
+
async function resolvePublicFetchConfig(repo, signal) {
|
|
1341
|
+
if (!URL.canParse(repo) || new URL(repo).protocol !== "https:") throw gitError("A public git `input` must be an https repository URL, not an ssh, git, or file address.");
|
|
1342
|
+
const url = new URL(repo);
|
|
1343
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
1344
|
+
const isLiteral = net.isIP(host) !== 0;
|
|
1345
|
+
const addresses = isLiteral ? [host] : (await dns.promises.lookup(host, { all: true })).map(({ address }) => address);
|
|
1346
|
+
if (!addresses.length || addresses.some((address) => !isPublicAddress(address))) throw gitError(`${host} is not a public address, so this git \`input\` is not fetched.`);
|
|
1347
|
+
if (!isLiteral) {
|
|
1348
|
+
const { stdout } = await x("git", ["--version"], {
|
|
1349
|
+
nodeOptions: { signal },
|
|
1350
|
+
throwOnError: true
|
|
1351
|
+
});
|
|
1352
|
+
const [, major = 0, minor = 0] = /(\d+)\.(\d+)/.exec(stdout)?.map(Number) ?? [];
|
|
1353
|
+
if (major < 2 || major === 2 && minor < 37) throw gitError(`Fetching a public git \`input\` needs git 2.37 or newer, found ${stdout.trim()}.`);
|
|
1354
|
+
}
|
|
1355
|
+
return publicFetchConfig({
|
|
1356
|
+
host,
|
|
1357
|
+
port: url.port || "443",
|
|
1358
|
+
addresses
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Fetches one commit of a git repository into a temporary directory with a shallow `git fetch`,
|
|
1363
|
+
* so any host and the user's own git credentials work, and relative `$ref` files resolve.
|
|
1364
|
+
* Resolves to the spec path on disk and a `dispose` that deletes the checkout.
|
|
1365
|
+
*
|
|
1366
|
+
* With `publicOnly`, the repository must be an https URL on public addresses, the fetch connects to
|
|
1367
|
+
* the addresses checked, and it does not follow a redirect.
|
|
1368
|
+
*/
|
|
1369
|
+
async function checkoutGitInput(input, signal, { publicOnly = false } = {}) {
|
|
1370
|
+
const { repo, ref, path } = parseGitInput(input);
|
|
1371
|
+
const fetchConfig = publicOnly ? await resolvePublicFetchConfig(repo, signal) : [];
|
|
1372
|
+
const dir = await mkdtemp(join(tmpdir(), "kubb-git-"));
|
|
1373
|
+
const dispose = () => rm(dir, {
|
|
1374
|
+
recursive: true,
|
|
1375
|
+
force: true
|
|
1376
|
+
});
|
|
1377
|
+
const specPath = resolve(dir, path);
|
|
1378
|
+
const relativePath = relative(dir, specPath);
|
|
1379
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
1380
|
+
await dispose();
|
|
1381
|
+
throw gitError(`The spec path "${path}" in the git \`input\` points outside the repository.`);
|
|
1382
|
+
}
|
|
1383
|
+
const label = `${redact(repo)}${ref ? ` at ${ref}` : ""}`;
|
|
1384
|
+
const env = {
|
|
1385
|
+
...process$1.env,
|
|
1386
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
1387
|
+
};
|
|
1388
|
+
const steps = [
|
|
1389
|
+
["init", "--quiet"],
|
|
1390
|
+
[
|
|
1391
|
+
"config",
|
|
1392
|
+
"core.symlinks",
|
|
1393
|
+
"false"
|
|
1394
|
+
],
|
|
1395
|
+
[
|
|
1396
|
+
...fetchConfig,
|
|
1397
|
+
"fetch",
|
|
1398
|
+
"--quiet",
|
|
1399
|
+
"--depth",
|
|
1400
|
+
"1",
|
|
1401
|
+
"--end-of-options",
|
|
1402
|
+
repo,
|
|
1403
|
+
ref ?? "HEAD"
|
|
1404
|
+
],
|
|
1405
|
+
[
|
|
1406
|
+
"checkout",
|
|
1407
|
+
"--quiet",
|
|
1408
|
+
"FETCH_HEAD"
|
|
1409
|
+
]
|
|
1410
|
+
];
|
|
1411
|
+
try {
|
|
1412
|
+
for (const args of steps) {
|
|
1413
|
+
const result = await x("git", args, {
|
|
1414
|
+
nodeOptions: {
|
|
1415
|
+
cwd: dir,
|
|
1416
|
+
env,
|
|
1417
|
+
signal
|
|
1418
|
+
},
|
|
1419
|
+
throwOnError: false
|
|
1420
|
+
});
|
|
1421
|
+
if (result.exitCode !== 0) {
|
|
1422
|
+
const stderr = result.stderr.replaceAll(repo, redact(repo)).trim();
|
|
1423
|
+
throw gitError(`\`git ${args[0]}\` failed for ${label}: ${stderr || `exit code ${result.exitCode}`}`);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
await dispose();
|
|
1428
|
+
if (Diagnostics.isError(error)) throw error;
|
|
1429
|
+
throw gitError(`Cannot run \`git\` for ${label}: ${getErrorMessage(error)}`, error);
|
|
1430
|
+
}
|
|
1431
|
+
return {
|
|
1432
|
+
path: specPath,
|
|
1433
|
+
dispose
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
//#endregion
|
|
1237
1437
|
//#region src/input.ts
|
|
1238
1438
|
/**
|
|
1239
1439
|
* Classifies an `input` value so callers branch on it once instead of repeating the checks.
|
|
1240
1440
|
*
|
|
1241
1441
|
* A non-string is a parsed spec (`object`). A string is `inline` when it holds OpenAPI content,
|
|
1242
1442
|
* meaning it starts with `{` or `[`, spans multiple lines, or opens with a YAML `openapi:` or
|
|
1243
|
-
* `swagger:` key. Otherwise a string is
|
|
1443
|
+
* `swagger:` key. Otherwise a string is `git` when it starts with `git+`, a `url` when it parses as
|
|
1444
|
+
* one, or a `file` path.
|
|
1244
1445
|
*/
|
|
1245
1446
|
function getInputKind(input) {
|
|
1246
1447
|
if (typeof input !== "string") return "object";
|
|
1247
1448
|
const trimmed = input.trimStart();
|
|
1248
1449
|
if (trimmed.startsWith("{") || trimmed.startsWith("[") || input.includes("\n") || /^(openapi|swagger)\s*:/i.test(trimmed)) return "inline";
|
|
1450
|
+
if (gitInputRegExp.test(input)) return "git";
|
|
1249
1451
|
if (URL.canParse(input)) return "url";
|
|
1250
1452
|
return "file";
|
|
1251
1453
|
}
|
|
@@ -1269,8 +1471,9 @@ function isLegacyInput(input) {
|
|
|
1269
1471
|
/**
|
|
1270
1472
|
* Normalizes `config.input` into an `AdapterSource` the adapter can parse.
|
|
1271
1473
|
*
|
|
1272
|
-
* A parsed object and inline content become `{ type: 'data' }`; a URL
|
|
1273
|
-
* local path is resolved against `config.root`,
|
|
1474
|
+
* A parsed object and inline content become `{ type: 'data' }`; a URL or git input is kept
|
|
1475
|
+
* verbatim and a local path is resolved against `config.root`, all as `{ type: 'path' }`. The
|
|
1476
|
+
* driver checks out a git input before the adapter parses it.
|
|
1274
1477
|
*/
|
|
1275
1478
|
function inputToAdapterSource(config) {
|
|
1276
1479
|
const input = config.input;
|
|
@@ -1297,7 +1500,7 @@ function inputToAdapterSource(config) {
|
|
|
1297
1500
|
type: "data",
|
|
1298
1501
|
data: input
|
|
1299
1502
|
};
|
|
1300
|
-
if (kind === "url") return {
|
|
1503
|
+
if (kind === "url" || kind === "git") return {
|
|
1301
1504
|
type: "path",
|
|
1302
1505
|
path: input
|
|
1303
1506
|
};
|
|
@@ -2231,13 +2434,20 @@ var KubbDriver = class {
|
|
|
2231
2434
|
async #parseInput() {
|
|
2232
2435
|
if (this.inputNode || !this.adapter || !this.#adapterSource) return;
|
|
2233
2436
|
this.options.signal?.throwIfAborted();
|
|
2437
|
+
const source = this.#adapterSource;
|
|
2438
|
+
const checkout = source.type === "path" && gitInputRegExp.test(source.path) ? await checkoutGitInput(source.path, this.options.signal) : void 0;
|
|
2234
2439
|
try {
|
|
2235
|
-
this.inputNode = await this.adapter.parse(
|
|
2440
|
+
this.inputNode = await this.adapter.parse(checkout ? {
|
|
2441
|
+
type: "path",
|
|
2442
|
+
path: checkout.path
|
|
2443
|
+
} : source, { signal: this.options.signal });
|
|
2236
2444
|
} catch (caughtError) {
|
|
2237
2445
|
throw new Diagnostics.Error(Diagnostics.fromExtension(caughtError, {
|
|
2238
2446
|
kind: "adapter",
|
|
2239
2447
|
extension: this.adapter
|
|
2240
2448
|
}));
|
|
2449
|
+
} finally {
|
|
2450
|
+
await checkout?.dispose();
|
|
2241
2451
|
}
|
|
2242
2452
|
}
|
|
2243
2453
|
/**
|
|
@@ -2856,6 +3066,6 @@ var Hookable = class {
|
|
|
2856
3066
|
}
|
|
2857
3067
|
};
|
|
2858
3068
|
//#endregion
|
|
2859
|
-
export {
|
|
3069
|
+
export { formatMs as A, toError as B, useConfig as C, defineExtension as D, checkCompatibility as E, listFiles as F, __name as H, read as I, rm$1 as L, inParallel as M, existsSync$1 as N, resolveMeta as O, isPathInside as P, write as R, tryUseKubb as S, useOptions as T, camelCase as V, kubbContext as _, FileManager as a, setOptions as b, Resolver as c, isPublicAddress as d, assertPluginOption as f, injectFile as g, addMacro as h, createNodeCache as i, getElapsedMs as j, Diagnostics as k, getInputKind as l, addGenerator as m, KubbDriver as n, _usingCtx as o, definePlugin as p, pluginNotFoundError as r, createResolver as s, Hookable as t, checkoutGitInput as u, requirePlugin as v, useKubb as w, setResolver as x, setMacros as y, writeRaw as z };
|
|
2860
3070
|
|
|
2861
|
-
//# sourceMappingURL=Hookable-
|
|
3071
|
+
//# sourceMappingURL=Hookable-D3jKB944.js.map
|