@kubb/studio 5.3.4 → 5.3.5
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 +116 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +116 -39
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +7 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +92 -19
- package/dist/protocol.js +7 -2
- package/dist/protocol.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -673,7 +673,7 @@ async function createAgent({ studioUrl, token, name, machineToken }) {
|
|
|
673
673
|
}
|
|
674
674
|
//#endregion
|
|
675
675
|
//#region package.json
|
|
676
|
-
var version = "5.3.
|
|
676
|
+
var version = "5.3.5";
|
|
677
677
|
//#endregion
|
|
678
678
|
//#region src/hooks.ts
|
|
679
679
|
/**
|
|
@@ -1736,12 +1736,6 @@ async function createSnapshotPackage(files, packageInfo) {
|
|
|
1736
1736
|
//#endregion
|
|
1737
1737
|
//#region src/ws.ts
|
|
1738
1738
|
/**
|
|
1739
|
-
* How many generated files are read from storage at once when building the
|
|
1740
|
-
* `kubb:generation:end` payload. A spec producing thousands of files would otherwise fire one
|
|
1741
|
-
* `storage.readItem` per file simultaneously.
|
|
1742
|
-
*/
|
|
1743
|
-
const FILE_READ_CONCURRENCY = 50;
|
|
1744
|
-
/**
|
|
1745
1739
|
* How long the initial handshake may take before the socket is closed and the reconnect loop
|
|
1746
1740
|
* takes over.
|
|
1747
1741
|
*/
|
|
@@ -1757,6 +1751,12 @@ const require = createRequire(import.meta.url);
|
|
|
1757
1751
|
function relativeStoragePath(root, filePath) {
|
|
1758
1752
|
return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll("\\", "/");
|
|
1759
1753
|
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Inverse of {@link relativeStoragePath}: rebuilds the storage key a relative path came from.
|
|
1756
|
+
*/
|
|
1757
|
+
function absoluteStoragePath(root, relativePath) {
|
|
1758
|
+
return resolve(root, relativePath);
|
|
1759
|
+
}
|
|
1760
1760
|
async function resolvePeerDependencies(names) {
|
|
1761
1761
|
const uniqueNames = [...new Set(names.map(toPackageName))];
|
|
1762
1762
|
const peerDependencies = {};
|
|
@@ -1874,12 +1874,12 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
1874
1874
|
}]
|
|
1875
1875
|
});
|
|
1876
1876
|
});
|
|
1877
|
-
on("kubb:build:end", ({ files, outputDir }) => {
|
|
1877
|
+
on("kubb:build:end", ({ files, config, outputDir }) => {
|
|
1878
1878
|
sendDataMessage({
|
|
1879
1879
|
type: "kubb:build:end",
|
|
1880
1880
|
data: [{
|
|
1881
1881
|
files: files.map((file) => ({
|
|
1882
|
-
path: file.path,
|
|
1882
|
+
path: relativeStoragePath(config.root, file.path),
|
|
1883
1883
|
name: file.name
|
|
1884
1884
|
})),
|
|
1885
1885
|
outputDir
|
|
@@ -1933,25 +1933,18 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
1933
1933
|
});
|
|
1934
1934
|
on("kubb:generation:end", async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {
|
|
1935
1935
|
const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name));
|
|
1936
|
-
const
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
}
|
|
1936
|
+
const keys = await storage.readKeys();
|
|
1937
|
+
const paths = new Set(keys.map((key) => relativeStoragePath(config.root, key)));
|
|
1938
|
+
options.onGenerationEnd?.({
|
|
1939
|
+
storage,
|
|
1940
|
+
root: config.root,
|
|
1941
|
+
paths,
|
|
1942
|
+
peerDependencies,
|
|
1943
|
+
missingDependencies
|
|
1945
1944
|
});
|
|
1946
|
-
options.onGenerationEnd?.(files);
|
|
1947
1945
|
sendDataMessage({
|
|
1948
1946
|
type: "kubb:generation:end",
|
|
1949
|
-
data: [
|
|
1950
|
-
config,
|
|
1951
|
-
storage: options.skipStorage ? {} : files,
|
|
1952
|
-
peerDependencies,
|
|
1953
|
-
missingDependencies
|
|
1954
|
-
}]
|
|
1947
|
+
data: []
|
|
1955
1948
|
});
|
|
1956
1949
|
if (!hrStart) return;
|
|
1957
1950
|
sendDataMessage({
|
|
@@ -2029,6 +2022,11 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
2029
2022
|
//#endregion
|
|
2030
2023
|
//#region src/StudioSession.ts
|
|
2031
2024
|
/**
|
|
2025
|
+
* How many files are read from storage at once when serving `studio:files` or packing a
|
|
2026
|
+
* `studio:snapshot`.
|
|
2027
|
+
*/
|
|
2028
|
+
const FILE_READ_CONCURRENCY = 50;
|
|
2029
|
+
/**
|
|
2032
2030
|
* How long the `agent:connect` handshake may go unacknowledged before `studio:ready` is given up
|
|
2033
2031
|
* on for this open. A Studio that predates the ack never sends one, so this only ever produces a
|
|
2034
2032
|
* warning, not a reconnect.
|
|
@@ -2051,6 +2049,7 @@ function applyStudioDefaults(options) {
|
|
|
2051
2049
|
allowConfigEdit: false,
|
|
2052
2050
|
allowInput: false,
|
|
2053
2051
|
allowExec: false,
|
|
2052
|
+
allowRead: false,
|
|
2054
2053
|
...options.permissions
|
|
2055
2054
|
},
|
|
2056
2055
|
retryInterval: options.retryInterval ?? agentDefaults.retryIntervalMs,
|
|
@@ -2131,6 +2130,12 @@ var StudioSession = class {
|
|
|
2131
2130
|
get #canUseInput() {
|
|
2132
2131
|
return this.#isSandbox || this.#options.permissions.allowInput;
|
|
2133
2132
|
}
|
|
2133
|
+
/**
|
|
2134
|
+
* A sandbox agent always allows reading its output back; a local agent only when opted in.
|
|
2135
|
+
*/
|
|
2136
|
+
get #canRead() {
|
|
2137
|
+
return this.#isSandbox || this.#options.permissions.allowRead;
|
|
2138
|
+
}
|
|
2134
2139
|
async connect() {
|
|
2135
2140
|
const { token, studioUrl, signal, heartbeatInterval, installLogger } = this.#options;
|
|
2136
2141
|
await installLogger?.(this.#hooks);
|
|
@@ -2224,7 +2229,8 @@ var StudioSession = class {
|
|
|
2224
2229
|
...permissions,
|
|
2225
2230
|
allowWrite: this.#canWrite,
|
|
2226
2231
|
allowInput: this.#canUseInput,
|
|
2227
|
-
allowConfigEdit: this.#canEditConfig
|
|
2232
|
+
allowConfigEdit: this.#canEditConfig,
|
|
2233
|
+
allowRead: this.#canRead
|
|
2228
2234
|
}
|
|
2229
2235
|
}
|
|
2230
2236
|
});
|
|
@@ -2366,6 +2372,9 @@ var StudioSession = class {
|
|
|
2366
2372
|
case "studio:snapshot":
|
|
2367
2373
|
await this.#handleSnapshot(ws, data, command);
|
|
2368
2374
|
return;
|
|
2375
|
+
case "studio:files":
|
|
2376
|
+
await this.#handleFiles(ws, data, command);
|
|
2377
|
+
return;
|
|
2369
2378
|
}
|
|
2370
2379
|
}
|
|
2371
2380
|
async #handleGenerate(ws, data, command) {
|
|
@@ -2389,13 +2398,10 @@ var StudioSession = class {
|
|
|
2389
2398
|
await this.#warn(`Ignored the spec from Studio; set ${remedy} to generate from it`);
|
|
2390
2399
|
}
|
|
2391
2400
|
const resolvedPlugins = plugins ?? config.plugins;
|
|
2392
|
-
|
|
2393
|
-
const detach = [setupHookListener(this.#hooks, root), setupEventsStream(ws, this.#hooks, data.jobId, {
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
generatedFiles = files;
|
|
2397
|
-
}
|
|
2398
|
-
})];
|
|
2401
|
+
this.#lastGeneration = void 0;
|
|
2402
|
+
const detach = [setupHookListener(this.#hooks, root), setupEventsStream(ws, this.#hooks, data.jobId, { onGenerationEnd: (result) => {
|
|
2403
|
+
this.#lastGeneration = result;
|
|
2404
|
+
} })];
|
|
2399
2405
|
try {
|
|
2400
2406
|
await generate({
|
|
2401
2407
|
config: {
|
|
@@ -2416,7 +2422,6 @@ var StudioSession = class {
|
|
|
2416
2422
|
});
|
|
2417
2423
|
} finally {
|
|
2418
2424
|
for (const remove of detach) remove();
|
|
2419
|
-
this.#lastGeneration = generatedFiles;
|
|
2420
2425
|
}
|
|
2421
2426
|
await this.#hooks.callHook("studio:command:end", {
|
|
2422
2427
|
command,
|
|
@@ -2499,23 +2504,39 @@ var StudioSession = class {
|
|
|
2499
2504
|
refuse("a sandbox agent has no project to build a package from");
|
|
2500
2505
|
return;
|
|
2501
2506
|
}
|
|
2502
|
-
const { name, version,
|
|
2507
|
+
const { name, version, bundledDependencies, uploadPath } = data.payload;
|
|
2503
2508
|
if (!name || !version || !uploadPath) {
|
|
2504
2509
|
await this.#warn("Ignored snapshot: the message was missing required fields");
|
|
2505
2510
|
refuse("the message was missing required fields");
|
|
2506
2511
|
return;
|
|
2507
2512
|
}
|
|
2508
|
-
const
|
|
2509
|
-
if (!
|
|
2513
|
+
const generation = this.#lastGeneration;
|
|
2514
|
+
if (!generation) {
|
|
2510
2515
|
await this.#warn("Ignored snapshot: no prior generation to pack");
|
|
2511
2516
|
refuse("no prior generation exists to pack, run a generation first");
|
|
2512
2517
|
return;
|
|
2513
2518
|
}
|
|
2519
|
+
const bundled = new Set(bundledDependencies ?? []);
|
|
2520
|
+
const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency));
|
|
2521
|
+
if (missing.length) {
|
|
2522
|
+
await this.#warn(`Ignored snapshot: missing dependencies: ${missing.join(", ")}`);
|
|
2523
|
+
refuse(`missing dependencies: ${missing.join(", ")}`);
|
|
2524
|
+
return;
|
|
2525
|
+
}
|
|
2514
2526
|
try {
|
|
2527
|
+
const files = {};
|
|
2528
|
+
await inParallel({
|
|
2529
|
+
items: [...generation.paths],
|
|
2530
|
+
limit: FILE_READ_CONCURRENCY,
|
|
2531
|
+
run: async (relativePath) => {
|
|
2532
|
+
const content = await generation.storage.readItem(absoluteStoragePath(generation.root, relativePath));
|
|
2533
|
+
if (content !== null) files[relativePath] = content;
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2515
2536
|
const { bytes, integrity } = await createSnapshotPackage(files, {
|
|
2516
2537
|
name,
|
|
2517
2538
|
version,
|
|
2518
|
-
peerDependencies: peerDependencies
|
|
2539
|
+
peerDependencies: generation.peerDependencies
|
|
2519
2540
|
});
|
|
2520
2541
|
const { token, studioUrl } = this.#options;
|
|
2521
2542
|
const redirect = await fetch(new URL(uploadPath, studioUrl), {
|
|
@@ -2535,7 +2556,8 @@ var StudioSession = class {
|
|
|
2535
2556
|
jobId: data.jobId,
|
|
2536
2557
|
payload: {
|
|
2537
2558
|
status: "ok",
|
|
2538
|
-
integrity
|
|
2559
|
+
integrity,
|
|
2560
|
+
peerDependencies: generation.peerDependencies
|
|
2539
2561
|
}
|
|
2540
2562
|
});
|
|
2541
2563
|
await this.#hooks.callHook("studio:command:end", {
|
|
@@ -2547,6 +2569,61 @@ var StudioSession = class {
|
|
|
2547
2569
|
refuse(getErrorMessage(error));
|
|
2548
2570
|
}
|
|
2549
2571
|
}
|
|
2572
|
+
async #handleFiles(ws, data, command) {
|
|
2573
|
+
const { client } = this.#options;
|
|
2574
|
+
const refuse = (message) => sendAgentMessage(ws, {
|
|
2575
|
+
type: "agent:files",
|
|
2576
|
+
jobId: data.jobId,
|
|
2577
|
+
payload: {
|
|
2578
|
+
status: "error",
|
|
2579
|
+
message
|
|
2580
|
+
}
|
|
2581
|
+
});
|
|
2582
|
+
if (!this.#canRead) {
|
|
2583
|
+
await this.#warn("Ignored files: reading generated files was not granted");
|
|
2584
|
+
refuse(`the agent was not granted permission to read generated files; set ${client?.kind === "cli" ? "--allow-read, or answer yes when kubb studio asks," : "KUBB_AGENT_ALLOW_READ=true"} to allow it`);
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
if (!Array.isArray(data.payload?.paths)) {
|
|
2588
|
+
await this.#warn("Ignored files: the message carried no paths");
|
|
2589
|
+
refuse("the message carried no paths");
|
|
2590
|
+
return;
|
|
2591
|
+
}
|
|
2592
|
+
const { paths } = data.payload;
|
|
2593
|
+
if (paths.length > 50) {
|
|
2594
|
+
await this.#warn(`Ignored files: requested ${paths.length} paths, more than the 50 allowed per request`);
|
|
2595
|
+
refuse(`at most 50 paths may be requested at once`);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
const generation = this.#lastGeneration;
|
|
2599
|
+
if (!generation) {
|
|
2600
|
+
await this.#warn("Ignored files: no prior generation to read from");
|
|
2601
|
+
refuse("no prior generation to read from, run a generation first");
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
const requested = paths.filter((path) => generation.paths.has(path));
|
|
2605
|
+
const files = {};
|
|
2606
|
+
await inParallel({
|
|
2607
|
+
items: requested,
|
|
2608
|
+
limit: FILE_READ_CONCURRENCY,
|
|
2609
|
+
run: async (path) => {
|
|
2610
|
+
const content = await generation.storage.readItem(absoluteStoragePath(generation.root, path));
|
|
2611
|
+
if (content !== null) files[path] = content;
|
|
2612
|
+
}
|
|
2613
|
+
});
|
|
2614
|
+
sendAgentMessage(ws, {
|
|
2615
|
+
type: "agent:files",
|
|
2616
|
+
jobId: data.jobId,
|
|
2617
|
+
payload: {
|
|
2618
|
+
status: "ok",
|
|
2619
|
+
files
|
|
2620
|
+
}
|
|
2621
|
+
});
|
|
2622
|
+
await this.#hooks.callHook("studio:command:end", {
|
|
2623
|
+
command,
|
|
2624
|
+
info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2550
2627
|
};
|
|
2551
2628
|
//#endregion
|
|
2552
2629
|
//#region src/client.ts
|