@kubb/studio 5.12.2 → 5.12.4
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 +195 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +196 -27
- package/dist/index.js.map +1 -1
- package/dist/{protocol-DhNrwhqp.d.ts → protocol-C4loKcxw.d.cts} +66 -12
- package/dist/{protocol-DhNrwhqp.d.cts → protocol-C4loKcxw.d.ts} +66 -12
- package/dist/protocol.cjs +15 -0
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +15 -1
- package/dist/protocol.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
const require_protocol = require("./protocol.cjs");
|
|
25
25
|
let node_crypto = require("node:crypto");
|
|
26
26
|
let node_fs = require("node:fs");
|
|
27
|
+
let node_fs_promises = require("node:fs/promises");
|
|
27
28
|
let node_module = require("node:module");
|
|
28
29
|
let node_os = require("node:os");
|
|
29
30
|
let node_path = require("node:path");
|
|
@@ -32,7 +33,6 @@ let node_process = require("node:process");
|
|
|
32
33
|
node_process = __toESM(node_process, 1);
|
|
33
34
|
let node_timers_promises = require("node:timers/promises");
|
|
34
35
|
let tinyexec = require("tinyexec");
|
|
35
|
-
let node_fs_promises = require("node:fs/promises");
|
|
36
36
|
let _kubb_core = require("@kubb/core");
|
|
37
37
|
let capnweb = require("capnweb");
|
|
38
38
|
let magicast = require("magicast");
|
|
@@ -230,7 +230,7 @@ function getElapsedMs(hrStart) {
|
|
|
230
230
|
}
|
|
231
231
|
//#endregion
|
|
232
232
|
//#region package.json
|
|
233
|
-
var version = "5.12.
|
|
233
|
+
var version = "5.12.4";
|
|
234
234
|
//#endregion
|
|
235
235
|
//#region src/resolveConfig.ts
|
|
236
236
|
/**
|
|
@@ -586,6 +586,44 @@ async function rebuildExtension({ disk, studioOptions, kind, extensions }) {
|
|
|
586
586
|
});
|
|
587
587
|
}))(mergeOptions(disk.options ?? {}, studioOptions));
|
|
588
588
|
}
|
|
589
|
+
/** The root `output` members Studio may set, with the values each takes. The rest, `path` and `postGenerate` among them, stay with the config file. */
|
|
590
|
+
const OUTPUT_VALUES = {
|
|
591
|
+
barrel: (value) => value === false || isPlainObject(value) && Object.keys(value).length === 1 && (value.type === "named" || value.type === "all"),
|
|
592
|
+
format: (value) => value === false || [
|
|
593
|
+
"auto",
|
|
594
|
+
"prettier",
|
|
595
|
+
"biome",
|
|
596
|
+
"oxfmt"
|
|
597
|
+
].includes(value),
|
|
598
|
+
lint: (value) => value === false || [
|
|
599
|
+
"auto",
|
|
600
|
+
"eslint",
|
|
601
|
+
"biome",
|
|
602
|
+
"oxlint"
|
|
603
|
+
].includes(value),
|
|
604
|
+
clean: (value) => typeof value === "boolean",
|
|
605
|
+
defaultBanner: (value) => value === false || value === "simple" || value === "full"
|
|
606
|
+
};
|
|
607
|
+
/** Whether Studio may set the root `output` member `key`. */
|
|
608
|
+
function isOutputKey(key) {
|
|
609
|
+
return Object.hasOwn(OUTPUT_VALUES, key);
|
|
610
|
+
}
|
|
611
|
+
/** Whether Studio may set the root `output` member `key` to `value`. */
|
|
612
|
+
function isOutputValue({ key, value }) {
|
|
613
|
+
return isOutputKey(key) && OUTPUT_VALUES[key](value);
|
|
614
|
+
}
|
|
615
|
+
/** The members of `output` Studio may see and set. */
|
|
616
|
+
function pickOutput(output) {
|
|
617
|
+
return Object.fromEntries(Object.entries(output).filter(([key]) => isOutputKey(key)));
|
|
618
|
+
}
|
|
619
|
+
/** Merges Studio's valid `output` overrides, with `null` unsetting a member. */
|
|
620
|
+
function mergeOutput({ output, studio }) {
|
|
621
|
+
if (!isPlainObject(studio)) return output;
|
|
622
|
+
return mergeOptions(output, Object.fromEntries(Object.entries(studio).filter(([key, value]) => value === null ? isOutputKey(key) : isOutputValue({
|
|
623
|
+
key,
|
|
624
|
+
value
|
|
625
|
+
}))));
|
|
626
|
+
}
|
|
589
627
|
/**
|
|
590
628
|
* Merges Studio-provided adapter option overrides into the disk config's adapter, see {@link rebuildExtension}.
|
|
591
629
|
*/
|
|
@@ -628,13 +666,7 @@ const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
|
628
666
|
* line count, so `enable-plugin` restores exactly those lines and not whatever comments follow.
|
|
629
667
|
*/
|
|
630
668
|
const DISABLED_MARKER = /^\/\/ kubb:disabled (.+)\s+(\d+)$/;
|
|
631
|
-
const OPERATIONS =
|
|
632
|
-
"set",
|
|
633
|
-
"remove",
|
|
634
|
-
"add-plugin",
|
|
635
|
-
"disable-plugin",
|
|
636
|
-
"enable-plugin"
|
|
637
|
-
]);
|
|
669
|
+
const OPERATIONS = new Set(Object.values(require_protocol.ConfigEditOperation));
|
|
638
670
|
const KINDS = /* @__PURE__ */ new Set([
|
|
639
671
|
"plugin",
|
|
640
672
|
"adapter",
|
|
@@ -645,17 +677,20 @@ const KINDS = /* @__PURE__ */ new Set([
|
|
|
645
677
|
* The edit crossed the wire, so its declared type is a promise the sender may not have kept.
|
|
646
678
|
*/
|
|
647
679
|
function invalidEdit(edit) {
|
|
648
|
-
const { operation, plugin, config, path, kind } = typeof edit === "object" && edit !== null ? edit : {};
|
|
680
|
+
const { operation, plugin, config, path, kind, input } = typeof edit === "object" && edit !== null ? edit : {};
|
|
649
681
|
if (typeof operation !== "string" || !OPERATIONS.has(operation)) return `unknown operation ${JSON.stringify(operation)}`;
|
|
650
|
-
if (typeof plugin !== "string" || !plugin) return "the edit names no plugin";
|
|
651
|
-
if (kind !== void 0 && (typeof kind !== "string" || !KINDS.has(kind))) return `unknown kind ${JSON.stringify(kind)}`;
|
|
652
|
-
if (kind !== void 0 && operation !== "set" && operation !== "remove") return `${operation} edits the plugins array, so it takes no kind`;
|
|
653
682
|
if (config !== void 0 && typeof config !== "string" && !Number.isSafeInteger(config)) return "config must name an entry or give its index";
|
|
654
|
-
if (operation ===
|
|
683
|
+
if (operation === require_protocol.ConfigEditOperation.SET_INPUT) return typeof input === "string" && input.trim() ? void 0 : "the edit names no input";
|
|
684
|
+
const isOutput = operation === require_protocol.ConfigEditOperation.SET_OUTPUT || operation === require_protocol.ConfigEditOperation.REMOVE_OUTPUT;
|
|
685
|
+
if (!isOutput && (typeof plugin !== "string" || !plugin)) return "the edit names no plugin";
|
|
686
|
+
if (kind !== void 0 && (typeof kind !== "string" || !KINDS.has(kind))) return `unknown kind ${JSON.stringify(kind)}`;
|
|
687
|
+
if (kind !== void 0 && operation !== require_protocol.ConfigEditOperation.SET && operation !== require_protocol.ConfigEditOperation.REMOVE) return `${operation} edits the plugins array, so it takes no kind`;
|
|
688
|
+
if (isOutput || operation === require_protocol.ConfigEditOperation.SET || operation === require_protocol.ConfigEditOperation.REMOVE) {
|
|
655
689
|
if (!Array.isArray(path) || !path.every((key) => typeof key === "string" && key)) return "path must be a list of option names";
|
|
656
690
|
if (!path.length) return "no option path given";
|
|
657
691
|
const unsafe = path.find((key) => !isSafeKey(key));
|
|
658
692
|
if (unsafe !== void 0) return `${unsafe} cannot be used as an option name`;
|
|
693
|
+
if (isOutput && !isOutputKey(path[0])) return `output.${path[0]} can only be changed in the config file`;
|
|
659
694
|
}
|
|
660
695
|
}
|
|
661
696
|
/** Where a node sits in the source text. */
|
|
@@ -956,9 +991,12 @@ function readPartial(node, prefix = []) {
|
|
|
956
991
|
}
|
|
957
992
|
/** The top-level options of a plugin call, each flagged with whether Studio may write it. */
|
|
958
993
|
function readOptions(call) {
|
|
959
|
-
const entries = {};
|
|
960
994
|
const options = call.arguments[0];
|
|
961
|
-
|
|
995
|
+
return options?.type === "ObjectExpression" ? readObject(options) : {};
|
|
996
|
+
}
|
|
997
|
+
/** The members of an object literal, each flagged with whether Studio may write it. */
|
|
998
|
+
function readObject(options) {
|
|
999
|
+
const entries = {};
|
|
962
1000
|
for (const [index, entry] of options.properties.entries()) {
|
|
963
1001
|
const key = memberKey(entry);
|
|
964
1002
|
if (key === void 0 || !isSafeKey(key)) continue;
|
|
@@ -1031,11 +1069,13 @@ function readConfig(source) {
|
|
|
1031
1069
|
});
|
|
1032
1070
|
const [adapter] = factoryCalls(mod, config, "adapter").map(toView);
|
|
1033
1071
|
const parsers = factoryCalls(mod, config, "parser").map(toView);
|
|
1072
|
+
const output = outputObject(config);
|
|
1034
1073
|
return {
|
|
1035
1074
|
name: configName(config),
|
|
1036
1075
|
plugins,
|
|
1037
1076
|
...adapter && { adapter },
|
|
1038
|
-
...parsers.length ? { parsers } : {}
|
|
1077
|
+
...parsers.length ? { parsers } : {},
|
|
1078
|
+
...!("reason" in output) && { output: pickOutput(readObject(output.object)) }
|
|
1039
1079
|
};
|
|
1040
1080
|
})
|
|
1041
1081
|
};
|
|
@@ -1084,6 +1124,16 @@ function applySet(source, call, path, value, quote) {
|
|
|
1084
1124
|
return { source: splice(source, closing, closing, printValue(nest(path, value), quote)) };
|
|
1085
1125
|
}
|
|
1086
1126
|
if (options.type !== "ObjectExpression") return { reason: "the factory was not called with an object literal" };
|
|
1127
|
+
return setIn({
|
|
1128
|
+
source,
|
|
1129
|
+
options,
|
|
1130
|
+
path,
|
|
1131
|
+
value,
|
|
1132
|
+
quote
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
/** Writes the literal `value` at `path` inside `options`, adding any intermediate object it needs. */
|
|
1136
|
+
function setIn({ source, options, path, value, quote }) {
|
|
1087
1137
|
const target = walk(options, path);
|
|
1088
1138
|
if ("reason" in target) return target;
|
|
1089
1139
|
if ("missing" in target) {
|
|
@@ -1092,6 +1142,43 @@ function applySet(source, call, path, value, quote) {
|
|
|
1092
1142
|
}
|
|
1093
1143
|
return { source: splice(source, rangeOf(target.value).start, rangeOf(target.value).end, printValue(value, quote)) };
|
|
1094
1144
|
}
|
|
1145
|
+
/** Writes the config's top-level `input` as a string literal, replacing what it held. */
|
|
1146
|
+
function applySetInput(source, config, input, quote) {
|
|
1147
|
+
const value = printValue(input, quote);
|
|
1148
|
+
const found = findMember(config, "input");
|
|
1149
|
+
if (!found) return { source: insertItem(source, config, `input: ${value}`) };
|
|
1150
|
+
if (found.member.type !== "ObjectProperty") return { reason: "input is not a plain property" };
|
|
1151
|
+
if (isShadowed(config, found.index)) return { reason: "a spread after input could override it" };
|
|
1152
|
+
return { source: splice(source, rangeOf(found.member.value).start, rangeOf(found.member.value).end, value) };
|
|
1153
|
+
}
|
|
1154
|
+
/** The config's root `output` object literal, or why Studio cannot edit inside it. */
|
|
1155
|
+
function outputObject(config) {
|
|
1156
|
+
const found = findMember(config, "output");
|
|
1157
|
+
if (found?.member.type !== "ObjectProperty" || found.member.value.type !== "ObjectExpression") return { reason: "output is not an object literal" };
|
|
1158
|
+
if (isShadowed(config, found.index)) return { reason: "a spread after output could override it" };
|
|
1159
|
+
return { object: found.member.value };
|
|
1160
|
+
}
|
|
1161
|
+
/** Writes or drops a member of the config's root `output` object. */
|
|
1162
|
+
function applyOutputEdit({ source, config, edit, quote }) {
|
|
1163
|
+
const output = outputObject(config);
|
|
1164
|
+
if ("reason" in output) return output;
|
|
1165
|
+
if (edit.operation === require_protocol.ConfigEditOperation.REMOVE_OUTPUT) return removeIn({
|
|
1166
|
+
source,
|
|
1167
|
+
options: output.object,
|
|
1168
|
+
path: edit.path
|
|
1169
|
+
});
|
|
1170
|
+
if (!isOptionValue(edit.value) || !isOutputValue({
|
|
1171
|
+
key: edit.path[0],
|
|
1172
|
+
value: nest(edit.path.slice(1), edit.value)
|
|
1173
|
+
})) return { reason: `the value is not one output.${edit.path[0]} takes` };
|
|
1174
|
+
return setIn({
|
|
1175
|
+
source,
|
|
1176
|
+
options: output.object,
|
|
1177
|
+
path: edit.path,
|
|
1178
|
+
value: edit.value,
|
|
1179
|
+
quote
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1095
1182
|
/**
|
|
1096
1183
|
* Deletes the option at `path`, falling the plugin back to its default for it. An option the file
|
|
1097
1184
|
* does not set already has its default, so removing it changes nothing.
|
|
@@ -1100,6 +1187,14 @@ function applyRemove(source, call, path) {
|
|
|
1100
1187
|
const options = call.arguments[0];
|
|
1101
1188
|
if (!options) return { source };
|
|
1102
1189
|
if (options.type !== "ObjectExpression") return { reason: "the factory was not called with an object literal" };
|
|
1190
|
+
return removeIn({
|
|
1191
|
+
source,
|
|
1192
|
+
options,
|
|
1193
|
+
path
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
/** Deletes the member at `path` inside `options`. A path that is not there already holds its default. */
|
|
1197
|
+
function removeIn({ source, options, path }) {
|
|
1103
1198
|
const target = walk(options, path);
|
|
1104
1199
|
if ("reason" in target) return target;
|
|
1105
1200
|
if ("missing" in target) return { source };
|
|
@@ -1188,7 +1283,14 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1188
1283
|
const findConfig = (entries) => typeof ref === "string" ? entries.configs.find((entry) => configName(entry) === ref) : entries.configs[ref ?? 0];
|
|
1189
1284
|
const config = findConfig(parsed);
|
|
1190
1285
|
if (!config) return { reason: `no config entry found for ${JSON.stringify(ref)}` };
|
|
1191
|
-
if (edit.operation ===
|
|
1286
|
+
if (edit.operation === require_protocol.ConfigEditOperation.SET_INPUT) return applySetInput(source, config, edit.input, quote);
|
|
1287
|
+
if (edit.operation === require_protocol.ConfigEditOperation.SET_OUTPUT || edit.operation === require_protocol.ConfigEditOperation.REMOVE_OUTPUT) return applyOutputEdit({
|
|
1288
|
+
source,
|
|
1289
|
+
config,
|
|
1290
|
+
edit,
|
|
1291
|
+
quote
|
|
1292
|
+
});
|
|
1293
|
+
if (edit.operation === require_protocol.ConfigEditOperation.DISABLE_PLUGIN) {
|
|
1192
1294
|
const result = disablePlugin(source, parsed.mod, config, edit.plugin);
|
|
1193
1295
|
if (!("reason" in result) || !result.reason.includes("shares a line")) return result;
|
|
1194
1296
|
const expanded = expandPluginsArray(source, config);
|
|
@@ -1196,8 +1298,8 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1196
1298
|
const reparsedConfig = reparsed && !("reason" in reparsed) ? findConfig(reparsed) : void 0;
|
|
1197
1299
|
return expanded !== void 0 && reparsed && !("reason" in reparsed) && reparsedConfig ? disablePlugin(expanded, reparsed.mod, reparsedConfig, edit.plugin) : result;
|
|
1198
1300
|
}
|
|
1199
|
-
if (edit.operation ===
|
|
1200
|
-
if (edit.operation ===
|
|
1301
|
+
if (edit.operation === require_protocol.ConfigEditOperation.ENABLE_PLUGIN) return enablePlugin(source, config, edit.plugin);
|
|
1302
|
+
if (edit.operation === require_protocol.ConfigEditOperation.ADD_PLUGIN) return applyAddPlugin({
|
|
1201
1303
|
source,
|
|
1202
1304
|
parsed,
|
|
1203
1305
|
config,
|
|
@@ -1213,7 +1315,7 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1213
1315
|
if (kind === "adapter") return { reason: `${edit.plugin} is not the config's adapter` };
|
|
1214
1316
|
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` };
|
|
1215
1317
|
}
|
|
1216
|
-
return edit.operation ===
|
|
1318
|
+
return edit.operation === require_protocol.ConfigEditOperation.SET ? applySet(source, call, edit.path, edit.value, quote) : applyRemove(source, call, edit.path);
|
|
1217
1319
|
}
|
|
1218
1320
|
/**
|
|
1219
1321
|
* Applies edits to a `kubb.config.ts` in place. Each edit rewrites only the text it targets, so
|
|
@@ -2151,6 +2253,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2151
2253
|
allowConfigEdit: false,
|
|
2152
2254
|
allowInstall: false,
|
|
2153
2255
|
allowUpdate: false,
|
|
2256
|
+
allowSaveInput: false,
|
|
2154
2257
|
allowRead: true,
|
|
2155
2258
|
allowReadAst: true
|
|
2156
2259
|
} : permissions;
|
|
@@ -2176,13 +2279,13 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2176
2279
|
permission
|
|
2177
2280
|
});
|
|
2178
2281
|
}
|
|
2179
|
-
async #refuse({ message, permission }) {
|
|
2282
|
+
async #refuse({ message, permission, code = _kubb_core.Diagnostics.code.unknown }) {
|
|
2180
2283
|
await this.#warn({
|
|
2181
2284
|
message,
|
|
2182
2285
|
permission
|
|
2183
2286
|
});
|
|
2184
2287
|
throw new _kubb_core.Diagnostics.Error({
|
|
2185
|
-
code
|
|
2288
|
+
code,
|
|
2186
2289
|
severity: "error",
|
|
2187
2290
|
message
|
|
2188
2291
|
});
|
|
@@ -2374,12 +2477,22 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2374
2477
|
});
|
|
2375
2478
|
const activePlugins = plugins ?? config.plugins;
|
|
2376
2479
|
if (patch?.input && !this.#sandbox && !this.#managed) await emit(this.#options.hooks, "kubb:warn", { message: "Ignored the spec from Studio: generating from a Studio spec is only available to a sandbox or managed agent" });
|
|
2377
|
-
const checkout = this.#managed && studioInput !== void 0 && (0, _kubb_core.getInputKind)(studioInput) === "git" ? await (0, _kubb_core.checkoutGitInput)(studioInput, signal, {
|
|
2480
|
+
const checkout = this.#managed && studioInput !== void 0 && (0, _kubb_core.getInputKind)(studioInput) === "git" ? await (0, _kubb_core.checkoutGitInput)(studioInput, signal, {
|
|
2481
|
+
publicOnly: true,
|
|
2482
|
+
authorization: input.gitAuth?.authorization
|
|
2483
|
+
}).catch((error) => this.#refuse({
|
|
2484
|
+
message: getErrorMessage(error),
|
|
2485
|
+
code: _kubb_core.Diagnostics.code.gitInputFailed
|
|
2486
|
+
})) : void 0;
|
|
2378
2487
|
const run = () => generate({
|
|
2379
2488
|
config: {
|
|
2380
2489
|
...config,
|
|
2381
2490
|
root,
|
|
2382
2491
|
input: checkout?.path ?? studioInput ?? config.input,
|
|
2492
|
+
output: mergeOutput({
|
|
2493
|
+
output: config.output,
|
|
2494
|
+
studio: patch?.output
|
|
2495
|
+
}),
|
|
2383
2496
|
storage: outputRoot ? stagingStorage({
|
|
2384
2497
|
root,
|
|
2385
2498
|
directory: outputRoot
|
|
@@ -2448,11 +2561,29 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2448
2561
|
if (busy) return refuse(busy);
|
|
2449
2562
|
this.#session.saving = true;
|
|
2450
2563
|
try {
|
|
2451
|
-
const
|
|
2564
|
+
const kept = [];
|
|
2565
|
+
for (const edit of edits) kept.push(await this.#keepInput(edit));
|
|
2566
|
+
const written = await writeConfigEdits({
|
|
2452
2567
|
filePath: configFile,
|
|
2453
|
-
edits,
|
|
2568
|
+
edits: kept.flatMap((edit) => "reason" in edit ? [] : [edit]),
|
|
2454
2569
|
extensions: await this.#installedExtensions()
|
|
2455
2570
|
});
|
|
2571
|
+
let next = 0;
|
|
2572
|
+
const outcomes = kept.map((edit, index) => "reason" in edit ? {
|
|
2573
|
+
edit: edits[index],
|
|
2574
|
+
applied: false,
|
|
2575
|
+
reason: edit.reason
|
|
2576
|
+
} : {
|
|
2577
|
+
...written.outcomes[next++],
|
|
2578
|
+
edit: edits[index]
|
|
2579
|
+
});
|
|
2580
|
+
let wroteDocument = false;
|
|
2581
|
+
for (const [index, edit] of kept.entries()) {
|
|
2582
|
+
if (!("document" in edit) || !edit.document || !outcomes[index].applied) continue;
|
|
2583
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(this.#options.root, edit.document.fileName), edit.document.text, "utf-8");
|
|
2584
|
+
wroteDocument = true;
|
|
2585
|
+
}
|
|
2586
|
+
const changed = written.changed || wroteDocument;
|
|
2456
2587
|
const applied = outcomes.filter((outcome) => outcome.applied).length;
|
|
2457
2588
|
await this.#logCommand("saveConfig", `applied ${applied}/${outcomes.length} edits to ${configPath}`, input.requestedBy);
|
|
2458
2589
|
return {
|
|
@@ -2467,6 +2598,33 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2467
2598
|
}
|
|
2468
2599
|
}
|
|
2469
2600
|
/**
|
|
2601
|
+
* Readies a `set-input` edit for the config file: a document is pointed at `openapi.yaml` (`openapi.json`
|
|
2602
|
+
* for JSON) in the project, one file per config entry, for `saveConfig` to write once the edit applies.
|
|
2603
|
+
* A managed agent refuses a file path, as in a generation.
|
|
2604
|
+
*/
|
|
2605
|
+
async #keepInput(edit) {
|
|
2606
|
+
if (edit?.operation !== require_protocol.ConfigEditOperation.SET_INPUT || typeof edit.input !== "string" || !edit.input.trim()) return edit;
|
|
2607
|
+
if (!this.#permissions.allowSaveInput) {
|
|
2608
|
+
await this.#warn({
|
|
2609
|
+
message: "Ignored the spec to save: saving the input was not granted",
|
|
2610
|
+
permission: require_protocol.AgentPermission.SAVE_INPUT
|
|
2611
|
+
});
|
|
2612
|
+
return { reason: "the agent was not granted permission to save the input" };
|
|
2613
|
+
}
|
|
2614
|
+
const kind = (0, _kubb_core.getInputKind)(edit.input);
|
|
2615
|
+
if (this.#managed && !MANAGED_INPUT_KINDS.has(kind)) return { reason: require_protocol.AgentMessage.MANAGED_INPUT_KIND };
|
|
2616
|
+
if (kind !== "inline") return edit;
|
|
2617
|
+
const fileName = `openapi${edit.config === void 0 || edit.config === 0 ? "" : `-${String(edit.config).replace(/[^\w.-]/g, "-")}`}.${isJson(edit.input) ? "json" : "yaml"}`;
|
|
2618
|
+
return {
|
|
2619
|
+
...edit,
|
|
2620
|
+
input: `./${fileName}`,
|
|
2621
|
+
document: {
|
|
2622
|
+
fileName,
|
|
2623
|
+
text: edit.input
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2470
2628
|
* Installs a registry extension into the project with `kubb add <id> --yes`, which also adds a
|
|
2471
2629
|
* plugin to `kubb.config.ts`. Studio calls `connect()` again afterwards to see the result.
|
|
2472
2630
|
*/
|
|
@@ -2767,6 +2925,15 @@ function pickConfig(configs, ref) {
|
|
|
2767
2925
|
if (ref !== void 0 && !Number.isSafeInteger(ref)) return void 0;
|
|
2768
2926
|
return configs[ref ?? 0];
|
|
2769
2927
|
}
|
|
2928
|
+
/** Whether `text` parses as JSON, so a pasted document is saved under the matching extension. */
|
|
2929
|
+
function isJson(text) {
|
|
2930
|
+
try {
|
|
2931
|
+
JSON.parse(text);
|
|
2932
|
+
return true;
|
|
2933
|
+
} catch {
|
|
2934
|
+
return false;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2770
2937
|
/** The input path or URL Studio may show: no inline document, and no credentials from a URL. */
|
|
2771
2938
|
function reportedInput(input) {
|
|
2772
2939
|
if (typeof input !== "string") return void 0;
|
|
@@ -2785,6 +2952,7 @@ function toEntryView(config, extensions) {
|
|
|
2785
2952
|
...config.name !== void 0 && { name: config.name },
|
|
2786
2953
|
...input !== void 0 && { input },
|
|
2787
2954
|
output: config.output.path,
|
|
2955
|
+
outputOptions: toJSONOptions(pickOutput(config.output)),
|
|
2788
2956
|
plugins: config.plugins.map((plugin) => ({
|
|
2789
2957
|
name: toStudioName({
|
|
2790
2958
|
name: (0, _kubb_core.resolveMeta)(plugin).name,
|
|
@@ -2822,6 +2990,7 @@ async function runAgent({ options, openRpc }) {
|
|
|
2822
2990
|
allowConfigEdit: false,
|
|
2823
2991
|
allowInstall: false,
|
|
2824
2992
|
allowUpdate: false,
|
|
2993
|
+
allowSaveInput: false,
|
|
2825
2994
|
allowExec: false,
|
|
2826
2995
|
allowRead: false,
|
|
2827
2996
|
allowReadAst: false,
|