@kubb/studio 5.6.0 → 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/README.md +80 -169
- package/dist/index.cjs +1452 -964
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +102 -406
- package/dist/index.js +1427 -947
- package/dist/index.js.map +1 -1
- package/dist/protocol-P0znFp9w.d.ts +626 -0
- package/dist/protocol.cjs +13 -13
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +2 -628
- package/dist/protocol.js +12 -14
- package/dist/protocol.js.map +1 -1
- package/package.json +7 -12
- package/dist/configFile-dJtYeqX1.cjs +0 -567
- package/dist/configFile-dJtYeqX1.cjs.map +0 -1
- package/dist/configFile-zhp5DQIe.js +0 -566
- package/dist/configFile-zhp5DQIe.js.map +0 -1
- package/dist/resolveConfig-CjF6j-3y.cjs +0 -399
- package/dist/resolveConfig-CjF6j-3y.cjs.map +0 -1
- package/dist/resolveConfig-DOUv_Tid.js +0 -352
- package/dist/resolveConfig-DOUv_Tid.js.map +0 -1
- package/dist/rolldown-runtime-qbf5tadS.cjs +0 -38
- package/dist/snapshotPackage-BFjiRhGd.cjs +0 -177
- package/dist/snapshotPackage-BFjiRhGd.cjs.map +0 -1
- package/dist/snapshotPackage-BwZ8IJX-.js +0 -176
- package/dist/snapshotPackage-BwZ8IJX-.js.map +0 -1
- package/dist/url-DC87bYiU.cjs +0 -19
- package/dist/url-DC87bYiU.cjs.map +0 -1
- package/dist/url-DhFfJpxp.js +0 -14
- package/dist/url-DhFfJpxp.js.map +0 -1
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
-
import { existsSync } from "node:fs";
|
|
6
|
-
import { readFile } from "node:fs/promises";
|
|
7
|
-
//#region ../../internals/utils/src/casing.ts
|
|
8
|
-
/**
|
|
9
|
-
* Shared implementation for camelCase and PascalCase conversion.
|
|
10
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
11
|
-
* and capitalizes each word according to `pascal`.
|
|
12
|
-
*
|
|
13
|
-
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
14
|
-
*/
|
|
15
|
-
function toCamelOrPascal(text, pascal) {
|
|
16
|
-
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
17
|
-
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
18
|
-
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
19
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Converts `text` to camelCase.
|
|
23
|
-
*
|
|
24
|
-
* @example Word boundaries
|
|
25
|
-
* `camelCase('hello-world') // 'helloWorld'`
|
|
26
|
-
*
|
|
27
|
-
* @example With a prefix
|
|
28
|
-
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
29
|
-
*/
|
|
30
|
-
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
31
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
32
|
-
}
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region ../../internals/utils/src/runtime.ts
|
|
35
|
-
/**
|
|
36
|
-
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
37
|
-
*
|
|
38
|
-
* Prefer the shared {@link runtime} instance over constructing your own.
|
|
39
|
-
*/
|
|
40
|
-
var Runtime = class {
|
|
41
|
-
/**
|
|
42
|
-
* `true` when the current process is running under Bun.
|
|
43
|
-
*
|
|
44
|
-
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
45
|
-
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
46
|
-
* otherwise look like Node.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```ts
|
|
50
|
-
* if (runtime.isBun) {
|
|
51
|
-
* await Bun.write(path, data)
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
get isBun() {
|
|
56
|
-
return typeof Bun !== "undefined";
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* `true` when the current process is running under Deno.
|
|
60
|
-
*/
|
|
61
|
-
get isDeno() {
|
|
62
|
-
return typeof globalThis.Deno !== "undefined";
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* `true` when the current process is running under Node.
|
|
66
|
-
*
|
|
67
|
-
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
68
|
-
*/
|
|
69
|
-
get isNode() {
|
|
70
|
-
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Name of the runtime executing the current process.
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```ts
|
|
77
|
-
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
78
|
-
* ```
|
|
79
|
-
*/
|
|
80
|
-
get name() {
|
|
81
|
-
if (this.isBun) return "bun";
|
|
82
|
-
if (this.isDeno) return "deno";
|
|
83
|
-
return "node";
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Version of the active runtime, or an empty string when it cannot be read.
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
get version() {
|
|
94
|
-
if (this.isBun) return process.versions.bun ?? "";
|
|
95
|
-
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
96
|
-
return process.versions?.node ?? "";
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
/**
|
|
100
|
-
* Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.
|
|
101
|
-
*/
|
|
102
|
-
const runtime = new Runtime();
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region ../../internals/utils/src/fs.ts
|
|
105
|
-
/**
|
|
106
|
-
* Reads the file at `path` as a UTF-8 string.
|
|
107
|
-
* Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* const source = await read('./src/Pet.ts')
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
async function read(path) {
|
|
115
|
-
if (runtime.isBun) return Bun.file(path).text();
|
|
116
|
-
return readFile(path, { encoding: "utf8" });
|
|
117
|
-
}
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/operations/resolveConfig.ts
|
|
120
|
-
/**
|
|
121
|
-
* Imports a package, falling back to how the user's project would resolve it.
|
|
122
|
-
*
|
|
123
|
-
* `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,
|
|
124
|
-
* `npm i -g`) only sees its own `node_modules` and misses the plugins installed next to the user's
|
|
125
|
-
* config. The retry resolves from `process.cwd()` instead.
|
|
126
|
-
*/
|
|
127
|
-
async function importFromProject(packageName) {
|
|
128
|
-
try {
|
|
129
|
-
return await import(packageName);
|
|
130
|
-
} catch {
|
|
131
|
-
const resolved = createRequire(pathToFileURL(`${process.cwd()}/`)).resolve(packageName);
|
|
132
|
-
const esm = resolved.replace(/\.cjs$/, ".js");
|
|
133
|
-
return await import(pathToFileURL(esm !== resolved && existsSync(esm) ? esm : resolved).href);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Strips the `@kubb/` scope from a plugin package name, matching the `name` convention Kubb
|
|
138
|
-
* plugin factories use internally.
|
|
139
|
-
*
|
|
140
|
-
* @example
|
|
141
|
-
* ```ts
|
|
142
|
-
* toPluginName('@kubb/plugin-ts') // 'plugin-ts'
|
|
143
|
-
* ```
|
|
144
|
-
*/
|
|
145
|
-
function toPluginName(packageName) {
|
|
146
|
-
return packageName.split("/").pop() ?? packageName;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Adds the `@kubb/` scope a plugin's package carries but its `name` does not, the inverse of
|
|
150
|
-
* {@link toPluginName}. A name outside the `plugin-` convention is left alone, so a third-party
|
|
151
|
-
* plugin is not reported as one of Kubb's.
|
|
152
|
-
*
|
|
153
|
-
* @example
|
|
154
|
-
* ```ts
|
|
155
|
-
* toPackageName('plugin-ts') // '@kubb/plugin-ts'
|
|
156
|
-
* toPackageName('@acme/my-plugin') // '@acme/my-plugin'
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
function toPackageName(name) {
|
|
160
|
-
return name.startsWith("plugin-") ? `@kubb/${name}` : name;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Derives the conventional named export for a `@kubb/*` plugin package from its package name.
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* ```ts
|
|
167
|
-
* toExportName('@kubb/plugin-react-query') // 'pluginReactQuery'
|
|
168
|
-
* toExportName('@kubb/plugin-ts') // 'pluginTs'
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
function toExportName(packageName) {
|
|
172
|
-
return camelCase(toPluginName(packageName));
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* A `@kubb/plugin-*` package specifier. Nothing else may reach `import()`: only Kubb's own
|
|
176
|
-
* plugins are supported, so a payload naming anything else, a third-party package or a path, is
|
|
177
|
-
* refused before it can execute.
|
|
178
|
-
*/
|
|
179
|
-
const KUBB_PLUGIN_SPECIFIER = /^@kubb\/plugin-[\w.-]+$/;
|
|
180
|
-
/**
|
|
181
|
-
* Whether `name` is a `@kubb/plugin-*` specifier. Exported so `configFile.ts` can refuse the same
|
|
182
|
-
* shape before printing a Studio-supplied plugin name into the config file's source text.
|
|
183
|
-
*/
|
|
184
|
-
function isKubbPluginSpecifier(name) {
|
|
185
|
-
return KUBB_PLUGIN_SPECIFIER.test(name);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Dynamically imports a `@kubb/plugin-*` package and returns its factory function.
|
|
189
|
-
*
|
|
190
|
-
* Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`
|
|
191
|
-
* build ARG, no runtime installation is possible in the distroless container.
|
|
192
|
-
*
|
|
193
|
-
* Resolution order: the camelCase named export the package name implies (e.g. `pluginTs`), then
|
|
194
|
-
* the default export.
|
|
195
|
-
*
|
|
196
|
-
* @throws if the package cannot be imported or exports no callable factory.
|
|
197
|
-
*/
|
|
198
|
-
async function loadPluginFactory(packageName) {
|
|
199
|
-
if (!isKubbPluginSpecifier(packageName)) throw new Error(`Plugin "${packageName}" is not a @kubb/plugin-* package. Kubb Studio only supports Kubb's own plugins.`);
|
|
200
|
-
let mod;
|
|
201
|
-
try {
|
|
202
|
-
mod = await importFromProject(packageName);
|
|
203
|
-
} catch (cause) {
|
|
204
|
-
throw new Error(`Plugin "${packageName}" could not be loaded. Make sure it is installed: \`npm install ${packageName}\``, { cause });
|
|
205
|
-
}
|
|
206
|
-
const exportName = toExportName(packageName);
|
|
207
|
-
if (typeof mod[exportName] === "function") return mod[exportName];
|
|
208
|
-
if (typeof mod["default"] === "function") return mod["default"];
|
|
209
|
-
throw new Error(`Plugin "${packageName}" does not export a callable factory. Tried the named export "${exportName}" and "default".`);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Resolves each plugin entry by dynamically importing the `@kubb/plugin-*` package and
|
|
213
|
-
* calling its factory with the provided options.
|
|
214
|
-
*
|
|
215
|
-
* Packages must be pre-installed in the Docker image at build time, use the `KUBB_PACKAGES`
|
|
216
|
-
* build ARG to control which ones are available at runtime.
|
|
217
|
-
*
|
|
218
|
-
* @example
|
|
219
|
-
* ```ts
|
|
220
|
-
* { name: '@kubb/plugin-react-query', options: { output: { path: './hooks' } } }
|
|
221
|
-
* { name: '@kubb/plugin-ts', options: { output: { path: './types' } } }
|
|
222
|
-
* ```
|
|
223
|
-
*/
|
|
224
|
-
async function resolvePlugins(plugins) {
|
|
225
|
-
return Promise.all(plugins.map(async ({ name, options }) => {
|
|
226
|
-
return (await loadPluginFactory(name))(options ?? {});
|
|
227
|
-
}));
|
|
228
|
-
}
|
|
229
|
-
/** The installed version of each plugin's package, plus the names that could not be resolved. */
|
|
230
|
-
async function resolvePeerDependencies(names) {
|
|
231
|
-
const require = createRequire(import.meta.url);
|
|
232
|
-
const uniqueNames = [...new Set(names.map(toPackageName))];
|
|
233
|
-
const peerDependencies = {};
|
|
234
|
-
const missingDependencies = [];
|
|
235
|
-
const versions = await Promise.all(uniqueNames.map(async (name) => {
|
|
236
|
-
try {
|
|
237
|
-
const path = require.resolve(`${name}/package.json`);
|
|
238
|
-
return JSON.parse(await readFile(path, "utf8")).version;
|
|
239
|
-
} catch {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
}));
|
|
243
|
-
for (const [index, name] of uniqueNames.entries()) {
|
|
244
|
-
const version = versions[index];
|
|
245
|
-
if (version) {
|
|
246
|
-
peerDependencies[name] = version;
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
missingDependencies.push(name);
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
peerDependencies,
|
|
253
|
-
missingDependencies
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Whether `value` is an object literal or a null-prototype object, not an array or class instance.
|
|
258
|
-
*/
|
|
259
|
-
function isPlainObject(value) {
|
|
260
|
-
if (typeof value !== "object" || value === null) return false;
|
|
261
|
-
const prototype = Object.getPrototypeOf(value);
|
|
262
|
-
return prototype === null || prototype === Object.prototype;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Whether `value` survives a JSON round trip, so it can be printed into a config file as a literal.
|
|
266
|
-
* A function, `undefined`, a non-finite number or a class instance comes back from Studio as a
|
|
267
|
-
* lossy copy, so an edit carrying one is refused rather than written into the user's source.
|
|
268
|
-
*/
|
|
269
|
-
function isOptionValue(value) {
|
|
270
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
271
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
272
|
-
if (Array.isArray(value)) return value.every(isOptionValue);
|
|
273
|
-
if (isPlainObject(value)) return Object.values(value).every(isOptionValue);
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Deep-merges Studio's options over the disk options, with Studio taking priority, except where the
|
|
278
|
-
* disk value cannot survive JSON. Studio only ever saw a lossy copy of such a value, so the disk
|
|
279
|
-
* value is kept rather than replaced by what Studio echoes back. Plain objects merge key by key, so
|
|
280
|
-
* Studio can still change a literal that sits next to a function (e.g. `group.type` beside `group.name`).
|
|
281
|
-
*
|
|
282
|
-
* @example
|
|
283
|
-
* ```ts
|
|
284
|
-
* mergeOptions({ macros: [camelCaseMacro], syntaxType: 'type' }, { macros: [{ name: 'camelCase' }], syntaxType: 'interface' })
|
|
285
|
-
* // { macros: [camelCaseMacro], syntaxType: 'interface' }
|
|
286
|
-
* ```
|
|
287
|
-
*/
|
|
288
|
-
function mergeOptions(disk, studio) {
|
|
289
|
-
const merged = { ...disk };
|
|
290
|
-
for (const [key, value] of Object.entries(studio)) {
|
|
291
|
-
const current = disk[key];
|
|
292
|
-
if (isPlainObject(current) && isPlainObject(value)) {
|
|
293
|
-
merged[key] = mergeOptions(current, value);
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
if (current !== void 0 && !isOptionValue(current)) continue;
|
|
297
|
-
merged[key] = value;
|
|
298
|
-
}
|
|
299
|
-
return merged;
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Merges studio plugin options with disk config plugins.
|
|
303
|
-
* Studio takes priority: options from studio win over disk (except values only the disk config
|
|
304
|
-
* can express, see {@link mergeOptions}), and a plugin Studio explicitly
|
|
305
|
-
* disabled is dropped even when the disk config still lists it. Disk plugins without a studio
|
|
306
|
-
* counterpart are kept as-is. Studio plugins not present on disk are appended.
|
|
307
|
-
*
|
|
308
|
-
* For plugins present in both configs, the plugin is re-instantiated with merged options
|
|
309
|
-
* so that all internal closures correctly reference the merged values.
|
|
310
|
-
*/
|
|
311
|
-
async function mergePlugins(diskPlugins, studioPlugins) {
|
|
312
|
-
const disabledNames = new Set((studioPlugins ?? []).filter((entry) => entry.disabled).map((entry) => toPluginName(entry.name)));
|
|
313
|
-
const activeDiskPlugins = disabledNames.size ? diskPlugins?.filter((plugin) => !disabledNames.has(plugin.name)) : diskPlugins;
|
|
314
|
-
const activeStudioPlugins = studioPlugins?.filter((entry) => !entry.disabled);
|
|
315
|
-
if (!activeDiskPlugins && !activeStudioPlugins?.length) return void 0;
|
|
316
|
-
if (!activeStudioPlugins?.length) return activeDiskPlugins;
|
|
317
|
-
if (!activeDiskPlugins) return resolvePlugins(activeStudioPlugins);
|
|
318
|
-
const studioEntryByName = new Map(activeStudioPlugins.map((entry) => [toPluginName(entry.name), entry]));
|
|
319
|
-
const diskNames = new Set(activeDiskPlugins.map((plugin) => plugin.name));
|
|
320
|
-
const merged = await Promise.all(activeDiskPlugins.map(async (diskPlugin) => {
|
|
321
|
-
const studioEntry = studioEntryByName.get(diskPlugin.name);
|
|
322
|
-
if (!studioEntry) return diskPlugin;
|
|
323
|
-
const options = mergeOptions(diskPlugin.options ?? {}, studioEntry.options ?? {});
|
|
324
|
-
const [resolved] = await resolvePlugins([{
|
|
325
|
-
name: studioEntry.name,
|
|
326
|
-
options
|
|
327
|
-
}]);
|
|
328
|
-
return resolved ?? diskPlugin;
|
|
329
|
-
}));
|
|
330
|
-
const studioOnly = activeStudioPlugins.filter((entry) => !diskNames.has(toPluginName(entry.name)));
|
|
331
|
-
return [...merged, ...await resolvePlugins(studioOnly)];
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Merges Studio-provided adapter option overrides into the disk config's adapter.
|
|
335
|
-
*
|
|
336
|
-
* Adapter instances carry live functions (`parse`, `getImports`, ...) that can't survive
|
|
337
|
-
* JSON serialization over the WebSocket, so `studioOptions` is treated as an options patch
|
|
338
|
-
* rather than a replacement adapter. Re-invokes the same `@kubb/adapter-<name>` factory the
|
|
339
|
-
* disk config used, with the merged options, so the resulting instance has fresh closures
|
|
340
|
-
* over the merged values instead of a plain object missing `parse`.
|
|
341
|
-
*/
|
|
342
|
-
async function mergeAdapter(diskAdapter, studioOptions) {
|
|
343
|
-
if (!studioOptions || !diskAdapter) return diskAdapter;
|
|
344
|
-
const packageName = `@kubb/adapter-${diskAdapter.name}`;
|
|
345
|
-
const factory = (await importFromProject(packageName))[toExportName(packageName)];
|
|
346
|
-
if (typeof factory !== "function") return diskAdapter;
|
|
347
|
-
return factory(mergeOptions(diskAdapter.options ?? {}, studioOptions));
|
|
348
|
-
}
|
|
349
|
-
//#endregion
|
|
350
|
-
export { resolvePeerDependencies as a, read as c, mergePlugins as i, isOptionValue as n, toExportName as o, mergeAdapter as r, toPackageName as s, isKubbPluginSpecifier as t };
|
|
351
|
-
|
|
352
|
-
//# sourceMappingURL=resolveConfig-DOUv_Tid.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolveConfig-DOUv_Tid.js","names":[],"sources":["../../../internals/utils/src/casing.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/fs.ts","../src/operations/resolveConfig.ts"],"sourcesContent":["type Options = {\n /**\n * Text prepended before casing is applied.\n */\n prefix?: string\n /**\n * Text appended before casing is applied.\n */\n suffix?: string\n}\n\n/**\n * Shared implementation for camelCase and PascalCase conversion.\n * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)\n * and capitalizes each word according to `pascal`.\n *\n * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.\n */\nfunction toCamelOrPascal(text: string, pascal: boolean): string {\n return text\n .trim()\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/(\\d)([a-z])/g, '$1 $2')\n .split(/[\\s\\-_./\\\\:]+/)\n .filter(Boolean)\n .map((word, i) => {\n if (word.length > 1 && word === word.toUpperCase()) return word\n const head = i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()\n return head + word.slice(1)\n })\n .join('')\n .replace(/[^a-zA-Z0-9]/g, '')\n}\n\n/**\n * Converts `text` to camelCase.\n *\n * @example Word boundaries\n * `camelCase('hello-world') // 'helloWorld'`\n *\n * @example With a prefix\n * `camelCase('tag', { prefix: 'create' }) // 'createTag'`\n */\nexport function camelCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false)\n}\n\n/**\n * Converts `text` to PascalCase.\n *\n * @example Word boundaries\n * `pascalCase('hello-world') // 'HelloWorld'`\n *\n * @example With a suffix\n * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`\n */\nexport function pascalCase(text: string, { prefix = '', suffix = '' }: Options = {}): string {\n return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true)\n}\n","/**\n * Name of the JavaScript runtime executing the current process.\n */\ntype RuntimeName = 'bun' | 'deno' | 'node'\n\n/**\n * Detects the JavaScript runtime executing the current process and exposes its name and version.\n *\n * Prefer the shared {@link runtime} instance over constructing your own.\n */\nclass Runtime {\n /**\n * `true` when the current process is running under Bun.\n *\n * Detection keys off the global `Bun` object rather than `process.versions`,\n * because Bun polyfills `process.versions.node` for Node compatibility and would\n * otherwise look like Node.\n *\n * @example\n * ```ts\n * if (runtime.isBun) {\n * await Bun.write(path, data)\n * }\n * ```\n */\n get isBun(): boolean {\n return typeof Bun !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Deno.\n */\n get isDeno(): boolean {\n return typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined'\n }\n\n /**\n * `true` when the current process is running under Node.\n *\n * Bun and Deno are excluded first so a polyfilled `process` does not register as Node.\n */\n get isNode(): boolean {\n return !this.isBun && !this.isDeno && typeof process !== 'undefined' && process.versions?.node != null\n }\n\n /**\n * Name of the runtime executing the current process.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\n get name(): RuntimeName {\n if (this.isBun) return 'bun'\n if (this.isDeno) return 'deno'\n\n return 'node'\n }\n\n /**\n * Version of the active runtime, or an empty string when it cannot be read.\n *\n * @example\n * ```ts\n * runtime.version // '1.3.11' under Bun, '22.22.2' under Node\n * ```\n */\n get version(): string {\n if (this.isBun) return process.versions.bun ?? ''\n if (this.isDeno) return (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? ''\n\n return process.versions?.node ?? ''\n }\n}\n\n/**\n * Shared {@link Runtime} instance describing the JavaScript runtime executing the current process.\n */\nexport const runtime = new Runtime()\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { existsSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { pathToFileURL } from 'node:url'\nimport type { Adapter, Plugin } from '@kubb/core'\nimport { camelCase } from '@internals/utils'\nimport type { JSONKubbConfig, OptionValue } from '../protocol/index.ts'\n\n/**\n * Turns the JSON config Studio sends back into live Kubb objects.\n *\n * A plugin or adapter instance carries closures (`parse`, `getImports`, ...) that cannot survive\n * JSON, so both sides pass options over the wire and the factory is re-invoked here with the merged\n * result. Only `@kubb/plugin-*` packages are resolved this way, so the reinstantiated factory is\n * always one Kubb ships, never an arbitrary module the payload names.\n */\n\ntype PluginFactory = (options: unknown) => Plugin\n\n/**\n * Imports a package, falling back to how the user's project would resolve it.\n *\n * `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,\n * `npm i -g`) only sees its own `node_modules` and misses the plugins installed next to the user's\n * config. The retry resolves from `process.cwd()` instead.\n */\nasync function importFromProject(packageName: string): Promise<Record<string, unknown>> {\n try {\n return await import(packageName)\n } catch {\n const require = createRequire(pathToFileURL(`${process.cwd()}/`))\n // `require.resolve` picks the package's `require` condition, so prefer the ESM build sitting\n // next to it. Loading the CJS copy would pull in a second `@kubb/core` instance.\n const resolved = require.resolve(packageName)\n const esm = resolved.replace(/\\.cjs$/, '.js')\n\n return await import(pathToFileURL(esm !== resolved && existsSync(esm) ? esm : resolved).href)\n }\n}\n\n/**\n * Strips the `@kubb/` scope from a plugin package name, matching the `name` convention Kubb\n * plugin factories use internally.\n *\n * @example\n * ```ts\n * toPluginName('@kubb/plugin-ts') // 'plugin-ts'\n * ```\n */\nfunction toPluginName(packageName: string): string {\n return packageName.split('/').pop() ?? packageName\n}\n\n/**\n * Adds the `@kubb/` scope a plugin's package carries but its `name` does not, the inverse of\n * {@link toPluginName}. A name outside the `plugin-` convention is left alone, so a third-party\n * plugin is not reported as one of Kubb's.\n *\n * @example\n * ```ts\n * toPackageName('plugin-ts') // '@kubb/plugin-ts'\n * toPackageName('@acme/my-plugin') // '@acme/my-plugin'\n * ```\n */\nexport function toPackageName(name: string): string {\n return name.startsWith('plugin-') ? `@kubb/${name}` : name\n}\n\n/**\n * Derives the conventional named export for a `@kubb/*` plugin package from its package name.\n *\n * @example\n * ```ts\n * toExportName('@kubb/plugin-react-query') // 'pluginReactQuery'\n * toExportName('@kubb/plugin-ts') // 'pluginTs'\n * ```\n */\nexport function toExportName(packageName: string): string {\n return camelCase(toPluginName(packageName))\n}\n\n/**\n * A `@kubb/plugin-*` package specifier. Nothing else may reach `import()`: only Kubb's own\n * plugins are supported, so a payload naming anything else, a third-party package or a path, is\n * refused before it can execute.\n */\nconst KUBB_PLUGIN_SPECIFIER = /^@kubb\\/plugin-[\\w.-]+$/\n\n/**\n * Whether `name` is a `@kubb/plugin-*` specifier. Exported so `configFile.ts` can refuse the same\n * shape before printing a Studio-supplied plugin name into the config file's source text.\n */\nexport function isKubbPluginSpecifier(name: string): boolean {\n return KUBB_PLUGIN_SPECIFIER.test(name)\n}\n\n/**\n * Dynamically imports a `@kubb/plugin-*` package and returns its factory function.\n *\n * Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`\n * build ARG, no runtime installation is possible in the distroless container.\n *\n * Resolution order: the camelCase named export the package name implies (e.g. `pluginTs`), then\n * the default export.\n *\n * @throws if the package cannot be imported or exports no callable factory.\n */\nasync function loadPluginFactory(packageName: string): Promise<PluginFactory> {\n if (!isKubbPluginSpecifier(packageName)) {\n throw new Error(`Plugin \"${packageName}\" is not a @kubb/plugin-* package. Kubb Studio only supports Kubb's own plugins.`)\n }\n\n let mod: Record<string, unknown>\n try {\n mod = await importFromProject(packageName)\n } catch (cause) {\n throw new Error(`Plugin \"${packageName}\" could not be loaded. Make sure it is installed: \\`npm install ${packageName}\\``, { cause })\n }\n\n const exportName = toExportName(packageName)\n\n if (typeof mod[exportName] === 'function') return mod[exportName] as PluginFactory\n\n if (typeof mod['default'] === 'function') return mod['default'] as PluginFactory\n\n throw new Error(`Plugin \"${packageName}\" does not export a callable factory. Tried the named export \"${exportName}\" and \"default\".`)\n}\n\n/**\n * Resolves each plugin entry by dynamically importing the `@kubb/plugin-*` package and\n * calling its factory with the provided options.\n *\n * Packages must be pre-installed in the Docker image at build time, use the `KUBB_PACKAGES`\n * build ARG to control which ones are available at runtime.\n *\n * @example\n * ```ts\n * { name: '@kubb/plugin-react-query', options: { output: { path: './hooks' } } }\n * { name: '@kubb/plugin-ts', options: { output: { path: './types' } } }\n * ```\n */\nexport async function resolvePlugins(plugins: NonNullable<JSONKubbConfig['plugins']>): Promise<Array<Plugin>> {\n return Promise.all(\n plugins.map(async ({ name, options }) => {\n const factory = await loadPluginFactory(name)\n return factory(options ?? {}) as Plugin\n }),\n )\n}\n\ntype PackageJSON = {\n version?: string\n}\n\n/** The installed version of each plugin's package, plus the names that could not be resolved. */\nexport async function resolvePeerDependencies(names: Array<string>): Promise<{\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}> {\n const require = createRequire(import.meta.url)\n const uniqueNames = [...new Set(names.map(toPackageName))]\n const peerDependencies: Record<string, string> = {}\n const missingDependencies: Array<string> = []\n\n const versions = await Promise.all(\n uniqueNames.map(async (name) => {\n try {\n const path = require.resolve(`${name}/package.json`)\n const packageJSON = JSON.parse(await readFile(path, 'utf8')) as PackageJSON\n return packageJSON.version\n } catch {\n return undefined\n }\n }),\n )\n\n for (const [index, name] of uniqueNames.entries()) {\n const version = versions[index]\n if (version) {\n peerDependencies[name] = version\n continue\n }\n missingDependencies.push(name)\n }\n\n return { peerDependencies, missingDependencies }\n}\n\n/**\n * Whether `value` is an object literal or a null-prototype object, not an array or class instance.\n */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false\n const prototype = Object.getPrototypeOf(value)\n return prototype === null || prototype === Object.prototype\n}\n\n/**\n * Whether `value` survives a JSON round trip, so it can be printed into a config file as a literal.\n * A function, `undefined`, a non-finite number or a class instance comes back from Studio as a\n * lossy copy, so an edit carrying one is refused rather than written into the user's source.\n */\nexport function isOptionValue(value: unknown): value is OptionValue {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return true\n if (typeof value === 'number') return Number.isFinite(value)\n if (Array.isArray(value)) return value.every(isOptionValue)\n if (isPlainObject(value)) return Object.values(value).every(isOptionValue)\n\n return false\n}\n\n/**\n * Deep-merges Studio's options over the disk options, with Studio taking priority, except where the\n * disk value cannot survive JSON. Studio only ever saw a lossy copy of such a value, so the disk\n * value is kept rather than replaced by what Studio echoes back. Plain objects merge key by key, so\n * Studio can still change a literal that sits next to a function (e.g. `group.type` beside `group.name`).\n *\n * @example\n * ```ts\n * mergeOptions({ macros: [camelCaseMacro], syntaxType: 'type' }, { macros: [{ name: 'camelCase' }], syntaxType: 'interface' })\n * // { macros: [camelCaseMacro], syntaxType: 'interface' }\n * ```\n */\nexport function mergeOptions(disk: Record<string, unknown>, studio: Record<string, unknown>): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...disk }\n\n for (const [key, value] of Object.entries(studio)) {\n const current = disk[key]\n\n if (isPlainObject(current) && isPlainObject(value)) {\n merged[key] = mergeOptions(current, value)\n continue\n }\n if (current !== undefined && !isOptionValue(current)) continue\n\n merged[key] = value\n }\n\n return merged\n}\n\n/**\n * Merges studio plugin options with disk config plugins.\n * Studio takes priority: options from studio win over disk (except values only the disk config\n * can express, see {@link mergeOptions}), and a plugin Studio explicitly\n * disabled is dropped even when the disk config still lists it. Disk plugins without a studio\n * counterpart are kept as-is. Studio plugins not present on disk are appended.\n *\n * For plugins present in both configs, the plugin is re-instantiated with merged options\n * so that all internal closures correctly reference the merged values.\n */\nexport async function mergePlugins(\n diskPlugins: Array<Plugin> | undefined,\n studioPlugins: JSONKubbConfig['plugins'] | undefined,\n): Promise<Array<Plugin> | undefined> {\n // Matched on the package's base name rather than by instantiating first. Every Kubb plugin\n // factory returns exactly that (`@kubb/plugin-ts` → `plugin-ts`), enforced by the `satisfies` on\n // each factory's name.\n const disabledNames = new Set((studioPlugins ?? []).filter((entry) => entry.disabled).map((entry) => toPluginName(entry.name)))\n const activeDiskPlugins = disabledNames.size ? diskPlugins?.filter((plugin) => !disabledNames.has(plugin.name)) : diskPlugins\n const activeStudioPlugins = studioPlugins?.filter((entry) => !entry.disabled)\n\n if (!activeDiskPlugins && !activeStudioPlugins?.length) return undefined\n if (!activeStudioPlugins?.length) return activeDiskPlugins\n\n if (!activeDiskPlugins) return resolvePlugins(activeStudioPlugins)\n\n const studioEntryByName = new Map(activeStudioPlugins.map((entry) => [toPluginName(entry.name), entry] as const))\n const diskNames = new Set(activeDiskPlugins.map((plugin) => plugin.name))\n\n // Each plugin is instantiated once, with its final options. Resolving the whole payload first\n // just to read the names would build every overlapping plugin twice and discard the first.\n const merged = await Promise.all(\n activeDiskPlugins.map(async (diskPlugin) => {\n const studioEntry = studioEntryByName.get(diskPlugin.name)\n if (!studioEntry) return diskPlugin\n\n // Disk as base, studio overrides, then re-instantiate so the plugin's closures reference the\n // merged values. A plugin that never sets `options` (e.g. `@kubb/plugin-barrel`) leaves\n // `diskPlugin.options` undefined.\n const options = mergeOptions((diskPlugin.options as Record<string, unknown>) ?? {}, (studioEntry.options as Record<string, unknown>) ?? {})\n const [resolved] = await resolvePlugins([{ name: studioEntry.name, options }])\n\n return resolved ?? diskPlugin\n }),\n )\n\n const studioOnly = activeStudioPlugins.filter((entry) => !diskNames.has(toPluginName(entry.name)))\n\n return [...merged, ...(await resolvePlugins(studioOnly))]\n}\n\n/**\n * Merges Studio-provided adapter option overrides into the disk config's adapter.\n *\n * Adapter instances carry live functions (`parse`, `getImports`, ...) that can't survive\n * JSON serialization over the WebSocket, so `studioOptions` is treated as an options patch\n * rather than a replacement adapter. Re-invokes the same `@kubb/adapter-<name>` factory the\n * disk config used, with the merged options, so the resulting instance has fresh closures\n * over the merged values instead of a plain object missing `parse`.\n */\nexport async function mergeAdapter(diskAdapter: Adapter | undefined, studioOptions: object | undefined): Promise<Adapter | undefined> {\n if (!studioOptions || !diskAdapter) {\n return diskAdapter\n }\n\n const packageName = `@kubb/adapter-${diskAdapter.name}`\n const mod = await importFromProject(packageName)\n const factory = mod[toExportName(packageName)]\n\n if (typeof factory !== 'function') {\n return diskAdapter\n }\n\n const mergedOptions = mergeOptions((diskAdapter.options as Record<string, unknown>) ?? {}, studioOptions as Record<string, unknown>)\n\n return factory(mergedOptions) as Adapter\n}\n"],"mappings":"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"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
//#region \0rolldown/runtime.js
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __name = (target, value) => __defProp(target, "name", {
|
|
5
|
-
value,
|
|
6
|
-
configurable: true
|
|
7
|
-
});
|
|
8
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
14
|
-
key = keys[i];
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
-
get: ((k) => from[k]).bind(null, key),
|
|
17
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
23
|
-
value: mod,
|
|
24
|
-
enumerable: true
|
|
25
|
-
}) : target, mod));
|
|
26
|
-
//#endregion
|
|
27
|
-
Object.defineProperty(exports, "__name", {
|
|
28
|
-
enumerable: true,
|
|
29
|
-
get: function() {
|
|
30
|
-
return __name;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
Object.defineProperty(exports, "__toESM", {
|
|
34
|
-
enumerable: true,
|
|
35
|
-
get: function() {
|
|
36
|
-
return __toESM;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
require("./rolldown-runtime-qbf5tadS.cjs");
|
|
2
|
-
const require_url = require("./url-DC87bYiU.cjs");
|
|
3
|
-
let node_crypto = require("node:crypto");
|
|
4
|
-
let node_path = require("node:path");
|
|
5
|
-
let node_fs_promises = require("node:fs/promises");
|
|
6
|
-
let node_util = require("node:util");
|
|
7
|
-
let node_os = require("node:os");
|
|
8
|
-
let node_zlib = require("node:zlib");
|
|
9
|
-
let tsdown = require("tsdown");
|
|
10
|
-
//#region src/operations/snapshotPackage.ts
|
|
11
|
-
const gzipAsync = (0, node_util.promisify)(node_zlib.gzip);
|
|
12
|
-
const UPLOAD_TIMEOUT_MS = 12e4;
|
|
13
|
-
/**
|
|
14
|
-
* Maps a generated file's path to its place inside the tarball, stripping everything before a
|
|
15
|
-
* `src`/`dist` segment and any `..`/empty path segment so a crafted file name cannot escape the
|
|
16
|
-
* `package/` root.
|
|
17
|
-
*/
|
|
18
|
-
function packagePath(filePath) {
|
|
19
|
-
const normalized = filePath.replaceAll("\\", "/");
|
|
20
|
-
return `package/${(normalized.match(/\/(?:src|dist)\/.*$/)?.[0].slice(1) ?? normalized.replace(/^\/+/, "")).split("/").filter((part) => part && part !== "." && part !== "..").join("/")}`;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Splits a tarball entry path into the legacy 100-byte `name` field and, when the path does not
|
|
24
|
-
* fit, the 155-byte USTAR `prefix` field that extends it. Throws rather than silently truncating
|
|
25
|
-
* a path the format cannot address (max 256 bytes: 100 name + 1 separator + 155 prefix).
|
|
26
|
-
*/
|
|
27
|
-
function splitEntryPath(path) {
|
|
28
|
-
if (Buffer.byteLength(path, "utf8") <= 100) return {
|
|
29
|
-
name: path,
|
|
30
|
-
prefix: ""
|
|
31
|
-
};
|
|
32
|
-
for (let i = path.length - 1; i >= 0; i--) {
|
|
33
|
-
if (path[i] !== "/") continue;
|
|
34
|
-
const prefix = path.slice(0, i);
|
|
35
|
-
const name = path.slice(i + 1);
|
|
36
|
-
if (Buffer.byteLength(prefix, "utf8") <= 155 && Buffer.byteLength(name, "utf8") <= 100) return {
|
|
37
|
-
name,
|
|
38
|
-
prefix
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
throw new Error(`Snapshot path is too long for a tar entry: ${path}`);
|
|
42
|
-
}
|
|
43
|
-
function header(path, size) {
|
|
44
|
-
const { name, prefix } = splitEntryPath(path);
|
|
45
|
-
const value = Buffer.alloc(512);
|
|
46
|
-
value.write(name, 0, "utf8");
|
|
47
|
-
value.write("0000644\0", 100, "ascii");
|
|
48
|
-
value.write("0000000\0", 108, "ascii");
|
|
49
|
-
value.write("0000000\0", 116, "ascii");
|
|
50
|
-
value.write(`${size.toString(8).padStart(11, "0")}\0`, 124, "ascii");
|
|
51
|
-
value.write(`${Math.floor(Date.now() / 1e3).toString(8).padStart(11, "0")}\0`, 136, "ascii");
|
|
52
|
-
value.fill(32, 148, 156);
|
|
53
|
-
value.write("0", 156, "ascii");
|
|
54
|
-
value.write("ustar\0", 257, "ascii");
|
|
55
|
-
value.write("00", 263, "ascii");
|
|
56
|
-
value.write("0000000\0", 265, "ascii");
|
|
57
|
-
value.write("0000000\0", 297, "ascii");
|
|
58
|
-
value.write(prefix, 345, "utf8");
|
|
59
|
-
const checksum = [...value].reduce((sum, byte) => sum + byte, 0);
|
|
60
|
-
value.write(`${checksum.toString(8).padStart(6, "0")}\0 `, 148, "ascii");
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Packs a generation's files into a gzipped, npm-installable tarball: a `package/` root with the
|
|
65
|
-
* generated sources, a `tsdown`-built `dist/` (esm + cjs), and a `package.json` manifest.
|
|
66
|
-
*/
|
|
67
|
-
async function createSnapshotPackage(files, packageInfo) {
|
|
68
|
-
const root = await (0, node_fs_promises.mkdtemp)((0, node_path.join)((0, node_os.tmpdir)(), "kubb-snapshot-"));
|
|
69
|
-
const dist = (0, node_path.join)(root, "dist");
|
|
70
|
-
const resolvedPaths = Object.entries(files).map(([name, content]) => ({
|
|
71
|
-
name,
|
|
72
|
-
content,
|
|
73
|
-
target: packagePath(name)
|
|
74
|
-
}));
|
|
75
|
-
const targetOwners = /* @__PURE__ */ new Map();
|
|
76
|
-
for (const { name, target } of resolvedPaths) {
|
|
77
|
-
const owner = targetOwners.get(target);
|
|
78
|
-
if (owner) throw new Error(`Snapshot has two generated files that sanitize to the same path "${target}": "${owner}" and "${name}"`);
|
|
79
|
-
targetOwners.set(target, name);
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await (0, node_fs_promises.mkdir)(dist);
|
|
83
|
-
await Promise.all(resolvedPaths.map(async ({ content, target }) => {
|
|
84
|
-
const path = (0, node_path.join)(root, target.slice(8));
|
|
85
|
-
await (0, node_fs_promises.mkdir)((0, node_path.join)(path, ".."), { recursive: true });
|
|
86
|
-
await (0, node_fs_promises.writeFile)(path, content);
|
|
87
|
-
}));
|
|
88
|
-
const sourceEntries = resolvedPaths.filter(({ name }) => /\.(?:[cm]?[jt]sx?)$/.test(name)).map(({ target }) => (0, node_path.join)(root, target.slice(8)));
|
|
89
|
-
if (sourceEntries.length) await (0, tsdown.build)({
|
|
90
|
-
entry: sourceEntries,
|
|
91
|
-
outDir: dist,
|
|
92
|
-
format: ["esm", "cjs"],
|
|
93
|
-
dts: false,
|
|
94
|
-
sourcemap: false,
|
|
95
|
-
unbundle: true,
|
|
96
|
-
report: false,
|
|
97
|
-
logLevel: "silent",
|
|
98
|
-
fixedExtension: true
|
|
99
|
-
});
|
|
100
|
-
const builtEntries = await Promise.all((await Array.fromAsync((0, node_fs_promises.glob)("**/*", {
|
|
101
|
-
cwd: dist,
|
|
102
|
-
withFileTypes: true
|
|
103
|
-
}))).filter((entry) => entry.isFile()).map(async (entry) => {
|
|
104
|
-
const filePath = (0, node_path.join)(entry.parentPath, entry.name);
|
|
105
|
-
return [`package/dist/${(0, node_path.relative)(dist, filePath).split(node_path.sep).join("/")}`, await (0, node_fs_promises.readFile)(filePath, "utf8")];
|
|
106
|
-
}));
|
|
107
|
-
const builtPaths = new Set(builtEntries.map(([path]) => path));
|
|
108
|
-
const hasBarrel = builtPaths.has("package/dist/index.mjs") && builtPaths.has("package/dist/index.cjs");
|
|
109
|
-
const barrelFields = hasBarrel ? {
|
|
110
|
-
main: "./dist/index.cjs",
|
|
111
|
-
module: "./dist/index.mjs"
|
|
112
|
-
} : {};
|
|
113
|
-
const barrelExport = hasBarrel ? { ".": {
|
|
114
|
-
import: "./dist/index.mjs",
|
|
115
|
-
require: "./dist/index.cjs"
|
|
116
|
-
} } : {};
|
|
117
|
-
const entries = {
|
|
118
|
-
"package/package.json": JSON.stringify({
|
|
119
|
-
...packageInfo,
|
|
120
|
-
type: "module",
|
|
121
|
-
...barrelFields,
|
|
122
|
-
exports: {
|
|
123
|
-
...barrelExport,
|
|
124
|
-
"./*": {
|
|
125
|
-
import: "./dist/*.mjs",
|
|
126
|
-
require: "./dist/*.cjs"
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}, null, 2),
|
|
130
|
-
...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),
|
|
131
|
-
...Object.fromEntries(builtEntries)
|
|
132
|
-
};
|
|
133
|
-
const chunks = [];
|
|
134
|
-
for (const [name, content] of Object.entries(entries)) {
|
|
135
|
-
const bytes = Buffer.from(content);
|
|
136
|
-
chunks.push(header(name, bytes.length), bytes, Buffer.alloc((512 - bytes.length % 512) % 512));
|
|
137
|
-
}
|
|
138
|
-
const bytes = await gzipAsync(Buffer.concat([...chunks, Buffer.alloc(1024)]));
|
|
139
|
-
return {
|
|
140
|
-
bytes,
|
|
141
|
-
integrity: `sha512-${(0, node_crypto.createHash)("sha512").update(bytes).digest("base64")}`
|
|
142
|
-
};
|
|
143
|
-
} finally {
|
|
144
|
-
await (0, node_fs_promises.rm)(root, {
|
|
145
|
-
recursive: true,
|
|
146
|
-
force: true
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
async function uploadSnapshot({ bytes, uploadPath, studioUrl, token, shutdown }) {
|
|
151
|
-
const uploadUrl = new URL(uploadPath, studioUrl);
|
|
152
|
-
if (uploadUrl.origin !== new URL(studioUrl).origin) throw new Error("Snapshot upload path must stay on the Studio origin");
|
|
153
|
-
const timeout = AbortSignal.timeout(UPLOAD_TIMEOUT_MS);
|
|
154
|
-
const signal = shutdown ? AbortSignal.any([shutdown, timeout]) : timeout;
|
|
155
|
-
const redirect = await fetch(uploadUrl, {
|
|
156
|
-
method: "PUT",
|
|
157
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
158
|
-
redirect: "manual",
|
|
159
|
-
signal
|
|
160
|
-
});
|
|
161
|
-
const storageUrl = redirect.headers.get("location");
|
|
162
|
-
if (redirect.status !== 307 || !storageUrl) throw new Error(`Studio did not provide a storage URL (status ${redirect.status})`);
|
|
163
|
-
const storage = new URL(storageUrl);
|
|
164
|
-
if (storage.protocol !== "https:" && !require_url.isLoopbackHost(storage.hostname)) throw new Error(`Refusing snapshot upload to ${storage.origin}`);
|
|
165
|
-
const response = await fetch(storage, {
|
|
166
|
-
method: "PUT",
|
|
167
|
-
body: new Uint8Array(bytes),
|
|
168
|
-
redirect: "error",
|
|
169
|
-
signal
|
|
170
|
-
});
|
|
171
|
-
if (!response.ok) throw new Error(`Snapshot upload failed with status ${response.status}`);
|
|
172
|
-
}
|
|
173
|
-
//#endregion
|
|
174
|
-
exports.createSnapshotPackage = createSnapshotPackage;
|
|
175
|
-
exports.uploadSnapshot = uploadSnapshot;
|
|
176
|
-
|
|
177
|
-
//# sourceMappingURL=snapshotPackage-BFjiRhGd.cjs.map
|