@kubb/studio 5.9.0 → 5.9.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 +203 -207
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -9
- package/dist/index.js +203 -207
- package/dist/index.js.map +1 -1
- package/dist/{protocol-BfjNY8-8.d.ts → protocol-gXoAXvT3.d.ts} +41 -10
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __name } from "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
-
import {
|
|
2
|
+
import { L as SnapshotUploadMetadata, a as AgentConnectInfo, c as ConfigEdit, j as PublishedSnapshot, l as ConfigEditOutcome, o as AgentPermissions, w as InstalledExtensions } from "./protocol-gXoAXvT3.js";
|
|
3
3
|
import { Config, Hookable, KubbHooks } from "@kubb/core";
|
|
4
4
|
//#region src/connect.d.ts
|
|
5
5
|
/**
|
|
@@ -242,13 +242,6 @@ export declare function createSnapshot({ studioUrl, apiKey, config, hooks, signa
|
|
|
242
242
|
*/
|
|
243
243
|
export declare const defaultStudioUrl = "https://kubb.studio";
|
|
244
244
|
//#endregion
|
|
245
|
-
//#region src/extensions.d.ts
|
|
246
|
-
/**
|
|
247
|
-
* The extension packages installed in a project, keyed by package name. Only these, besides
|
|
248
|
-
* `@kubb/plugin-*` packages, may be imported on behalf of Studio.
|
|
249
|
-
*/
|
|
250
|
-
type InstalledExtensions = ReadonlyMap<string, ExtensionEntry>;
|
|
251
|
-
//#endregion
|
|
252
245
|
//#region src/configFile.d.ts
|
|
253
246
|
/**
|
|
254
247
|
* Applies `edits` to the config file at `filePath`, writing it only when something changed.
|
|
@@ -264,5 +257,5 @@ export declare function writeConfigEdits({ filePath, edits, extensions }: {
|
|
|
264
257
|
changed: boolean;
|
|
265
258
|
}>;
|
|
266
259
|
//#endregion
|
|
267
|
-
export type { ConnectAgentOptions,
|
|
260
|
+
export type { ConnectAgentOptions, StudioConnectedContext };
|
|
268
261
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -71,74 +71,34 @@ function getErrorMessage(value) {
|
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
73
|
//#region ../../internals/utils/src/runtime.ts
|
|
74
|
+
const isBun = typeof Bun !== "undefined";
|
|
75
|
+
const isDeno = "Deno" in globalThis;
|
|
74
76
|
/**
|
|
75
|
-
*
|
|
77
|
+
* The JavaScript runtime executing the current process: its name, version and whether it is Bun.
|
|
76
78
|
*
|
|
77
|
-
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
82
|
+
* ```
|
|
78
83
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*
|
|
83
|
-
* Detection keys off the global `Bun` object rather than `process.versions`,
|
|
84
|
-
* because Bun polyfills `process.versions.node` for Node compatibility and would
|
|
85
|
-
* otherwise look like Node.
|
|
86
|
-
*
|
|
87
|
-
* @example
|
|
88
|
-
* ```ts
|
|
89
|
-
* if (runtime.isBun) {
|
|
90
|
-
* await Bun.write(path, data)
|
|
91
|
-
* }
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
get isBun() {
|
|
95
|
-
return typeof Bun !== "undefined";
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* `true` when the current process is running under Deno.
|
|
99
|
-
*/
|
|
100
|
-
get isDeno() {
|
|
101
|
-
return typeof globalThis.Deno !== "undefined";
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* `true` when the current process is running under Node.
|
|
105
|
-
*
|
|
106
|
-
* Bun and Deno are excluded first so a polyfilled `process` does not register as Node.
|
|
107
|
-
*/
|
|
108
|
-
get isNode() {
|
|
109
|
-
return !this.isBun && !this.isDeno && typeof process !== "undefined" && process.versions?.node != null;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Name of the runtime executing the current process.
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```ts
|
|
116
|
-
* runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
get name() {
|
|
120
|
-
if (this.isBun) return "bun";
|
|
121
|
-
if (this.isDeno) return "deno";
|
|
122
|
-
return "node";
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Version of the active runtime, or an empty string when it cannot be read.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* runtime.version // '1.3.11' under Bun, '22.22.2' under Node
|
|
130
|
-
* ```
|
|
131
|
-
*/
|
|
132
|
-
get version() {
|
|
133
|
-
if (this.isBun) return process.versions.bun ?? "";
|
|
134
|
-
if (this.isDeno) return globalThis.Deno?.version?.deno ?? "";
|
|
135
|
-
return process.versions?.node ?? "";
|
|
136
|
-
}
|
|
84
|
+
const runtime = {
|
|
85
|
+
isBun,
|
|
86
|
+
...detect()
|
|
137
87
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
88
|
+
function detect() {
|
|
89
|
+
if (isBun) return {
|
|
90
|
+
name: "bun",
|
|
91
|
+
version: process.versions.bun ?? ""
|
|
92
|
+
};
|
|
93
|
+
if (isDeno) return {
|
|
94
|
+
name: "deno",
|
|
95
|
+
version: globalThis.Deno?.version?.deno ?? ""
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
name: "node",
|
|
99
|
+
version: process.versions?.node ?? ""
|
|
100
|
+
};
|
|
101
|
+
}
|
|
142
102
|
//#endregion
|
|
143
103
|
//#region ../../internals/utils/src/fs.ts
|
|
144
104
|
/**
|
|
@@ -192,7 +152,7 @@ function getElapsedMs(hrStart) {
|
|
|
192
152
|
}
|
|
193
153
|
//#endregion
|
|
194
154
|
//#region package.json
|
|
195
|
-
var version = "5.9.
|
|
155
|
+
var version = "5.9.2";
|
|
196
156
|
//#endregion
|
|
197
157
|
//#region src/resolveConfig.ts
|
|
198
158
|
/**
|
|
@@ -237,47 +197,19 @@ function toPluginName(packageName) {
|
|
|
237
197
|
function toPackageName(name) {
|
|
238
198
|
return name.startsWith("plugin-") ? `@kubb/${name}` : name;
|
|
239
199
|
}
|
|
240
|
-
function resolvesFrom(base, packageName) {
|
|
241
|
-
try {
|
|
242
|
-
createRequire(base).resolve(`${packageName}/package.json`);
|
|
243
|
-
return true;
|
|
244
|
-
} catch {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
200
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*/
|
|
253
|
-
function isInstalled({ packageName, root }) {
|
|
254
|
-
const project = pathToFileURL(`${root}/`);
|
|
255
|
-
if (resolvesFrom(project, packageName) || resolvesFrom(import.meta.url, packageName)) return true;
|
|
256
|
-
try {
|
|
257
|
-
return resolvesFrom(createRequire(project).resolve("kubb/package.json"), packageName);
|
|
258
|
-
} catch {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* The name Studio knows a plugin by: its `@kubb/plugin-*` package when one is installed, otherwise
|
|
264
|
-
* the plugin's own `name`, so a plugin defined in the user's project is never reported as a Kubb
|
|
265
|
-
* package that does not exist. An installed extension whose manifest `name` matches wins.
|
|
201
|
+
* The name Studio knows an extension by: the package of the installed extension whose manifest
|
|
202
|
+
* `name` matches, otherwise its own `name`, so a plugin defined in the user's project is never
|
|
203
|
+
* reported as a Kubb package that does not exist.
|
|
266
204
|
*
|
|
267
205
|
* @example
|
|
268
206
|
* ```ts
|
|
269
|
-
* toStudioName({ name: 'plugin-ts',
|
|
270
|
-
* toStudioName({ name: 'plugin-zod-operations',
|
|
207
|
+
* toStudioName({ name: 'plugin-ts', extensions }) // '@kubb/plugin-ts'
|
|
208
|
+
* toStudioName({ name: 'plugin-zod-operations', extensions }) // 'plugin-zod-operations', a local plugin
|
|
271
209
|
* ```
|
|
272
210
|
*/
|
|
273
|
-
function toStudioName({ name,
|
|
274
|
-
|
|
275
|
-
if (installed) return installed.package;
|
|
276
|
-
const packageName = toPackageName(name);
|
|
277
|
-
return packageName !== name && isInstalled({
|
|
278
|
-
packageName,
|
|
279
|
-
root
|
|
280
|
-
}) ? packageName : name;
|
|
211
|
+
function toStudioName({ name, extensions = /* @__PURE__ */ new Map(), kind = "plugin" }) {
|
|
212
|
+
return [...extensions.values()].find(({ manifest }) => manifest.kind === kind && manifest.name === name)?.package ?? name;
|
|
281
213
|
}
|
|
282
214
|
/**
|
|
283
215
|
* Derives the conventional named export for a `@kubb/*` plugin package from its package name.
|
|
@@ -356,33 +288,6 @@ async function resolvePlugins({ plugins, extensions = /* @__PURE__ */ new Map()
|
|
|
356
288
|
}))(options ?? {});
|
|
357
289
|
}));
|
|
358
290
|
}
|
|
359
|
-
/** The installed version of each plugin's package, plus the names that could not be resolved. */
|
|
360
|
-
async function resolvePeerDependencies(names) {
|
|
361
|
-
const require = createRequire(import.meta.url);
|
|
362
|
-
const uniqueNames = [...new Set(names.map(toPackageName))];
|
|
363
|
-
const peerDependencies = {};
|
|
364
|
-
const missingDependencies = [];
|
|
365
|
-
const versions = await Promise.all(uniqueNames.map(async (name) => {
|
|
366
|
-
try {
|
|
367
|
-
const path = require.resolve(`${name}/package.json`);
|
|
368
|
-
return JSON.parse(await readFile(path, "utf8")).version;
|
|
369
|
-
} catch {
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
}));
|
|
373
|
-
for (const [index, name] of uniqueNames.entries()) {
|
|
374
|
-
const version = versions[index];
|
|
375
|
-
if (version) {
|
|
376
|
-
peerDependencies[name] = version;
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
missingDependencies.push(name);
|
|
380
|
-
}
|
|
381
|
-
return {
|
|
382
|
-
peerDependencies,
|
|
383
|
-
missingDependencies
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
291
|
/**
|
|
387
292
|
* Keys that reach an object's prototype when assigned. Never read from or written into options.
|
|
388
293
|
*/
|
|
@@ -445,6 +350,7 @@ function isOptionValue(value) {
|
|
|
445
350
|
* ```ts
|
|
446
351
|
* mergeOptions({ macros: [camelCaseMacro], syntaxType: 'type' }, { macros: [{ name: 'camelCase' }], syntaxType: 'interface' })
|
|
447
352
|
* // { macros: [camelCaseMacro], syntaxType: 'interface' }
|
|
353
|
+
* mergeOptions({ emptySchemaType: 'any' }, { emptySchemaType: null }) // {}
|
|
448
354
|
* ```
|
|
449
355
|
*/
|
|
450
356
|
function mergeOptions(disk, studio) {
|
|
@@ -452,6 +358,10 @@ function mergeOptions(disk, studio) {
|
|
|
452
358
|
for (const [key, value] of Object.entries(studio)) {
|
|
453
359
|
if (!isSafeKey(key)) continue;
|
|
454
360
|
const current = disk[key];
|
|
361
|
+
if (value === null) {
|
|
362
|
+
if (current === void 0 || isOptionValue(current)) delete merged[key];
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
455
365
|
if (isPlainObject(current) && isPlainObject(value)) {
|
|
456
366
|
merged[key] = mergeOptions(current, value);
|
|
457
367
|
continue;
|
|
@@ -509,20 +419,54 @@ async function mergePlugins({ diskPlugins, studioPlugins, extensions = /* @__PUR
|
|
|
509
419
|
})];
|
|
510
420
|
}
|
|
511
421
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
422
|
+
* Re-invokes the factory behind a disk adapter or parser with Studio's options merged over its own.
|
|
423
|
+
* An instance carries live functions (`parse`, `print`, ...) that cannot survive JSON, so Studio's
|
|
424
|
+
* options are a patch, not a replacement, and the factory is found from the disk instance itself: the
|
|
425
|
+
* installed extension whose manifest names it, or the `@kubb/<kind>-<name>` package for a built-in
|
|
426
|
+
* one. Throws when no factory resolves, as `mergePlugins` does, so an override Studio sent is never
|
|
427
|
+
* dropped without a word. An extension without a manifest, such as `parserTsx`, has no options in
|
|
428
|
+
* Studio, so Studio never sends one for it.
|
|
429
|
+
*/
|
|
430
|
+
async function rebuildExtension({ disk, studioOptions, kind, extensions }) {
|
|
431
|
+
if (!studioOptions || !Object.keys(studioOptions).length) return disk;
|
|
432
|
+
const { name } = resolveMeta(disk);
|
|
433
|
+
const installed = [...extensions.values()].find(({ manifest }) => manifest.kind === kind && manifest.name === name);
|
|
434
|
+
const packageName = installed?.package ?? `@kubb/${kind}-${name}`;
|
|
435
|
+
const factory = await importFromProject(packageName).then((mod) => mod[installed?.manifest.export ?? toExportName(packageName)]).catch((cause) => {
|
|
436
|
+
throw new Error(`The ${kind} "${name}" is not an installed Kubb extension, so Studio cannot change its options. Edit it in the config file.`, { cause });
|
|
437
|
+
});
|
|
438
|
+
if (typeof factory !== "function") throw new Error(`The ${kind} "${name}" is not an installed Kubb extension, so Studio cannot change its options. Edit it in the config file.`);
|
|
439
|
+
return factory(mergeOptions(disk.options ?? {}, studioOptions));
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Merges Studio-provided adapter option overrides into the disk config's adapter, see {@link rebuildExtension}.
|
|
519
443
|
*/
|
|
520
|
-
async function mergeAdapter(diskAdapter, studioOptions) {
|
|
521
|
-
if (!
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
444
|
+
async function mergeAdapter(diskAdapter, studioOptions, extensions = /* @__PURE__ */ new Map()) {
|
|
445
|
+
if (!diskAdapter) return diskAdapter;
|
|
446
|
+
return rebuildExtension({
|
|
447
|
+
disk: diskAdapter,
|
|
448
|
+
studioOptions,
|
|
449
|
+
kind: "adapter",
|
|
450
|
+
extensions
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Merges Studio-provided parser option overrides into the disk config's parsers, matched by the name
|
|
455
|
+
* Studio knows each parser by, see {@link rebuildExtension}. Parsers Studio does not name are kept as is.
|
|
456
|
+
*/
|
|
457
|
+
async function mergeParsers({ diskParsers, studioParsers, extensions = /* @__PURE__ */ new Map() }) {
|
|
458
|
+
if (!studioParsers?.length) return diskParsers;
|
|
459
|
+
const studioOptions = new Map(studioParsers.map((entry) => [entry.name, entry.options]));
|
|
460
|
+
return Promise.all(diskParsers.map((parser) => rebuildExtension({
|
|
461
|
+
disk: parser,
|
|
462
|
+
studioOptions: studioOptions.get(toStudioName({
|
|
463
|
+
name: resolveMeta(parser).name,
|
|
464
|
+
extensions,
|
|
465
|
+
kind: "parser"
|
|
466
|
+
})),
|
|
467
|
+
kind: "parser",
|
|
468
|
+
extensions
|
|
469
|
+
})));
|
|
526
470
|
}
|
|
527
471
|
//#endregion
|
|
528
472
|
//#region src/configFile.ts
|
|
@@ -543,14 +487,21 @@ const OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
543
487
|
"disable-plugin",
|
|
544
488
|
"enable-plugin"
|
|
545
489
|
]);
|
|
490
|
+
const KINDS = /* @__PURE__ */ new Set([
|
|
491
|
+
"plugin",
|
|
492
|
+
"adapter",
|
|
493
|
+
"parser"
|
|
494
|
+
]);
|
|
546
495
|
/**
|
|
547
496
|
* Why an edit from Studio cannot be applied as sent, checked before anything reads its fields.
|
|
548
497
|
* The edit crossed the wire, so its declared type is a promise the sender may not have kept.
|
|
549
498
|
*/
|
|
550
499
|
function invalidEdit(edit) {
|
|
551
|
-
const { operation, plugin, config, path,
|
|
500
|
+
const { operation, plugin, config, path, kind } = typeof edit === "object" && edit !== null ? edit : {};
|
|
552
501
|
if (typeof operation !== "string" || !OPERATIONS.has(operation)) return `unknown operation ${JSON.stringify(operation)}`;
|
|
553
502
|
if (typeof plugin !== "string" || !plugin) return "the edit names no plugin";
|
|
503
|
+
if (kind !== void 0 && (typeof kind !== "string" || !KINDS.has(kind))) return `unknown kind ${JSON.stringify(kind)}`;
|
|
504
|
+
if (kind !== void 0 && operation !== "set" && operation !== "remove") return `${operation} edits the plugins array, so it takes no kind`;
|
|
554
505
|
if (config !== void 0 && typeof config !== "string" && !Number.isSafeInteger(config)) return "config must name an entry or give its index";
|
|
555
506
|
if (operation === "set" || operation === "remove") {
|
|
556
507
|
if (!Array.isArray(path) || !path.every((key) => typeof key === "string" && key)) return "path must be a list of option names";
|
|
@@ -558,7 +509,6 @@ function invalidEdit(edit) {
|
|
|
558
509
|
const unsafe = path.find((key) => !isSafeKey(key));
|
|
559
510
|
if (unsafe !== void 0) return `${unsafe} cannot be used as an option name`;
|
|
560
511
|
}
|
|
561
|
-
if (operation === "add-plugin" && importName !== void 0 && typeof importName !== "string") return "importName must be a string";
|
|
562
512
|
}
|
|
563
513
|
/** Where a node sits in the source text. */
|
|
564
514
|
function rangeOf(node) {
|
|
@@ -753,29 +703,42 @@ function importedFrom(mod) {
|
|
|
753
703
|
const imports = /* @__PURE__ */ new Map();
|
|
754
704
|
for (const statement of bodyOf(mod)) {
|
|
755
705
|
if (statement.type === "ImportDeclaration") {
|
|
756
|
-
for (const specifier of statement.specifiers)
|
|
706
|
+
for (const specifier of statement.specifiers) {
|
|
707
|
+
const imported = specifier.type === "ImportSpecifier" ? specifier.imported : specifier.local;
|
|
708
|
+
imports.set(specifier.local.name, {
|
|
709
|
+
from: statement.source.value,
|
|
710
|
+
exported: imported.type === "Identifier" ? imported.name : imported.value
|
|
711
|
+
});
|
|
712
|
+
}
|
|
757
713
|
continue;
|
|
758
714
|
}
|
|
759
715
|
if (statement.type !== "VariableDeclaration") continue;
|
|
760
716
|
for (const declarator of statement.declarations) {
|
|
761
717
|
const from = requiredModule(declarator.init);
|
|
762
718
|
if (from === void 0 || declarator.id.type !== "ObjectPattern") continue;
|
|
763
|
-
for (const entry of declarator.id.properties) if (entry.type === "ObjectProperty" && entry.value.type === "Identifier") imports.set(entry.value.name,
|
|
719
|
+
for (const entry of declarator.id.properties) if (entry.type === "ObjectProperty" && entry.value.type === "Identifier") imports.set(entry.value.name, {
|
|
720
|
+
from,
|
|
721
|
+
exported: memberKey(entry) ?? entry.value.name
|
|
722
|
+
});
|
|
764
723
|
}
|
|
765
724
|
}
|
|
766
725
|
return imports;
|
|
767
726
|
}
|
|
768
|
-
/**
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
727
|
+
/**
|
|
728
|
+
* Every factory call of one kind that resolves to an import: the `pluginX(...)` elements of the
|
|
729
|
+
* `plugins` array, the `adapter` value, or the elements of the `parsers` array.
|
|
730
|
+
*/
|
|
731
|
+
function factoryCalls(mod, config, kind = "plugin") {
|
|
732
|
+
const node = property(config, kind === "adapter" ? "adapter" : `${kind}s`);
|
|
733
|
+
const elements = kind === "adapter" ? [node] : node?.type === "ArrayExpression" ? node.elements : [];
|
|
772
734
|
const imports = importedFrom(mod);
|
|
773
|
-
return
|
|
735
|
+
return elements.flatMap((element) => {
|
|
774
736
|
if (element?.type !== "CallExpression" || element.callee.type !== "Identifier") return [];
|
|
775
|
-
const
|
|
776
|
-
return
|
|
737
|
+
const imported = imports.get(element.callee.name);
|
|
738
|
+
return imported ? [{
|
|
777
739
|
importName: element.callee.name,
|
|
778
|
-
|
|
740
|
+
exportName: imported.exported,
|
|
741
|
+
packageName: imported.from,
|
|
779
742
|
call: element
|
|
780
743
|
}] : [];
|
|
781
744
|
});
|
|
@@ -844,11 +807,11 @@ function readConfig(source) {
|
|
|
844
807
|
reason: parsed.reason
|
|
845
808
|
};
|
|
846
809
|
const { mod, configs } = parsed;
|
|
847
|
-
const importNames = new Map([...importedFrom(mod)].map(([local, from]) => [from, local]));
|
|
810
|
+
const importNames = new Map([...importedFrom(mod)].map(([local, { from }]) => [from, local]));
|
|
848
811
|
return {
|
|
849
812
|
managed: true,
|
|
850
813
|
configs: configs.map((config) => {
|
|
851
|
-
const active =
|
|
814
|
+
const active = factoryCalls(mod, config).map(({ importName, packageName, call }) => ({
|
|
852
815
|
line: call.loc?.start.line ?? 0,
|
|
853
816
|
plugin: {
|
|
854
817
|
importName,
|
|
@@ -866,9 +829,18 @@ function readConfig(source) {
|
|
|
866
829
|
}
|
|
867
830
|
}));
|
|
868
831
|
const plugins = [...active, ...disabled].sort((a, b) => a.line - b.line).map(({ plugin }) => plugin);
|
|
832
|
+
const toView = ({ importName, packageName, call }) => ({
|
|
833
|
+
importName,
|
|
834
|
+
packageName,
|
|
835
|
+
options: readOptions(call)
|
|
836
|
+
});
|
|
837
|
+
const [adapter] = factoryCalls(mod, config, "adapter").map(toView);
|
|
838
|
+
const parsers = factoryCalls(mod, config, "parser").map(toView);
|
|
869
839
|
return {
|
|
870
840
|
name: configName(config),
|
|
871
|
-
plugins
|
|
841
|
+
plugins,
|
|
842
|
+
...adapter && { adapter },
|
|
843
|
+
...parsers.length ? { parsers } : {}
|
|
872
844
|
};
|
|
873
845
|
})
|
|
874
846
|
};
|
|
@@ -876,7 +848,7 @@ function readConfig(source) {
|
|
|
876
848
|
/** The modules the config file imports, in source order. Empty when the file cannot be parsed. */
|
|
877
849
|
function readImports(source) {
|
|
878
850
|
try {
|
|
879
|
-
return [...new Set(importedFrom(parseModule(source)).values())];
|
|
851
|
+
return [...new Set([...importedFrom(parseModule(source)).values()].map(({ from }) => from))];
|
|
880
852
|
} catch {
|
|
881
853
|
return [];
|
|
882
854
|
}
|
|
@@ -913,10 +885,10 @@ function applySet(source, call, path, value, quote) {
|
|
|
913
885
|
const options = call.arguments[0];
|
|
914
886
|
if (!options) {
|
|
915
887
|
const closing = rangeOf(call).end - 1;
|
|
916
|
-
if (source[closing] !== ")") return { reason: "the
|
|
888
|
+
if (source[closing] !== ")") return { reason: "the factory call could not be located in the file" };
|
|
917
889
|
return { source: splice(source, closing, closing, printValue(nest(path, value), quote)) };
|
|
918
890
|
}
|
|
919
|
-
if (options.type !== "ObjectExpression") return { reason: "the
|
|
891
|
+
if (options.type !== "ObjectExpression") return { reason: "the factory was not called with an object literal" };
|
|
920
892
|
const target = walk(options, path);
|
|
921
893
|
if ("reason" in target) return target;
|
|
922
894
|
if ("missing" in target) {
|
|
@@ -928,7 +900,7 @@ function applySet(source, call, path, value, quote) {
|
|
|
928
900
|
/** Deletes the option at `path`, falling the plugin back to its default for it. */
|
|
929
901
|
function applyRemove(source, call, path) {
|
|
930
902
|
const options = call.arguments[0];
|
|
931
|
-
if (options?.type !== "ObjectExpression") return { reason: "the
|
|
903
|
+
if (options?.type !== "ObjectExpression") return { reason: "the factory call has no options to remove" };
|
|
932
904
|
const target = walk(options, path);
|
|
933
905
|
if ("reason" in target) return target;
|
|
934
906
|
if ("missing" in target) return { reason: `${path.join(".")} is not set` };
|
|
@@ -959,10 +931,10 @@ function applyAddPlugin({ source, parsed, config, edit, quote, extensions }) {
|
|
|
959
931
|
packageName: edit.plugin,
|
|
960
932
|
extensions
|
|
961
933
|
})) return { reason: `"${edit.plugin}" is not a @kubb/plugin-* package or an installed Kubb plugin` };
|
|
962
|
-
const importName = extensions.get(edit.plugin)?.manifest.export ??
|
|
934
|
+
const importName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
963
935
|
if (!IDENTIFIER.test(importName)) return { reason: `"${importName}" is not a valid import name` };
|
|
964
|
-
if (
|
|
965
|
-
const taken = importedFrom(parsed.mod).get(importName);
|
|
936
|
+
if (factoryCalls(parsed.mod, config).some((plugin) => plugin.packageName === edit.plugin)) return { source };
|
|
937
|
+
const taken = importedFrom(parsed.mod).get(importName)?.from;
|
|
966
938
|
if (taken && taken !== edit.plugin) return { reason: `${importName} is already imported from ${taken}` };
|
|
967
939
|
const options = edit.options ?? {};
|
|
968
940
|
if (!isOptionValue(options)) return { reason: "the options are not literals that can be written to a config file" };
|
|
@@ -973,7 +945,7 @@ function applyAddPlugin({ source, parsed, config, edit, quote, extensions }) {
|
|
|
973
945
|
}
|
|
974
946
|
/** Comments out a plugin call in place, keeping its options on disk so `enable-plugin` can restore them exactly. */
|
|
975
947
|
function disablePlugin(source, mod, config, plugin) {
|
|
976
|
-
const loc =
|
|
948
|
+
const loc = factoryCalls(mod, config).find((entry) => entry.packageName === plugin)?.call.loc;
|
|
977
949
|
if (!loc) return { reason: `${plugin} is not in the plugins array` };
|
|
978
950
|
const lines = source.split("\n");
|
|
979
951
|
const from = loc.start.line - 1;
|
|
@@ -1010,8 +982,14 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1010
982
|
quote,
|
|
1011
983
|
extensions
|
|
1012
984
|
});
|
|
1013
|
-
const
|
|
1014
|
-
|
|
985
|
+
const kind = edit.kind ?? "plugin";
|
|
986
|
+
const calls = factoryCalls(parsed.mod, config, kind).filter((entry) => entry.packageName === edit.plugin);
|
|
987
|
+
const exportName = extensions.get(edit.plugin)?.manifest.export ?? toExportName(edit.plugin);
|
|
988
|
+
const call = (calls.find((entry) => entry.exportName === exportName) ?? calls[0])?.call;
|
|
989
|
+
if (!call) {
|
|
990
|
+
if (kind === "adapter") return { reason: `${edit.plugin} is not the config's adapter` };
|
|
991
|
+
return { reason: kind === "plugin" && disabledBlocks(source, config).some((block) => block.packageName === edit.plugin) ? `${edit.plugin} is disabled, enable it first` : `${edit.plugin} is not in the ${kind}s array` };
|
|
992
|
+
}
|
|
1015
993
|
return edit.operation === "set" ? applySet(source, call, edit.path, edit.value, quote) : applyRemove(source, call, edit.path);
|
|
1016
994
|
}
|
|
1017
995
|
/**
|
|
@@ -1083,32 +1061,6 @@ async function writeConfigEdits({ filePath, edits, extensions }) {
|
|
|
1083
1061
|
return result;
|
|
1084
1062
|
}
|
|
1085
1063
|
//#endregion
|
|
1086
|
-
//#region src/extensions.ts
|
|
1087
|
-
/** Extensions `kubb` ships with, so they count as installed without being a direct dependency. */
|
|
1088
|
-
const BUILT_IN_PACKAGES = [
|
|
1089
|
-
"@kubb/adapter-oas",
|
|
1090
|
-
"@kubb/parser-ts",
|
|
1091
|
-
"@kubb/parser-md",
|
|
1092
|
-
"@kubb/plugin-barrel"
|
|
1093
|
-
];
|
|
1094
|
-
/**
|
|
1095
|
-
* Finds the extensions installed in the project at `root`: its `dependencies` and `devDependencies`,
|
|
1096
|
-
* the `packages` its config file imports, and the extensions `kubb` ships with. Keeps each package
|
|
1097
|
-
* that resolves and ships a valid `kubb-extension.json`. Never throws.
|
|
1098
|
-
*/
|
|
1099
|
-
async function discoverExtensions({ root, packages = [] }) {
|
|
1100
|
-
const installed = await readInstalledPackages({
|
|
1101
|
-
root,
|
|
1102
|
-
packages: [...packages, ...BUILT_IN_PACKAGES],
|
|
1103
|
-
resolveFrom: ["kubb"]
|
|
1104
|
-
});
|
|
1105
|
-
return new Map(installed.flatMap(({ package: name, version, manifest }) => version !== void 0 && manifest ? [[name, {
|
|
1106
|
-
package: name,
|
|
1107
|
-
version,
|
|
1108
|
-
manifest
|
|
1109
|
-
}]] : []));
|
|
1110
|
-
}
|
|
1111
|
-
//#endregion
|
|
1112
1064
|
//#region src/generations.ts
|
|
1113
1065
|
const READ_CONCURRENCY = 50;
|
|
1114
1066
|
const MB = 1048576;
|
|
@@ -1516,6 +1468,13 @@ const OPEN_TIMEOUT_MS = 5e3;
|
|
|
1516
1468
|
const MAX_RETRY_DELAY_MS = 3e4;
|
|
1517
1469
|
const DISK_SNAPSHOT_MAX_FILES = 1e4;
|
|
1518
1470
|
const SANDBOX_GENERATION_TTL_MS = 9e5;
|
|
1471
|
+
/** Extensions `kubb` ships with, so they count as installed without being a direct dependency. */
|
|
1472
|
+
const BUILT_IN_PACKAGES = [
|
|
1473
|
+
"@kubb/adapter-oas",
|
|
1474
|
+
"@kubb/parser-ts",
|
|
1475
|
+
"@kubb/parser-md",
|
|
1476
|
+
"@kubb/plugin-barrel"
|
|
1477
|
+
];
|
|
1519
1478
|
/**
|
|
1520
1479
|
* Studio closed with {@link AgentCloseCode.UNAUTHORIZED}. Hosts forget the token and pair again.
|
|
1521
1480
|
*/
|
|
@@ -1614,10 +1573,10 @@ var Agent = class extends RpcTarget {
|
|
|
1614
1573
|
});
|
|
1615
1574
|
return value;
|
|
1616
1575
|
}
|
|
1617
|
-
async #readConfigFileView(
|
|
1576
|
+
async #readConfigFileView() {
|
|
1618
1577
|
if (!this.#permissions.allowConfigEdit) return void 0;
|
|
1619
1578
|
try {
|
|
1620
|
-
return readConfig(
|
|
1579
|
+
return readConfig(await read(this.#options.configFile));
|
|
1621
1580
|
} catch (error) {
|
|
1622
1581
|
await this.#warn({ message: `Could not read ${this.#options.configFile}: ${getErrorMessage(error)}` });
|
|
1623
1582
|
return;
|
|
@@ -1625,10 +1584,16 @@ var Agent = class extends RpcTarget {
|
|
|
1625
1584
|
}
|
|
1626
1585
|
async #installedExtensions() {
|
|
1627
1586
|
const source = await read(this.#options.configFile).catch(() => "");
|
|
1628
|
-
|
|
1587
|
+
const installed = await readInstalledPackages({
|
|
1629
1588
|
root: this.#options.root,
|
|
1630
|
-
packages: readImports(source)
|
|
1589
|
+
packages: [...readImports(source), ...BUILT_IN_PACKAGES],
|
|
1590
|
+
resolveFrom: ["kubb"]
|
|
1631
1591
|
});
|
|
1592
|
+
return new Map(installed.flatMap(({ package: name, version, manifest }) => version !== void 0 && manifest ? [[name, {
|
|
1593
|
+
package: name,
|
|
1594
|
+
version,
|
|
1595
|
+
manifest
|
|
1596
|
+
}]] : []));
|
|
1632
1597
|
}
|
|
1633
1598
|
async connect(info) {
|
|
1634
1599
|
this.#session.info = info;
|
|
@@ -1651,11 +1616,26 @@ var Agent = class extends RpcTarget {
|
|
|
1651
1616
|
plugins: config.plugins.map((plugin) => ({
|
|
1652
1617
|
name: toStudioName({
|
|
1653
1618
|
name: resolveMeta(plugin).name,
|
|
1654
|
-
root,
|
|
1655
1619
|
extensions
|
|
1656
1620
|
}),
|
|
1657
1621
|
options: toJSONOptions(plugin.options)
|
|
1658
1622
|
})),
|
|
1623
|
+
adapter: config.adapter && {
|
|
1624
|
+
name: toStudioName({
|
|
1625
|
+
name: resolveMeta(config.adapter).name,
|
|
1626
|
+
extensions,
|
|
1627
|
+
kind: "adapter"
|
|
1628
|
+
}),
|
|
1629
|
+
options: toJSONOptions(config.adapter.options)
|
|
1630
|
+
},
|
|
1631
|
+
parsers: config.parsers.map((parser) => ({
|
|
1632
|
+
name: toStudioName({
|
|
1633
|
+
name: resolveMeta(parser).name,
|
|
1634
|
+
extensions,
|
|
1635
|
+
kind: "parser"
|
|
1636
|
+
}),
|
|
1637
|
+
options: toJSONOptions(parser.options)
|
|
1638
|
+
})),
|
|
1659
1639
|
extensions: [...extensions.values()]
|
|
1660
1640
|
},
|
|
1661
1641
|
permissions: this.#permissions
|
|
@@ -1715,11 +1695,19 @@ var Agent = class extends RpcTarget {
|
|
|
1715
1695
|
const studioInput = this.#sandbox ? patch?.input : void 0;
|
|
1716
1696
|
if (this.#sandbox && (typeof studioInput !== "string" || !studioInput.trim() || getInputKind(studioInput) !== "inline")) return this.#refuse({ message: "A sandbox agent generates only from the OpenAPI document content Studio sends, not from a file path or URL" });
|
|
1717
1697
|
const [config, extensions] = await Promise.all([this.#options.loadConfig(), this.#installedExtensions()]);
|
|
1718
|
-
const [plugins, adapter] = await Promise.all([
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1698
|
+
const [plugins, adapter, parsers] = await Promise.all([
|
|
1699
|
+
mergePlugins({
|
|
1700
|
+
diskPlugins: config.plugins,
|
|
1701
|
+
studioPlugins: patch?.plugins,
|
|
1702
|
+
extensions
|
|
1703
|
+
}),
|
|
1704
|
+
mergeAdapter(config.adapter, patch?.adapter, extensions),
|
|
1705
|
+
mergeParsers({
|
|
1706
|
+
diskParsers: config.parsers,
|
|
1707
|
+
studioParsers: patch?.parsers,
|
|
1708
|
+
extensions
|
|
1709
|
+
})
|
|
1710
|
+
]).catch(async (error) => {
|
|
1723
1711
|
await emit(this.#options.hooks, "kubb:error", { error: toError(error) });
|
|
1724
1712
|
throw error;
|
|
1725
1713
|
});
|
|
@@ -1743,7 +1731,8 @@ var Agent = class extends RpcTarget {
|
|
|
1743
1731
|
input: studioInput ?? config.input,
|
|
1744
1732
|
storage: this.#permissions.allowWrite ? fsStorage() : memoryStorage(),
|
|
1745
1733
|
plugins: activePlugins,
|
|
1746
|
-
adapter
|
|
1734
|
+
adapter,
|
|
1735
|
+
parsers
|
|
1747
1736
|
},
|
|
1748
1737
|
hooks: this.#options.hooks,
|
|
1749
1738
|
signal,
|
|
@@ -1805,7 +1794,7 @@ var Agent = class extends RpcTarget {
|
|
|
1805
1794
|
}
|
|
1806
1795
|
if (this.#session.active) return { value: refuse("a generation is in progress") };
|
|
1807
1796
|
try {
|
|
1808
|
-
const {
|
|
1797
|
+
const { outcomes, changed } = await writeConfigEdits({
|
|
1809
1798
|
filePath: configFile,
|
|
1810
1799
|
edits,
|
|
1811
1800
|
extensions: await this.#installedExtensions()
|
|
@@ -1814,8 +1803,7 @@ var Agent = class extends RpcTarget {
|
|
|
1814
1803
|
return {
|
|
1815
1804
|
value: {
|
|
1816
1805
|
outcomes,
|
|
1817
|
-
changed
|
|
1818
|
-
file: changed ? await this.#readConfigFileView(source) : void 0
|
|
1806
|
+
changed
|
|
1819
1807
|
},
|
|
1820
1808
|
info: `applied ${applied}/${outcomes.length} edits to ${configPath}`
|
|
1821
1809
|
};
|
|
@@ -2296,8 +2284,16 @@ async function createSnapshot({ studioUrl = defaultStudioUrl, apiKey, config, ho
|
|
|
2296
2284
|
if (content === null) throw new Error(`The generation listed ${path} but did not keep its content`);
|
|
2297
2285
|
return [path, content];
|
|
2298
2286
|
})));
|
|
2299
|
-
const
|
|
2300
|
-
const
|
|
2287
|
+
const pluginNames = config.plugins.map((plugin) => resolveMeta(plugin).name);
|
|
2288
|
+
const installed = await readInstalledPackages({
|
|
2289
|
+
root: config.root,
|
|
2290
|
+
packages: pluginNames.map(toPackageName),
|
|
2291
|
+
resolveFrom: ["kubb"]
|
|
2292
|
+
});
|
|
2293
|
+
const packageOf = (name) => installed.find(({ manifest }) => manifest?.kind === "plugin" && manifest.name === name)?.package ?? toPackageName(name);
|
|
2294
|
+
const names = [...new Set(pluginNames.map(packageOf))];
|
|
2295
|
+
const peerDependencies = Object.fromEntries(installed.flatMap(({ package: name, version }) => names.includes(name) && version ? [[name, version]] : []));
|
|
2296
|
+
const missing = names.filter((name) => !(name in peerDependencies) && !BUNDLED_DEPENDENCIES.includes(name));
|
|
2301
2297
|
if (missing.length) throw new Error(`Missing dependencies: ${missing.join(", ")}`);
|
|
2302
2298
|
const bytes = await createSnapshotPackage({
|
|
2303
2299
|
files,
|