@kubb/core 5.11.6 → 5.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,14 @@
1
1
  import "node:module";
2
2
  import { satisfies } from "verkit";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
- import { glob, mkdir, readFile, rm, writeFile } from "node:fs/promises";
5
- import path, { dirname, isAbsolute, join, relative, resolve } from "node:path";
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 { tmpdir } from "node:os";
9
12
  //#region \0rolldown/runtime.js
10
13
  var __defProp = Object.defineProperty;
11
14
  var __name = (target, value) => __defProp(target, "name", {
@@ -14,7 +17,7 @@ var __name = (target, value) => __defProp(target, "name", {
14
17
  });
15
18
  //#endregion
16
19
  //#region package.json
17
- var version = "5.11.6";
20
+ var version = "5.12.0";
18
21
  //#endregion
19
22
  //#region ../../internals/utils/src/casing.ts
20
23
  /**
@@ -364,6 +367,12 @@ const diagnosticCatalog = {
364
367
  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
368
  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
369
  },
370
+ gitInputFailed: {
371
+ code: "KUBB_GIT_INPUT_FAILED",
372
+ title: "Git input failed",
373
+ 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.",
374
+ 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."
375
+ },
367
376
  inputRequired: {
368
377
  code: "KUBB_INPUT_REQUIRED",
369
378
  title: "Input required",
@@ -1234,18 +1243,130 @@ function definePlugin(definition) {
1234
1243
  });
1235
1244
  }
1236
1245
  //#endregion
1246
+ //#region src/gitInput.ts
1247
+ /** Matches a git `input`: the `git+` prefix npm uses for git dependencies. */
1248
+ const gitInputRegExp = /^git\+/i;
1249
+ function gitError(message, cause) {
1250
+ return new Diagnostics.Error({
1251
+ code: Diagnostics.code.gitInputFailed,
1252
+ severity: "error",
1253
+ message,
1254
+ 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.",
1255
+ location: { kind: "config" },
1256
+ cause: cause === void 0 ? void 0 : toError(cause)
1257
+ });
1258
+ }
1259
+ /** The repository URL without a user or password, safe to print. */
1260
+ function redact(repo) {
1261
+ if (!URL.canParse(repo)) return repo;
1262
+ const url = new URL(repo);
1263
+ url.username = "";
1264
+ url.password = "";
1265
+ return url.href;
1266
+ }
1267
+ /**
1268
+ * Splits `git+<repo>#<ref>:<path>` into its parts. Git refs cannot contain `:`, so the first one
1269
+ * in the fragment ends the ref. Throws `KUBB_GIT_INPUT_FAILED` when the fragment or path is missing.
1270
+ */
1271
+ function parseGitInput(input) {
1272
+ const hashIndex = input.indexOf("#");
1273
+ const repo = input.slice(4, hashIndex === -1 ? void 0 : hashIndex);
1274
+ const fragment = hashIndex === -1 ? "" : input.slice(hashIndex + 1);
1275
+ const colonIndex = fragment.indexOf(":");
1276
+ const ref = colonIndex === -1 ? void 0 : fragment.slice(0, colonIndex);
1277
+ const path = colonIndex === -1 ? fragment : fragment.slice(colonIndex + 1);
1278
+ if (!repo || !path || ref === "") throw gitError(`The git \`input\` for ${redact(repo)} has no repository, ref, or spec path after \`#\`.`);
1279
+ return {
1280
+ repo,
1281
+ ref,
1282
+ path
1283
+ };
1284
+ }
1285
+ /**
1286
+ * Fetches one commit of a git repository into a temporary directory with a shallow `git fetch`,
1287
+ * so any host and the user's own git credentials work, and relative `$ref` files resolve.
1288
+ * Resolves to the spec path on disk and a `dispose` that deletes the checkout.
1289
+ */
1290
+ async function checkoutGitInput(input, signal) {
1291
+ const { repo, ref, path } = parseGitInput(input);
1292
+ const dir = await mkdtemp(join(tmpdir(), "kubb-git-"));
1293
+ const dispose = () => rm(dir, {
1294
+ recursive: true,
1295
+ force: true
1296
+ });
1297
+ const specPath = resolve(dir, path);
1298
+ const relativePath = relative(dir, specPath);
1299
+ if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
1300
+ await dispose();
1301
+ throw gitError(`The spec path "${path}" in the git \`input\` points outside the repository.`);
1302
+ }
1303
+ const label = `${redact(repo)}${ref ? ` at ${ref}` : ""}`;
1304
+ const env = {
1305
+ ...process$1.env,
1306
+ GIT_TERMINAL_PROMPT: "0"
1307
+ };
1308
+ const steps = [
1309
+ ["init", "--quiet"],
1310
+ [
1311
+ "config",
1312
+ "core.symlinks",
1313
+ "false"
1314
+ ],
1315
+ [
1316
+ "fetch",
1317
+ "--quiet",
1318
+ "--depth",
1319
+ "1",
1320
+ "--end-of-options",
1321
+ repo,
1322
+ ref ?? "HEAD"
1323
+ ],
1324
+ [
1325
+ "checkout",
1326
+ "--quiet",
1327
+ "FETCH_HEAD"
1328
+ ]
1329
+ ];
1330
+ try {
1331
+ for (const args of steps) {
1332
+ const result = await x("git", args, {
1333
+ nodeOptions: {
1334
+ cwd: dir,
1335
+ env,
1336
+ signal
1337
+ },
1338
+ throwOnError: false
1339
+ });
1340
+ if (result.exitCode !== 0) {
1341
+ const stderr = result.stderr.replaceAll(repo, redact(repo)).trim();
1342
+ throw gitError(`\`git ${args[0]}\` failed for ${label}: ${stderr || `exit code ${result.exitCode}`}`);
1343
+ }
1344
+ }
1345
+ } catch (error) {
1346
+ await dispose();
1347
+ if (Diagnostics.isError(error)) throw error;
1348
+ throw gitError(`Cannot run \`git\` for ${label}: ${getErrorMessage(error)}`, error);
1349
+ }
1350
+ return {
1351
+ path: specPath,
1352
+ dispose
1353
+ };
1354
+ }
1355
+ //#endregion
1237
1356
  //#region src/input.ts
1238
1357
  /**
1239
1358
  * Classifies an `input` value so callers branch on it once instead of repeating the checks.
1240
1359
  *
1241
1360
  * A non-string is a parsed spec (`object`). A string is `inline` when it holds OpenAPI content,
1242
1361
  * meaning it starts with `{` or `[`, spans multiple lines, or opens with a YAML `openapi:` or
1243
- * `swagger:` key. Otherwise a string is a `url` when it parses as one, or a `file` path.
1362
+ * `swagger:` key. Otherwise a string is `git` when it starts with `git+`, a `url` when it parses as
1363
+ * one, or a `file` path.
1244
1364
  */
1245
1365
  function getInputKind(input) {
1246
1366
  if (typeof input !== "string") return "object";
1247
1367
  const trimmed = input.trimStart();
1248
1368
  if (trimmed.startsWith("{") || trimmed.startsWith("[") || input.includes("\n") || /^(openapi|swagger)\s*:/i.test(trimmed)) return "inline";
1369
+ if (gitInputRegExp.test(input)) return "git";
1249
1370
  if (URL.canParse(input)) return "url";
1250
1371
  return "file";
1251
1372
  }
@@ -1269,8 +1390,9 @@ function isLegacyInput(input) {
1269
1390
  /**
1270
1391
  * Normalizes `config.input` into an `AdapterSource` the adapter can parse.
1271
1392
  *
1272
- * A parsed object and inline content become `{ type: 'data' }`; a URL is kept verbatim and a
1273
- * local path is resolved against `config.root`, both as `{ type: 'path' }`.
1393
+ * A parsed object and inline content become `{ type: 'data' }`; a URL or git input is kept
1394
+ * verbatim and a local path is resolved against `config.root`, all as `{ type: 'path' }`. The
1395
+ * driver checks out a git input before the adapter parses it.
1274
1396
  */
1275
1397
  function inputToAdapterSource(config) {
1276
1398
  const input = config.input;
@@ -1297,7 +1419,7 @@ function inputToAdapterSource(config) {
1297
1419
  type: "data",
1298
1420
  data: input
1299
1421
  };
1300
- if (kind === "url") return {
1422
+ if (kind === "url" || kind === "git") return {
1301
1423
  type: "path",
1302
1424
  path: input
1303
1425
  };
@@ -2231,13 +2353,20 @@ var KubbDriver = class {
2231
2353
  async #parseInput() {
2232
2354
  if (this.inputNode || !this.adapter || !this.#adapterSource) return;
2233
2355
  this.options.signal?.throwIfAborted();
2356
+ const source = this.#adapterSource;
2357
+ const checkout = source.type === "path" && gitInputRegExp.test(source.path) ? await checkoutGitInput(source.path, this.options.signal) : void 0;
2234
2358
  try {
2235
- this.inputNode = await this.adapter.parse(this.#adapterSource, { signal: this.options.signal });
2359
+ this.inputNode = await this.adapter.parse(checkout ? {
2360
+ type: "path",
2361
+ path: checkout.path
2362
+ } : source, { signal: this.options.signal });
2236
2363
  } catch (caughtError) {
2237
2364
  throw new Diagnostics.Error(Diagnostics.fromExtension(caughtError, {
2238
2365
  kind: "adapter",
2239
2366
  extension: this.adapter
2240
2367
  }));
2368
+ } finally {
2369
+ await checkout?.dispose();
2241
2370
  }
2242
2371
  }
2243
2372
  /**
@@ -2856,6 +2985,6 @@ var Hookable = class {
2856
2985
  }
2857
2986
  };
2858
2987
  //#endregion
2859
- export { inParallel as A, __name as B, useOptions as C, Diagnostics as D, resolveMeta as E, rm as F, write as I, writeRaw as L, isPathInside as M, listFiles as N, formatMs as O, read as P, toError as R, useKubb as S, defineExtension as T, setMacros as _, FileManager as a, tryUseKubb as b, Resolver as c, definePlugin as d, addGenerator as f, requirePlugin as g, kubbContext as h, createNodeCache as i, existsSync$1 as j, getElapsedMs as k, getInputKind as l, injectFile as m, KubbDriver as n, _usingCtx as o, addMacro as p, pluginNotFoundError as r, createResolver as s, Hookable as t, assertPluginOption as u, setOptions as v, checkCompatibility as w, useConfig as x, setResolver as y, camelCase as z };
2988
+ export { getElapsedMs as A, camelCase as B, useKubb as C, resolveMeta as D, defineExtension as E, read as F, rm$1 as I, write as L, existsSync$1 as M, isPathInside as N, Diagnostics as O, listFiles as P, writeRaw as R, useConfig as S, checkCompatibility as T, __name as V, requirePlugin as _, FileManager as a, setResolver as b, Resolver as c, assertPluginOption as d, definePlugin as f, kubbContext as g, injectFile as h, createNodeCache as i, inParallel as j, formatMs as k, getInputKind as l, addMacro as m, KubbDriver as n, _usingCtx as o, addGenerator as p, pluginNotFoundError as r, createResolver as s, Hookable as t, checkoutGitInput as u, setMacros as v, useOptions as w, tryUseKubb as x, setOptions as y, toError as z };
2860
2989
 
2861
- //# sourceMappingURL=Hookable-CvStaYYq.js.map
2990
+ //# sourceMappingURL=Hookable-Cqx0g2T-.js.map