@kubb/studio 5.12.2 → 5.12.3
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 +89 -22
- 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 +90 -23
- package/dist/index.js.map +1 -1
- package/dist/{protocol-DhNrwhqp.d.ts → protocol-WYICLbSg.d.cts} +36 -7
- package/dist/{protocol-DhNrwhqp.d.cts → protocol-WYICLbSg.d.ts} +36 -7
- package/dist/protocol.cjs +13 -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 +13 -1
- package/dist/protocol.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as SnapshotUploadMetadata, R as PublishedSnapshot, a as AgentConnectInfo, c as AgentPermissions, f as ConfigEditOutcome, j as InstalledExtensions, s as AgentPermission, u as ConfigEdit } from "./protocol-WYICLbSg.cjs";
|
|
2
2
|
import { Config, Hookable, KubbHooks } from "@kubb/core";
|
|
3
3
|
//#region src/connect.d.ts
|
|
4
4
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as SnapshotUploadMetadata, R as PublishedSnapshot, a as AgentConnectInfo, c as AgentPermissions, f as ConfigEditOutcome, j as InstalledExtensions, s as AgentPermission, u as ConfigEdit } from "./protocol-WYICLbSg.js";
|
|
2
2
|
import { Config, Hookable, KubbHooks } from "@kubb/core";
|
|
3
3
|
//#region src/connect.d.ts
|
|
4
4
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { AGENT_INSTANCE_HEADER, AGENT_SOCKET_PATH, AgentCloseCode, AgentMessage, AgentPermission, MAX_SNAPSHOT_BYTES, generationEventTypes } from "./protocol.js";
|
|
1
|
+
import { AGENT_INSTANCE_HEADER, AGENT_SOCKET_PATH, AgentCloseCode, AgentMessage, AgentPermission, ConfigEditOperation, MAX_SNAPSHOT_BYTES, generationEventTypes } from "./protocol.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { createHash, hash, randomUUID } from "node:crypto";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
|
+
import { mkdir, mkdir as mkdir$1, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
5
6
|
import { tmpdir } from "node:os";
|
|
6
7
|
import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
7
8
|
import process$1 from "node:process";
|
|
8
9
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
9
10
|
import { x } from "tinyexec";
|
|
10
|
-
import { mkdir, mkdir as mkdir$1, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import { Diagnostics, Hookable, cacheStorage, checkoutGitInput, createKubb, fsStorage, getInputKind, isPackageName, isPublicAddress, readInstalledPackages, resolveCacheDir, resolveMeta } from "@kubb/core";
|
|
12
12
|
import { RpcTarget, newWebSocketRpcSession } from "capnweb";
|
|
13
13
|
import { detectCodeFormat, parseExpression, parseModule } from "magicast";
|
|
@@ -202,7 +202,7 @@ function getElapsedMs(hrStart) {
|
|
|
202
202
|
}
|
|
203
203
|
//#endregion
|
|
204
204
|
//#region package.json
|
|
205
|
-
var version = "5.12.
|
|
205
|
+
var version = "5.12.3";
|
|
206
206
|
//#endregion
|
|
207
207
|
//#region src/resolveConfig.ts
|
|
208
208
|
/**
|
|
@@ -600,13 +600,7 @@ const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
|
600
600
|
* line count, so `enable-plugin` restores exactly those lines and not whatever comments follow.
|
|
601
601
|
*/
|
|
602
602
|
const DISABLED_MARKER = /^\/\/ kubb:disabled (.+)\s+(\d+)$/;
|
|
603
|
-
const OPERATIONS =
|
|
604
|
-
"set",
|
|
605
|
-
"remove",
|
|
606
|
-
"add-plugin",
|
|
607
|
-
"disable-plugin",
|
|
608
|
-
"enable-plugin"
|
|
609
|
-
]);
|
|
603
|
+
const OPERATIONS = new Set(Object.values(ConfigEditOperation));
|
|
610
604
|
const KINDS = /* @__PURE__ */ new Set([
|
|
611
605
|
"plugin",
|
|
612
606
|
"adapter",
|
|
@@ -617,13 +611,14 @@ const KINDS = /* @__PURE__ */ new Set([
|
|
|
617
611
|
* The edit crossed the wire, so its declared type is a promise the sender may not have kept.
|
|
618
612
|
*/
|
|
619
613
|
function invalidEdit(edit) {
|
|
620
|
-
const { operation, plugin, config, path, kind } = typeof edit === "object" && edit !== null ? edit : {};
|
|
614
|
+
const { operation, plugin, config, path, kind, input } = typeof edit === "object" && edit !== null ? edit : {};
|
|
621
615
|
if (typeof operation !== "string" || !OPERATIONS.has(operation)) return `unknown operation ${JSON.stringify(operation)}`;
|
|
616
|
+
if (config !== void 0 && typeof config !== "string" && !Number.isSafeInteger(config)) return "config must name an entry or give its index";
|
|
617
|
+
if (operation === ConfigEditOperation.SET_INPUT) return typeof input === "string" && input.trim() ? void 0 : "the edit names no input";
|
|
622
618
|
if (typeof plugin !== "string" || !plugin) return "the edit names no plugin";
|
|
623
619
|
if (kind !== void 0 && (typeof kind !== "string" || !KINDS.has(kind))) return `unknown kind ${JSON.stringify(kind)}`;
|
|
624
|
-
if (kind !== void 0 && operation !==
|
|
625
|
-
if (
|
|
626
|
-
if (operation === "set" || operation === "remove") {
|
|
620
|
+
if (kind !== void 0 && operation !== ConfigEditOperation.SET && operation !== ConfigEditOperation.REMOVE) return `${operation} edits the plugins array, so it takes no kind`;
|
|
621
|
+
if (operation === ConfigEditOperation.SET || operation === ConfigEditOperation.REMOVE) {
|
|
627
622
|
if (!Array.isArray(path) || !path.every((key) => typeof key === "string" && key)) return "path must be a list of option names";
|
|
628
623
|
if (!path.length) return "no option path given";
|
|
629
624
|
const unsafe = path.find((key) => !isSafeKey(key));
|
|
@@ -1064,6 +1059,15 @@ function applySet(source, call, path, value, quote) {
|
|
|
1064
1059
|
}
|
|
1065
1060
|
return { source: splice(source, rangeOf(target.value).start, rangeOf(target.value).end, printValue(value, quote)) };
|
|
1066
1061
|
}
|
|
1062
|
+
/** Writes the config's top-level `input` as a string literal, replacing what it held. */
|
|
1063
|
+
function applySetInput(source, config, input, quote) {
|
|
1064
|
+
const value = printValue(input, quote);
|
|
1065
|
+
const found = findMember(config, "input");
|
|
1066
|
+
if (!found) return { source: insertItem(source, config, `input: ${value}`) };
|
|
1067
|
+
if (found.member.type !== "ObjectProperty") return { reason: "input is not a plain property" };
|
|
1068
|
+
if (isShadowed(config, found.index)) return { reason: "a spread after input could override it" };
|
|
1069
|
+
return { source: splice(source, rangeOf(found.member.value).start, rangeOf(found.member.value).end, value) };
|
|
1070
|
+
}
|
|
1067
1071
|
/**
|
|
1068
1072
|
* Deletes the option at `path`, falling the plugin back to its default for it. An option the file
|
|
1069
1073
|
* does not set already has its default, so removing it changes nothing.
|
|
@@ -1160,7 +1164,8 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1160
1164
|
const findConfig = (entries) => typeof ref === "string" ? entries.configs.find((entry) => configName(entry) === ref) : entries.configs[ref ?? 0];
|
|
1161
1165
|
const config = findConfig(parsed);
|
|
1162
1166
|
if (!config) return { reason: `no config entry found for ${JSON.stringify(ref)}` };
|
|
1163
|
-
if (edit.operation ===
|
|
1167
|
+
if (edit.operation === ConfigEditOperation.SET_INPUT) return applySetInput(source, config, edit.input, quote);
|
|
1168
|
+
if (edit.operation === ConfigEditOperation.DISABLE_PLUGIN) {
|
|
1164
1169
|
const result = disablePlugin(source, parsed.mod, config, edit.plugin);
|
|
1165
1170
|
if (!("reason" in result) || !result.reason.includes("shares a line")) return result;
|
|
1166
1171
|
const expanded = expandPluginsArray(source, config);
|
|
@@ -1168,8 +1173,8 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1168
1173
|
const reparsedConfig = reparsed && !("reason" in reparsed) ? findConfig(reparsed) : void 0;
|
|
1169
1174
|
return expanded !== void 0 && reparsed && !("reason" in reparsed) && reparsedConfig ? disablePlugin(expanded, reparsed.mod, reparsedConfig, edit.plugin) : result;
|
|
1170
1175
|
}
|
|
1171
|
-
if (edit.operation ===
|
|
1172
|
-
if (edit.operation ===
|
|
1176
|
+
if (edit.operation === ConfigEditOperation.ENABLE_PLUGIN) return enablePlugin(source, config, edit.plugin);
|
|
1177
|
+
if (edit.operation === ConfigEditOperation.ADD_PLUGIN) return applyAddPlugin({
|
|
1173
1178
|
source,
|
|
1174
1179
|
parsed,
|
|
1175
1180
|
config,
|
|
@@ -1185,7 +1190,7 @@ function applyEdit({ source, edit, quote, extensions }) {
|
|
|
1185
1190
|
if (kind === "adapter") return { reason: `${edit.plugin} is not the config's adapter` };
|
|
1186
1191
|
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` };
|
|
1187
1192
|
}
|
|
1188
|
-
return edit.operation ===
|
|
1193
|
+
return edit.operation === ConfigEditOperation.SET ? applySet(source, call, edit.path, edit.value, quote) : applyRemove(source, call, edit.path);
|
|
1189
1194
|
}
|
|
1190
1195
|
/**
|
|
1191
1196
|
* Applies edits to a `kubb.config.ts` in place. Each edit rewrites only the text it targets, so
|
|
@@ -2123,6 +2128,7 @@ var Agent = class extends RpcTarget {
|
|
|
2123
2128
|
allowConfigEdit: false,
|
|
2124
2129
|
allowInstall: false,
|
|
2125
2130
|
allowUpdate: false,
|
|
2131
|
+
allowSaveInput: false,
|
|
2126
2132
|
allowRead: true,
|
|
2127
2133
|
allowReadAst: true
|
|
2128
2134
|
} : permissions;
|
|
@@ -2148,13 +2154,13 @@ var Agent = class extends RpcTarget {
|
|
|
2148
2154
|
permission
|
|
2149
2155
|
});
|
|
2150
2156
|
}
|
|
2151
|
-
async #refuse({ message, permission }) {
|
|
2157
|
+
async #refuse({ message, permission, code = Diagnostics.code.unknown }) {
|
|
2152
2158
|
await this.#warn({
|
|
2153
2159
|
message,
|
|
2154
2160
|
permission
|
|
2155
2161
|
});
|
|
2156
2162
|
throw new Diagnostics.Error({
|
|
2157
|
-
code
|
|
2163
|
+
code,
|
|
2158
2164
|
severity: "error",
|
|
2159
2165
|
message
|
|
2160
2166
|
});
|
|
@@ -2346,7 +2352,13 @@ var Agent = class extends RpcTarget {
|
|
|
2346
2352
|
});
|
|
2347
2353
|
const activePlugins = plugins ?? config.plugins;
|
|
2348
2354
|
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" });
|
|
2349
|
-
const checkout = this.#managed && studioInput !== void 0 && getInputKind(studioInput) === "git" ? await checkoutGitInput(studioInput, signal, {
|
|
2355
|
+
const checkout = this.#managed && studioInput !== void 0 && getInputKind(studioInput) === "git" ? await checkoutGitInput(studioInput, signal, {
|
|
2356
|
+
publicOnly: true,
|
|
2357
|
+
authorization: input.gitAuth?.authorization
|
|
2358
|
+
}).catch((error) => this.#refuse({
|
|
2359
|
+
message: getErrorMessage(error),
|
|
2360
|
+
code: Diagnostics.code.gitInputFailed
|
|
2361
|
+
})) : void 0;
|
|
2350
2362
|
const run = () => generate({
|
|
2351
2363
|
config: {
|
|
2352
2364
|
...config,
|
|
@@ -2420,11 +2432,29 @@ var Agent = class extends RpcTarget {
|
|
|
2420
2432
|
if (busy) return refuse(busy);
|
|
2421
2433
|
this.#session.saving = true;
|
|
2422
2434
|
try {
|
|
2423
|
-
const
|
|
2435
|
+
const kept = [];
|
|
2436
|
+
for (const edit of edits) kept.push(await this.#keepInput(edit));
|
|
2437
|
+
const written = await writeConfigEdits({
|
|
2424
2438
|
filePath: configFile,
|
|
2425
|
-
edits,
|
|
2439
|
+
edits: kept.flatMap((edit) => "reason" in edit ? [] : [edit]),
|
|
2426
2440
|
extensions: await this.#installedExtensions()
|
|
2427
2441
|
});
|
|
2442
|
+
let next = 0;
|
|
2443
|
+
const outcomes = kept.map((edit, index) => "reason" in edit ? {
|
|
2444
|
+
edit: edits[index],
|
|
2445
|
+
applied: false,
|
|
2446
|
+
reason: edit.reason
|
|
2447
|
+
} : {
|
|
2448
|
+
...written.outcomes[next++],
|
|
2449
|
+
edit: edits[index]
|
|
2450
|
+
});
|
|
2451
|
+
let wroteDocument = false;
|
|
2452
|
+
for (const [index, edit] of kept.entries()) {
|
|
2453
|
+
if (!("document" in edit) || !edit.document || !outcomes[index].applied) continue;
|
|
2454
|
+
await writeFile(path.join(this.#options.root, edit.document.fileName), edit.document.text, "utf-8");
|
|
2455
|
+
wroteDocument = true;
|
|
2456
|
+
}
|
|
2457
|
+
const changed = written.changed || wroteDocument;
|
|
2428
2458
|
const applied = outcomes.filter((outcome) => outcome.applied).length;
|
|
2429
2459
|
await this.#logCommand("saveConfig", `applied ${applied}/${outcomes.length} edits to ${configPath}`, input.requestedBy);
|
|
2430
2460
|
return {
|
|
@@ -2439,6 +2469,33 @@ var Agent = class extends RpcTarget {
|
|
|
2439
2469
|
}
|
|
2440
2470
|
}
|
|
2441
2471
|
/**
|
|
2472
|
+
* Readies a `set-input` edit for the config file: a document is pointed at `openapi.yaml` (`openapi.json`
|
|
2473
|
+
* for JSON) in the project, one file per config entry, for `saveConfig` to write once the edit applies.
|
|
2474
|
+
* A managed agent refuses a file path, as in a generation.
|
|
2475
|
+
*/
|
|
2476
|
+
async #keepInput(edit) {
|
|
2477
|
+
if (edit?.operation !== ConfigEditOperation.SET_INPUT || typeof edit.input !== "string" || !edit.input.trim()) return edit;
|
|
2478
|
+
if (!this.#permissions.allowSaveInput) {
|
|
2479
|
+
await this.#warn({
|
|
2480
|
+
message: "Ignored the spec to save: saving the input was not granted",
|
|
2481
|
+
permission: AgentPermission.SAVE_INPUT
|
|
2482
|
+
});
|
|
2483
|
+
return { reason: "the agent was not granted permission to save the input" };
|
|
2484
|
+
}
|
|
2485
|
+
const kind = getInputKind(edit.input);
|
|
2486
|
+
if (this.#managed && !MANAGED_INPUT_KINDS.has(kind)) return { reason: AgentMessage.MANAGED_INPUT_KIND };
|
|
2487
|
+
if (kind !== "inline") return edit;
|
|
2488
|
+
const fileName = `openapi${edit.config === void 0 || edit.config === 0 ? "" : `-${String(edit.config).replace(/[^\w.-]/g, "-")}`}.${isJson(edit.input) ? "json" : "yaml"}`;
|
|
2489
|
+
return {
|
|
2490
|
+
...edit,
|
|
2491
|
+
input: `./${fileName}`,
|
|
2492
|
+
document: {
|
|
2493
|
+
fileName,
|
|
2494
|
+
text: edit.input
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
/**
|
|
2442
2499
|
* Installs a registry extension into the project with `kubb add <id> --yes`, which also adds a
|
|
2443
2500
|
* plugin to `kubb.config.ts`. Studio calls `connect()` again afterwards to see the result.
|
|
2444
2501
|
*/
|
|
@@ -2739,6 +2796,15 @@ function pickConfig(configs, ref) {
|
|
|
2739
2796
|
if (ref !== void 0 && !Number.isSafeInteger(ref)) return void 0;
|
|
2740
2797
|
return configs[ref ?? 0];
|
|
2741
2798
|
}
|
|
2799
|
+
/** Whether `text` parses as JSON, so a pasted document is saved under the matching extension. */
|
|
2800
|
+
function isJson(text) {
|
|
2801
|
+
try {
|
|
2802
|
+
JSON.parse(text);
|
|
2803
|
+
return true;
|
|
2804
|
+
} catch {
|
|
2805
|
+
return false;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2742
2808
|
/** The input path or URL Studio may show: no inline document, and no credentials from a URL. */
|
|
2743
2809
|
function reportedInput(input) {
|
|
2744
2810
|
if (typeof input !== "string") return void 0;
|
|
@@ -2794,6 +2860,7 @@ async function runAgent({ options, openRpc }) {
|
|
|
2794
2860
|
allowConfigEdit: false,
|
|
2795
2861
|
allowInstall: false,
|
|
2796
2862
|
allowUpdate: false,
|
|
2863
|
+
allowSaveInput: false,
|
|
2797
2864
|
allowExec: false,
|
|
2798
2865
|
allowRead: false,
|
|
2799
2866
|
allowReadAst: false,
|