@kubb/studio 5.6.1 → 5.6.2

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/index.cjs CHANGED
@@ -216,7 +216,7 @@ function getElapsedMs(hrStart) {
216
216
  }
217
217
  //#endregion
218
218
  //#region package.json
219
- var version = "5.6.1";
219
+ var version = "5.6.2";
220
220
  //#endregion
221
221
  //#region src/resolveConfig.ts
222
222
  /**
@@ -1058,19 +1058,21 @@ async function generate({ config, hooks, signal, allowExec }) {
1058
1058
  lint: false,
1059
1059
  postGenerate: []
1060
1060
  };
1061
- const result = await (0, _kubb_core.createKubb)({
1061
+ const kubb = (0, _kubb_core.createKubb)({
1062
1062
  ...config,
1063
1063
  output
1064
1064
  }, {
1065
1065
  hooks,
1066
1066
  signal
1067
- }).generate();
1067
+ });
1068
+ const result = await kubb.generate();
1068
1069
  if (!result.success) throw generationFailure(result.diagnostics);
1070
+ const { storage, root } = kubb.config;
1069
1071
  return {
1070
- storage: config.storage,
1071
- root: config.root,
1072
+ storage,
1073
+ root,
1072
1074
  paths: new Set(result.files.map((file) => relativeStoragePath({
1073
- root: config.root,
1075
+ root,
1074
1076
  filePath: file.path
1075
1077
  })))
1076
1078
  };
@@ -2015,29 +2017,24 @@ const gzipAsync = (0, node_util.promisify)(node_zlib.gzip);
2015
2017
  const UPLOAD_TIMEOUT_MS = 12e4;
2016
2018
  const BUNDLED_DEPENDENCIES = ["@kubb/plugin-barrel"];
2017
2019
  /**
2018
- * Maps a generated file's path to its place inside the tarball, stripping everything before a
2019
- * `src`/`dist` segment and any `..`/empty path segment so a crafted file name cannot escape the
2020
- * `package/` root.
2020
+ * Maps a generated file to its path in the package, from its `src`/`dist` segment on, without `..` parts that could escape it.
2021
2021
  */
2022
2022
  function packagePath(filePath) {
2023
2023
  const normalized = filePath.replaceAll("\\", "/");
2024
- return `package/${(normalized.match(/\/(?:src|dist)\/.*$/)?.[0].slice(1) ?? normalized.replace(/^\/+/, "")).split("/").filter((part) => part && part !== "." && part !== "..").join("/")}`;
2024
+ return (normalized.match(/\/(?:src|dist)\/.*$/)?.[0] ?? normalized).split("/").filter((part) => part && part !== "." && part !== "..").join("/");
2025
2025
  }
2026
2026
  /**
2027
- * Splits a tarball entry path into the legacy 100-byte `name` field and, when the path does not
2028
- * fit, the 155-byte USTAR `prefix` field that extends it. Throws rather than silently truncating
2029
- * a path the format cannot address (max 256 bytes: 100 name + 1 separator + 155 prefix).
2027
+ * Splits a tar path into the 100-byte USTAR `name` and the 155-byte `prefix`, and throws for a path that fits neither.
2030
2028
  */
