@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
package/dist/index.cjs
CHANGED
|
@@ -32,8 +32,12 @@ node_path = __toESM(node_path, 1);
|
|
|
32
32
|
let node_fs = require("node:fs");
|
|
33
33
|
let node_util = require("node:util");
|
|
34
34
|
let _kubb_ast = require("@kubb/ast");
|
|
35
|
-
let
|
|
35
|
+
let node_dns = require("node:dns");
|
|
36
|
+
node_dns = __toESM(node_dns, 1);
|
|
37
|
+
let node_net = require("node:net");
|
|
38
|
+
node_net = __toESM(node_net, 1);
|
|
36
39
|
let node_os = require("node:os");
|
|
40
|
+
let node_crypto = require("node:crypto");
|
|
37
41
|
let node_readline = require("node:readline");
|
|
38
42
|
let unstorage = require("unstorage");
|
|
39
43
|
let unstorage_drivers_fs = require("unstorage/drivers/fs");
|
|
@@ -41,7 +45,7 @@ unstorage_drivers_fs = __toESM(unstorage_drivers_fs, 1);
|
|
|
41
45
|
let node_url = require("node:url");
|
|
42
46
|
let node_module = require("node:module");
|
|
43
47
|
//#region package.json
|
|
44
|
-
var version = "5.
|
|
48
|
+
var version = "5.12.1";
|
|
45
49
|
//#endregion
|
|
46
50
|
//#region ../../internals/utils/src/casing.ts
|
|
47
51
|
/**
|
|
@@ -529,6 +533,12 @@ const diagnosticCatalog = {
|
|
|
529
533
|
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.",
|
|
530
534
|
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`."
|
|
531
535
|
},
|
|
536
|
+
gitInputFailed: {
|
|
537
|
+
code: "KUBB_GIT_INPUT_FAILED",
|
|
538
|
+
title: "Git input failed",
|
|
539
|
+
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.",
|
|
540
|
+
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."
|
|
541
|
+
},
|
|
532
542
|
inputRequired: {
|
|
533
543
|
code: "KUBB_INPUT_REQUIRED",
|
|
534
544
|
title: "Input required",
|
|
@@ -1417,18 +1427,209 @@ function definePlugin(definition) {
|
|
|
1417
1427
|
});
|
|
1418
1428
|
}
|
|
1419
1429
|
//#endregion
|
|
1430
|
+
//#region src/publicAddress.ts
|
|
1431
|
+
function createBlockList() {
|
|
1432
|
+
const list = new node_net.default.BlockList();
|
|
1433
|
+
for (const [address, prefix] of [
|
|
1434
|
+
["0.0.0.0", 8],
|
|
1435
|
+
["10.0.0.0", 8],
|
|
1436
|
+
["100.64.0.0", 10],
|
|
1437
|
+
["127.0.0.0", 8],
|
|
1438
|
+
["169.254.0.0", 16],
|
|
1439
|
+
["172.16.0.0", 12],
|
|
1440
|
+
["192.0.0.0", 24],
|
|
1441
|
+
["192.168.0.0", 16],
|
|
1442
|
+
["198.18.0.0", 15],
|
|
1443
|
+
["224.0.0.0", 3]
|
|
1444
|
+
]) list.addSubnet(address, prefix, "ipv4");
|
|
1445
|
+
for (const [address, prefix] of [
|
|
1446
|
+
["::", 128],
|
|
1447
|
+
["::1", 128],
|
|
1448
|
+
["64:ff9b::", 96],
|
|
1449
|
+
["fc00::", 7],
|
|
1450
|
+
["fe80::", 10],
|
|
1451
|
+
["ff00::", 8]
|
|
1452
|
+
]) list.addSubnet(address, prefix, "ipv6");
|
|
1453
|
+
return list;
|
|
1454
|
+
}
|
|
1455
|
+
const privateAddresses = createBlockList();
|
|
1456
|
+
/**
|
|
1457
|
+
* Whether `address` is a routable public IP. Loopback, private, link-local, cloud metadata, and
|
|
1458
|
+
* multicast ranges are not, and an IPv4-mapped IPv6 address counts as the IPv4 address it wraps.
|
|
1459
|
+
* Pass `blocked` to replace the ranges.
|
|
1460
|
+
*/
|
|
1461
|
+
function isPublicAddress(address, blocked = privateAddresses) {
|
|
1462
|
+
const family = node_net.default.isIP(address);
|
|
1463
|
+
return family !== 0 && !blocked.check(address, family === 4 ? "ipv4" : "ipv6");
|
|
1464
|
+
}
|
|
1465
|
+
//#endregion
|
|
1466
|
+
//#region src/gitInput.ts
|
|
1467
|
+
/** Matches a git `input`: the `git+` prefix npm uses for git dependencies. */
|
|
1468
|
+
const gitInputRegExp = /^git\+/i;
|
|
1469
|
+
function gitError(message, cause) {
|
|
1470
|
+
return new Diagnostics.Error({
|
|
1471
|
+
code: Diagnostics.code.gitInputFailed,
|
|
1472
|
+
severity: "error",
|
|
1473
|
+
message,
|
|
1474
|
+
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.",
|
|
1475
|
+
location: { kind: "config" },
|
|
1476
|
+
cause: cause === void 0 ? void 0 : toError(cause)
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
/** The repository URL without a user or password, safe to print. */
|
|
1480
|
+
function redact(repo) {
|
|
1481
|
+
if (!URL.canParse(repo)) return repo;
|
|
1482
|
+
const url = new URL(repo);
|
|
1483
|
+
url.username = "";
|
|
1484
|
+
url.password = "";
|
|
1485
|
+
return url.href;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Splits `git+<repo>#<ref>:<path>` into its parts. Git refs cannot contain `:`, so the first one
|
|
1489
|
+
* in the fragment ends the ref. Throws `KUBB_GIT_INPUT_FAILED` when the fragment or path is missing.
|
|
1490
|
+
*/
|
|
1491
|
+
function parseGitInput(input) {
|
|
1492
|
+
const hashIndex = input.indexOf("#");
|
|
1493
|
+
const repo = input.slice(4, hashIndex === -1 ? void 0 : hashIndex);
|
|
1494
|
+
const fragment = hashIndex === -1 ? "" : input.slice(hashIndex + 1);
|
|
1495
|
+
const colonIndex = fragment.indexOf(":");
|
|
1496
|
+
const ref = colonIndex === -1 ? void 0 : fragment.slice(0, colonIndex);
|
|
1497
|
+
const path = colonIndex === -1 ? fragment : fragment.slice(colonIndex + 1);
|
|
1498
|
+
if (!repo || !path || ref === "") throw gitError(`The git \`input\` for ${redact(repo)} has no repository, ref, or spec path after \`#\`.`);
|
|
1499
|
+
return {
|
|
1500
|
+
repo,
|
|
1501
|
+
ref,
|
|
1502
|
+
path
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* The `git -c` settings that confine a fetch to https and to `addresses`: the connection goes to
|
|
1507
|
+
* the addresses already checked, whatever the host answers to a second lookup, and a redirect to
|
|
1508
|
+
* another host is an error. Needs git 2.37 for the pinning.
|
|
1509
|
+
*
|
|
1510
|
+
* @internal
|
|
1511
|
+
*/
|
|
1512
|
+
function publicFetchConfig({ host, port, addresses }) {
|
|
1513
|
+
const pinned = addresses.map((address) => node_net.default.isIPv6(address) ? `[${address}]` : address).join(",");
|
|
1514
|
+
const config = [
|
|
1515
|
+
"http.followRedirects=false",
|
|
1516
|
+
"protocol.allow=never",
|
|
1517
|
+
"protocol.https.allow=always"
|
|
1518
|
+
];
|
|
1519
|
+
if (!node_net.default.isIP(host)) config.push(`http.curloptResolve=${host}:${port}:${pinned}`);
|
|
1520
|
+
return config.flatMap((setting) => ["-c", setting]);
|
|
1521
|
+
}
|
|
1522
|
+
async function resolvePublicFetchConfig(repo, signal) {
|
|
1523
|
+
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.");
|
|
1524
|
+
const url = new URL(repo);
|
|
1525
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
1526
|
+
const isLiteral = node_net.default.isIP(host) !== 0;
|
|
1527
|
+
const addresses = isLiteral ? [host] : (await node_dns.default.promises.lookup(host, { all: true })).map(({ address }) => address);
|
|
1528
|
+
if (!addresses.length || addresses.some((address) => !isPublicAddress(address))) throw gitError(`${host} is not a public address, so this git \`input\` is not fetched.`);
|
|
1529
|
+
if (!isLiteral) {
|
|
1530
|
+
const { stdout } = await (0, tinyexec.x)("git", ["--version"], {
|
|
1531
|
+
nodeOptions: { signal },
|
|
1532
|
+
throwOnError: true
|
|
1533
|
+
});
|
|
1534
|
+
const [, major = 0, minor = 0] = /(\d+)\.(\d+)/.exec(stdout)?.map(Number) ?? [];
|
|
1535
|
+
if (major < 2 || major === 2 && minor < 37) throw gitError(`Fetching a public git \`input\` needs git 2.37 or newer, found ${stdout.trim()}.`);
|
|
1536
|
+
}
|
|
1537
|
+
return publicFetchConfig({
|
|
1538
|
+
host,
|
|
1539
|
+
port: url.port || "443",
|
|
1540
|
+
addresses
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Fetches one commit of a git repository into a temporary directory with a shallow `git fetch`,
|
|
1545
|
+
* so any host and the user's own git credentials work, and relative `$ref` files resolve.
|
|
1546
|
+
* Resolves to the spec path on disk and a `dispose` that deletes the checkout.
|
|
1547
|
+
*
|
|
1548
|
+
* With `publicOnly`, the repository must be an https URL on public addresses, the fetch connects to
|
|
1549
|
+
* the addresses checked, and it does not follow a redirect.
|
|
1550
|
+
*/
|
|
1551
|
+
async function checkoutGitInput(input, signal, { publicOnly = false } = {}) {
|
|
1552
|
+
const { repo, ref, path } = parseGitInput(input);
|
|
1553
|
+
const fetchConfig = publicOnly ? await resolvePublicFetchConfig(repo, signal) : [];
|
|
1554
|
+
const dir = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "kubb-git-"));
|
|
1555
|
+
const dispose = () => (0, node_fs_promises.rm)(dir, {
|
|
1556
|
+
recursive: true,
|
|
1557
|
+
force: true
|
|
1558
|
+
});
|
|
1559
|
+
const specPath = (0, node_path.resolve)(dir, path);
|
|
1560
|
+
const relativePath = (0, node_path.relative)(dir, specPath);
|
|
1561
|
+
if (relativePath === ".." || relativePath.startsWith(`..${node_path.sep}`) || (0, node_path.isAbsolute)(relativePath)) {
|
|
1562
|
+
await dispose();
|
|
1563
|
+
throw gitError(`The spec path "${path}" in the git \`input\` points outside the repository.`);
|
|
1564
|
+
}
|
|
1565
|
+
const label = `${redact(repo)}${ref ? ` at ${ref}` : ""}`;
|
|
1566
|
+
const env = {
|
|
1567
|
+
...node_process.default.env,
|
|
1568
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
1569
|
+
};
|
|
1570
|
+
const steps = [
|
|
1571
|
+
["init", "--quiet"],
|
|
1572
|
+
[
|
|
1573
|
+
"config",
|
|
1574
|
+
"core.symlinks",
|
|
1575
|
+
"false"
|
|
1576
|
+
],
|
|
1577
|
+
[
|
|
1578
|
+
...fetchConfig,
|
|
1579
|
+
"fetch",
|
|
1580
|
+
"--quiet",
|
|
1581
|
+
"--depth",
|
|
1582
|
+
"1",
|
|
1583
|
+
"--end-of-options",
|
|
1584
|
+
repo,
|
|
1585
|
+
ref ?? "HEAD"
|
|
1586
|
+
],
|
|
1587
|
+
[
|
|
1588
|
+
"checkout",
|
|
1589
|
+
"--quiet",
|
|
1590
|
+
"FETCH_HEAD"
|
|
1591
|
+
]
|
|
1592
|
+
];
|
|
1593
|
+
try {
|
|
1594
|
+
for (const args of steps) {
|
|
1595
|
+
const result = await (0, tinyexec.x)("git", args, {
|
|
1596
|
+
nodeOptions: {
|
|
1597
|
+
cwd: dir,
|
|
1598
|
+
env,
|
|
1599
|
+
signal
|
|
1600
|
+
},
|
|
1601
|
+
throwOnError: false
|
|
1602
|
+
});
|
|
1603
|
+
if (result.exitCode !== 0) {
|
|
1604
|
+
const stderr = result.stderr.replaceAll(repo, redact(repo)).trim();
|
|
1605
|
+
throw gitError(`\`git ${args[0]}\` failed for ${label}: ${stderr || `exit code ${result.exitCode}`}`);
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
await dispose();
|
|
1610
|
+
if (Diagnostics.isError(error)) throw error;
|
|
1611
|
+
throw gitError(`Cannot run \`git\` for ${label}: ${getErrorMessage(error)}`, error);
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
path: specPath,
|
|
1615
|
+
dispose
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
//#endregion
|
|
1420
1619
|
//#region src/input.ts
|
|
1421
1620
|
/**
|
|
1422
1621
|
* Classifies an `input` value so callers branch on it once instead of repeating the checks.
|
|
1423
1622
|
*
|
|
1424
1623
|
* A non-string is a parsed spec (`object`). A string is `inline` when it holds OpenAPI content,
|
|
1425
1624
|
* meaning it starts with `{` or `[`, spans multiple lines, or opens with a YAML `openapi:` or
|
|
1426
|
-
* `swagger:` key. Otherwise a string is
|
|
1625
|
+
* `swagger:` key. Otherwise a string is `git` when it starts with `git+`, a `url` when it parses as
|
|
1626
|
+
* one, or a `file` path.
|
|
1427
1627
|
*/
|
|
1428
1628
|
function getInputKind(input) {
|
|
1429
1629
|
if (typeof input !== "string") return "object";
|
|
1430
1630
|
const trimmed = input.trimStart();
|
|
1431
1631
|
if (trimmed.startsWith("{") || trimmed.startsWith("[") || input.includes("\n") || /^(openapi|swagger)\s*:/i.test(trimmed)) return "inline";
|
|
1632
|
+
if (gitInputRegExp.test(input)) return "git";
|
|
1432
1633
|
if (URL.canParse(input)) return "url";
|
|
1433
1634
|
return "file";
|
|
1434
1635
|
}
|
|
@@ -1452,8 +1653,9 @@ function isLegacyInput(input) {
|
|
|
1452
1653
|
/**
|
|
1453
1654
|
* Normalizes `config.input` into an `AdapterSource` the adapter can parse.
|
|
1454
1655
|
*
|
|
1455
|
-
* A parsed object and inline content become `{ type: 'data' }`; a URL
|
|
1456
|
-
* local path is resolved against `config.root`,
|
|
1656
|
+
* A parsed object and inline content become `{ type: 'data' }`; a URL or git input is kept
|
|
1657
|
+
* verbatim and a local path is resolved against `config.root`, all as `{ type: 'path' }`. The
|
|
1658
|
+
* driver checks out a git input before the adapter parses it.
|
|
1457
1659
|
*/
|
|
1458
1660
|
function inputToAdapterSource(config) {
|
|
1459
1661
|
const input = config.input;
|
|
@@ -1480,7 +1682,7 @@ function inputToAdapterSource(config) {
|
|
|
1480
1682
|
type: "data",
|
|
1481
1683
|
data: input
|
|
1482
1684
|
};
|
|
1483
|
-
if (kind === "url") return {
|
|
1685
|
+
if (kind === "url" || kind === "git") return {
|
|
1484
1686
|
type: "path",
|
|
1485
1687
|
path: input
|
|
1486
1688
|
};
|
|
@@ -2414,13 +2616,20 @@ var KubbDriver = class {
|
|
|
2414
2616
|
async #parseInput() {
|
|
2415
2617
|
if (this.inputNode || !this.adapter || !this.#adapterSource) return;
|
|
2416
2618
|
this.options.signal?.throwIfAborted();
|
|
2619
|
+
const source = this.#adapterSource;
|
|
2620
|
+
const checkout = source.type === "path" && gitInputRegExp.test(source.path) ? await checkoutGitInput(source.path, this.options.signal) : void 0;
|
|
2417
2621
|
try {
|
|
2418
|
-
this.inputNode = await this.adapter.parse(
|
|
2622
|
+
this.inputNode = await this.adapter.parse(checkout ? {
|
|
2623
|
+
type: "path",
|
|
2624
|
+
path: checkout.path
|
|
2625
|
+
} : source, { signal: this.options.signal });
|
|
2419
2626
|
} catch (caughtError) {
|
|
2420
2627
|
throw new Diagnostics.Error(Diagnostics.fromExtension(caughtError, {
|
|
2421
2628
|
kind: "adapter",
|
|
2422
2629
|
extension: this.adapter
|
|
2423
2630
|
}));
|
|
2631
|
+
} finally {
|
|
2632
|
+
await checkout?.dispose();
|
|
2424
2633
|
}
|
|
2425
2634
|
}
|
|
2426
2635
|
/**
|
|
@@ -4262,6 +4471,7 @@ exports.addMacro = addMacro;
|
|
|
4262
4471
|
exports.assertPluginOption = assertPluginOption;
|
|
4263
4472
|
exports.cacheStorage = cacheStorage;
|
|
4264
4473
|
exports.checkCompatibility = checkCompatibility;
|
|
4474
|
+
exports.checkoutGitInput = checkoutGitInput;
|
|
4265
4475
|
exports.cliReporter = cliReporter;
|
|
4266
4476
|
exports.createAdapter = createAdapter;
|
|
4267
4477
|
exports.createCliReporter = createCliReporter;
|
|
@@ -4282,6 +4492,7 @@ exports.htmlReporter = htmlReporter;
|
|
|
4282
4492
|
exports.injectFile = injectFile;
|
|
4283
4493
|
exports.isExtensionManifest = isExtensionManifest;
|
|
4284
4494
|
exports.isPackageName = isPackageName;
|
|
4495
|
+
exports.isPublicAddress = isPublicAddress;
|
|
4285
4496
|
exports.isRegistryEntry = isRegistryEntry;
|
|
4286
4497
|
exports.jsonReporter = jsonReporter;
|
|
4287
4498
|
exports.kubbContext = kubbContext;
|