2031
2029
  function splitEntryPath(path) {
2032
- if (Buffer.byteLength(path, "utf8") <= 100) return {
2030
+ if (Buffer.byteLength(path) <= 100) return {
2033
2031
  name: path,
2034
2032
  prefix: ""
2035
2033
  };
2036
- for (let i = path.length - 1; i >= 0; i--) {
2037
- if (path[i] !== "/") continue;
2034
+ for (let i = path.lastIndexOf("/"); i > 0; i = path.lastIndexOf("/", i - 1)) {
2038
2035
  const prefix = path.slice(0, i);
2039
2036
  const name = path.slice(i + 1);
2040
- if (Buffer.byteLength(prefix, "utf8") <= 155 && Buffer.byteLength(name, "utf8") <= 100) return {
2037
+ if (Buffer.byteLength(prefix) <= 155 && Buffer.byteLength(name) <= 100) return {
2041
2038
  name,
2042
2039
  prefix
2043
2040
  };
@@ -2047,21 +2044,23 @@ function splitEntryPath(path) {
2047
2044
  function header(path, size) {
2048
2045
  const { name, prefix } = splitEntryPath(path);
2049
2046
  const value = Buffer.alloc(512);
2050
- value.write(name, 0, "utf8");
2051
- value.write("0000644\0", 100, "ascii");
2052
- value.write("0000000\0", 108, "ascii");
2053
- value.write("0000000\0", 116, "ascii");
2054
- value.write(`${size.toString(8).padStart(11, "0")}\0`, 124, "ascii");
2055
- value.write("03560116604\0", 136, "ascii");
2047
+ value.write(name);
2048
+ value.write("0000644\0", 100);
2049
+ for (const offset of [
2050
+ 108,
2051
+ 116,
2052
+ 265,
2053
+ 297
2054
+ ]) value.write("0000000\0", offset);
2055
+ value.write(`${size.toString(8).padStart(11, "0")}\0`, 124);
2056
+ value.write("03560116604\0", 136);
2056
2057
  value.fill(32, 148, 156);
2057
- value.write("0", 156, "ascii");
2058
- value.write("ustar\0", 257, "ascii");
2059
- value.write("00", 263, "ascii");
2060
- value.write("0000000\0", 265, "ascii");
2061
- value.write("0000000\0", 297, "ascii");
2062
- value.write(prefix, 345, "utf8");
2063
- const checksum = [...value].reduce((sum, byte) => sum + byte, 0);
2064
- value.write(`${checksum.toString(8).padStart(6, "0")}\0 `, 148, "ascii");
2058
+ value.write("0", 156);
2059
+ value.write("ustar\0", 257);
2060
+ value.write("00", 263);
2061
+ value.write(prefix, 345);
2062
+ const checksum = value.reduce((sum, byte) => sum + byte, 0);
2063
+ value.write(`${checksum.toString(8).padStart(6, "0")}\0 `, 148);
2065
2064
  return value;
2066
2065
  }
2067
2066
  /**
@@ -2069,31 +2068,29 @@ function header(path, size) {
2069
2068
  * generated sources, a `tsdown`-built `dist/` (esm + cjs), and a `package.json` manifest.
2070
2069
  */
2071
2070
  async function createSnapshotPackage({ files, packageInfo }) {
2071
+ const sources = /* @__PURE__ */ new Map();
2072
+ for (const [name, content] of Object.entries(files)) {
2073
+ const path = packagePath(name);
2074
+ const owner = sources.get(path)?.name;
2075
+ if (owner) throw new Error(`Snapshot has two generated files that sanitize to the same path "package/${path}": "${owner}" and "${name}"`);
2076
+ sources.set(path, {
2077
+ name,
2078
+ content
2079
+ });
2080
+ }
2072
2081
  const root = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "kubb-snapshot-"));
2073
2082
  const dist = (0, node_path.join)(root, "dist");
2074
- const resolvedPaths = Object.entries(files).map(([name, content]) => ({
2075
- name,
2076
- content,
2077
- target: packagePath(name)
2078
- }));
2079
- const targetOwners = /* @__PURE__ */ new Map();
2080
- for (const { name, target } of resolvedPaths) {
2081
- const owner = targetOwners.get(target);
2082
- if (owner) throw new Error(`Snapshot has two generated files that sanitize to the same path "${target}": "${owner}" and "${name}"`);
2083
- targetOwners.set(target, name);
2084
- }
2085
2083
  try {
2086
2084
  await (0, node_fs_promises.mkdir)(dist);
2087
- await Promise.all(resolvedPaths.map(async ({ content, target }) => {
2088
- const path = (0, node_path.join)(root, target.slice(8));
2089
- await (0, node_fs_promises.mkdir)((0, node_path.join)(path, ".."), { recursive: true });
2090
- await (0, node_fs_promises.writeFile)(path, content);
2085
+ await Promise.all([...sources].map(async ([path, { content }]) => {
2086
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)((0, node_path.join)(root, path)), { recursive: true });
2087
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(root, path), content);
2091
2088
  }));
2092
- const sourceEntries = resolvedPaths.filter(({ name }) => /\.(?:[cm]?[jt]sx?)$/.test(name)).map(({ target }) => (0, node_path.join)(root, target.slice(8)));
2093
- if (sourceEntries.length) {
2089
+ const entry = [...sources.keys()].filter((path) => /\.(?:[cm]?[jt]sx?)$/.test(path)).map((path) => (0, node_path.join)(root, path));
2090
+ if (entry.length) {
2094
2091
  const { build } = await import("tsdown");
2095
2092
  await build({
2096
- entry: sourceEntries,
2093
+ entry,
2097
2094
  outDir: dist,
2098
2095
  format: ["esm", "cjs"],
2099
2096
  dts: false,
@@ -2104,38 +2101,38 @@ async function createSnapshotPackage({ files, packageInfo }) {
2104
2101
  fixedExtension: true
2105
2102
  });
2106
2103
  }
2107
- const builtEntries = await Promise.all((await (0, _kubb_core.fsStorage)().readKeys(dist)).map(async (key) => [`package/dist/${key}`, await (0, node_fs_promises.readFile)((0, node_path.join)(dist, key), "utf8")]));
2108
- const builtPaths = new Set(builtEntries.map(([path]) => path));
2109
- const hasBarrel = builtPaths.has("package/dist/index.mjs") && builtPaths.has("package/dist/index.cjs");
2110
- const barrelFields = hasBarrel ? {
2111
- main: "./dist/index.cjs",
2112
- module: "./dist/index.mjs"
2113
- } : {};
2114
- const barrelExport = hasBarrel ? { ".": {
2115
- import: "./dist/index.mjs",
2116
- require: "./dist/index.cjs"
2117
- } } : {};
2118
- const entries = {
2119
- "package/package.json": JSON.stringify({
2120
- ...packageInfo,
2121
- type: "module",
2122
- ...barrelFields,
2123
- exports: {
2124
- ...barrelExport,
2125
- "./*": {
2126
- import: "./dist/*.mjs",
2127
- require: "./dist/*.cjs"
2128
- }
2104
+ const built = await (0, _kubb_core.fsStorage)().readKeys(dist);
2105
+ const hasBarrel = built.includes("index.mjs") && built.includes("index.cjs");
2106
+ const manifest = {
2107
+ ...packageInfo,
2108
+ type: "module",
2109
+ ...hasBarrel ? {
2110
+ main: "./dist/index.cjs",
2111
+ module: "./dist/index.mjs"
2112
+ } : {},
2113
+ exports: {
2114
+ ...hasBarrel ? { ".": {
2115
+ import: "./dist/index.mjs",
2116
+ require: "./dist/index.cjs"
2117
+ } } : {},
2118
+ "./*": {
2119
+ import: "./dist/*.mjs",
2120
+ require: "./dist/*.cjs"
2129
2121
  }
2130
- }, null, 2),
2131
- ...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),
2132
- ...Object.fromEntries(builtEntries)
2122
+ }
2133
2123
  };
2134
- const chunks = [];
2135
- for (const [name, content] of Object.entries(entries)) {
2124
+ const chunks = [...new Map([
2125
+ ["package.json", JSON.stringify(manifest, null, 2)],
2126
+ ...[...sources].map(([path, { content }]) => [path, content]),
2127
+ ...await Promise.all(built.map(async (key) => [`dist/${key}`, await (0, node_fs_promises.readFile)((0, node_path.join)(dist, key), "utf8")]))
2128
+ ])].flatMap(([path, content]) => {
2136
2129
  const bytes = Buffer.from(content);
2137
- chunks.push(header(name, bytes.length), bytes, Buffer.alloc((512 - bytes.length % 512) % 512));
2138
- }
2130
+ return [
2131
+ header(`package/${path}`, bytes.length),
2132
+ bytes,
2133
+ Buffer.alloc((512 - bytes.length % 512) % 512)
2134
+ ];
2135
+ });
2139
2136
  return await gzipAsync(Buffer.concat([...chunks, Buffer.alloc(1024)]));
2140
2137
  } finally {
2141
2138
  await (0, node_fs_promises.rm)(root, {
@@ -2144,25 +2141,12 @@ async function createSnapshotPackage({ files, packageInfo }) {
2144
2141
  });
2145
2142
  }
2146
2143
  }
2147
- function failureMessage({ status, _data }) {
2148
- const upgrade = _data?.data?.upgradeUrl ? ` Upgrade at ${_data.data.upgradeUrl}.` : "";
2149
- return `Kubb Studio refused the snapshot (status ${status})${_data?.message ? `: ${_data.message}` : ""}.${upgrade}`;
2150
- }
2151
2144
  /**
2152
- * Generates the project, packs it as an npm tarball, and publishes it to Studio in one
2153
- * `POST /api/snapshots` request.
2145
+ * Generates the project, packs it as an npm tarball, and publishes it to Studio in one `POST /api/snapshots` request.
2154
2146
  *
2155
2147
  * @example
2156
2148
  * ```ts
2157
- * const snapshot = await createSnapshot({
2158
- * apiKey: process.env.KUBB_TOKEN!,
2159
- * config,
2160
- * id: 'gh:123:pr-4',
2161
- * name: 'acme/api#4',
2162
- * packageName: '@acme/api',
2163
- * version: '1.0.0',
2164
- * })
2165
- * console.log(snapshot.url, snapshot.changes)
2149
+ * const { url } = await createSnapshot({ apiKey: process.env.KUBB_TOKEN!, config, id: 'gh:123:pr-4', name: 'acme/api#4', packageName: '@acme/api', version: '1.0.0' })
2166
2150
  * ```
2167
2151
  */
2168
2152
  async function createSnapshot({ studioUrl = defaultStudioUrl, apiKey, config, hooks, signal, ...metadata }) {
@@ -2202,7 +2186,7 @@ async function createSnapshot({ studioUrl = defaultStudioUrl, apiKey, config, ho
2202
2186
  }));
2203
2187
  body.set("tarball", new Blob([new Uint8Array(bytes)], { type: "application/gzip" }), "snapshot.tgz");
2204
2188
  const timeout = AbortSignal.timeout(UPLOAD_TIMEOUT_MS);
2205
- const response = await ofetch.ofetch.raw(url.href, {
2189
+ const { ok, status, _data } = await ofetch.ofetch.raw(url.href, {
2206
2190
  method: "POST",
2207
2191
  headers: { "x-api-key": apiKey },
2208
2192
  body,
@@ -2210,10 +2194,12 @@ async function createSnapshot({ studioUrl = defaultStudioUrl, apiKey, config, ho
2210
2194
  signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
2211
2195
  ignoreResponseError: true
2212
2196
  });
2213
- if (!response.ok) throw new Error(failureMessage(response));
2214
- const snapshot = response._data?.snapshot;
2215
- if (!snapshot) throw new Error(`Kubb Studio returned no snapshot (status ${response.status})`);
2216
- return snapshot;
2197
+ if (!ok) {
2198
+ const upgrade = _data?.data?.upgradeUrl ? ` Upgrade at ${_data.data.upgradeUrl}.` : "";
2199
+ throw new Error(`Kubb Studio refused the snapshot (status ${status})${_data?.message ? `: ${_data.message}` : ""}.${upgrade}`);
2200
+ }
2201
+ if (!_data?.snapshot) throw new Error(`Kubb Studio returned no snapshot (status ${status})`);
2202
+ return _data.snapshot;
2217
2203
  }
2218
2204
  //#endregion
2219
2205
  exports.IncompatibleAgentError = IncompatibleAgentError;