@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.cjs
CHANGED
|
@@ -678,7 +678,7 @@ async function createAgent({ studioUrl, token, name, machineToken }) {
|
|
|
678
678
|
}
|
|
679
679
|
//#endregion
|
|
680
680
|
//#region package.json
|
|
681
|
-
var version = "5.3.
|
|
681
|
+
var version = "5.3.5";
|
|
682
682
|
//#endregion
|
|
683
683
|
//#region src/hooks.ts
|
|
684
684
|
/**
|
|
@@ -1741,12 +1741,6 @@ async function createSnapshotPackage(files, packageInfo) {
|
|
|
1741
1741
|
//#endregion
|
|
1742
1742
|
//#region src/ws.ts
|
|
1743
1743
|
/**
|
|
1744
|
-
* How many generated files are read from storage at once when building the
|
|
1745
|
-
* `kubb:generation:end` payload. A spec producing thousands of files would otherwise fire one
|
|
1746
|
-
* `storage.readItem` per file simultaneously.
|
|
1747
|
-
*/
|
|
1748
|
-
const FILE_READ_CONCURRENCY = 50;
|
|
1749
|
-
/**
|
|
1750
1744
|
* How long the initial handshake may take before the socket is closed and the reconnect loop
|
|
1751
1745
|
* takes over.
|
|
1752
1746
|
*/
|
|
@@ -1762,6 +1756,12 @@ const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__
|
|
|
1762
1756
|
function relativeStoragePath(root, filePath) {
|
|
1763
1757
|
return ((0, node_path.isAbsolute)(filePath) ? (0, node_path.relative)((0, node_path.resolve)(root), filePath) : filePath).replaceAll("\\", "/");
|
|
1764
1758
|
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Inverse of {@link relativeStoragePath}: rebuilds the storage key a relative path came from.
|
|
1761
|
+
*/
|
|
1762
|
+
function absoluteStoragePath(root, relativePath) {
|
|
1763
|
+
return (0, node_path.resolve)(root, relativePath);
|
|
1764
|
+
}
|
|
1765
1765
|
async function resolvePeerDependencies(names) {
|
|
1766
1766
|
const uniqueNames = [...new Set(names.map(toPackageName))];
|
|
1767
1767
|
const peerDependencies = {};
|
|
@@ -1879,12 +1879,12 @@ function setupEventsStream(ws$5, hooks, jobId, options = {}) {
|
|
|
1879
1879
|
}]
|
|
1880
1880
|
});
|
|
1881
1881
|
});
|
|
1882
|
-
on("kubb:build:end", ({ files, outputDir }) => {
|
|
1882
|
+
on("kubb:build:end", ({ files, config, outputDir }) => {
|
|
1883
1883
|
sendDataMessage({
|
|
1884
1884
|
type: "kubb:build:end",
|
|
1885
1885
|
data: [{
|
|
1886
1886
|
files: files.map((file) => ({
|
|
1887
|
-
path: file.path,
|
|
1887
|
+
path: relativeStoragePath(config.root, file.path),
|
|
1888
1888
|
name: file.name
|
|
1889
1889
|
})),
|
|
1890
1890
|
outputDir
|
|
@@ -1938,25 +1938,18 @@ function setupEventsStream(ws$5, hooks, jobId, options = {}) {
|
|
|
1938
1938
|
});
|
|
1939
1939
|
on("kubb:generation:end", async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {
|
|
1940
1940
|
const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name));
|
|
1941
|
-
const
|
|
1942
|
-
const
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
}
|
|
1941
|
+
const keys = await storage.readKeys();
|
|
1942
|
+
const paths = new Set(keys.map((key) => relativeStoragePath(config.root, key)));
|
|
1943
|
+
options.onGenerationEnd?.({
|
|
1944
|
+
storage,
|
|
1945
|
+
root: config.root,
|
|
1946
|
+
paths,
|
|
1947
|
+
peerDependencies,
|
|
1948
|
+
missingDependencies
|
|
1950
1949
|
});
|
|
1951
|
-
options.onGenerationEnd?.(files);
|
|
1952
1950
|
sendDataMessage({
|
|
1953
1951
|
type: "kubb:generation:end",
|
|
1954
|
-
data: [
|
|
1955
|
-
config,
|
|
1956
|
-
storage: options.skipStorage ? {} : files,
|
|
1957
|
-
peerDependencies,
|
|
1958
|
-
missingDependencies
|
|
1959
|
-
}]
|
|
1952
|
+
data: []
|
|
1960
1953
|
});
|
|
1961
1954
|
if (!hrStart) return;
|
|
1962
1955
|
sendDataMessage({
|
|
@@ -2034,6 +2027,11 @@ function setupEventsStream(ws$5, hooks, jobId, options = {}) {
|
|
|
2034
2027
|
//#endregion
|
|
2035
2028
|
//#region src/StudioSession.ts
|
|
2036
2029
|
/**
|
|
2030
|
+
* How many files are read from storage at once when serving `studio:files` or packing a
|
|
2031
|
+
* `studio:snapshot`.
|
|
2032
|
+
*/
|
|
2033
|
+
const FILE_READ_CONCURRENCY = 50;
|
|
2034
|
+
/**
|
|
2037
2035
|
* How long the `agent:connect` handshake may go unacknowledged before `studio:ready` is given up
|
|
2038
2036
|
* on for this open. A Studio that predates the ack never sends one, so this only ever produces a
|
|
2039
2037
|
* warning, not a reconnect.
|
|
@@ -2056,6 +2054,7 @@ function applyStudioDefaults(options) {
|
|
|
2056
2054
|
allowConfigEdit: false,
|
|
2057
2055
|
allowInput: false,
|
|
2058
2056
|
allowExec: false,
|
|
2057
|
+
allowRead: false,
|
|
2059
2058
|
...options.permissions
|
|
2060
2059
|
},
|
|
2061
2060
|
retryInterval: options.retryInterval ?? agentDefaults.retryIntervalMs,
|
|
@@ -2136,6 +2135,12 @@ var StudioSession = class {
|
|
|
2136
2135
|
get #canUseInput() {
|
|
2137
2136
|
return this.#isSandbox || this.#options.permissions.allowInput;
|
|
2138
2137
|
}
|
|
2138
|
+
/**
|
|
2139
|
+
* A sandbox agent always allows reading its output back; a local agent only when opted in.
|
|
2140
|
+
*/
|
|
2141
|
+
get #canRead() {
|
|
2142
|
+
return this.#isSandbox || this.#options.permissions.allowRead;
|
|
2143
|
+
}
|
|
2139
2144
|
async connect() {
|
|
2140
2145
|
const { token, studioUrl, signal, heartbeatInterval, installLogger } = this.#options;
|
|
2141
2146
|
await installLogger?.(this.#hooks);
|
|
@@ -2229,7 +2234,8 @@ var StudioSession = class {
|
|
|
2229
2234
|
...permissions,
|
|
2230
2235
|
allowWrite: this.#canWrite,
|
|
2231
2236
|
allowInput: this.#canUseInput,
|
|
2232
|
-
allowConfigEdit: this.#canEditConfig
|
|
2237
|
+
allowConfigEdit: this.#canEditConfig,
|
|
2238
|
+
allowRead: this.#canRead
|
|
2233
2239
|
}
|
|
2234
2240
|
}
|
|
2235
2241
|
});
|
|
@@ -2371,6 +2377,9 @@ var StudioSession = class {
|
|
|
2371
2377
|
case "studio:snapshot":
|
|
2372
2378
|
await this.#handleSnapshot(ws, data, command);
|
|
2373
2379
|
return;
|
|
2380
|
+
case "studio:files":
|
|
2381
|
+
await this.#handleFiles(ws, data, command);
|
|
2382
|
+
return;
|
|
2374
2383
|
}
|
|
2375
2384
|
}
|
|
2376
2385
|
async #handleGenerate(ws, data, command) {
|
|
@@ -2394,13 +2403,10 @@ var StudioSession = class {
|
|
|
2394
2403
|
await this.#warn(`Ignored the spec from Studio; set ${remedy} to generate from it`);
|
|
2395
2404
|
}
|
|
2396
2405
|
const resolvedPlugins = plugins ?? config.plugins;
|
|
2397
|
-
|
|
2398
|
-
const detach = [setupHookListener(this.#hooks, root), setupEventsStream(ws, this.#hooks, data.jobId, {
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
generatedFiles = files;
|
|
2402
|
-
}
|
|
2403
|
-
})];
|
|
2406
|
+
this.#lastGeneration = void 0;
|
|
2407
|
+
const detach = [setupHookListener(this.#hooks, root), setupEventsStream(ws, this.#hooks, data.jobId, { onGenerationEnd: (result) => {
|
|
2408
|
+
this.#lastGeneration = result;
|
|
2409
|
+
} })];
|
|
2404
2410
|
try {
|
|
2405
2411
|
await generate({
|
|
2406
2412
|
config: {
|
|
@@ -2421,7 +2427,6 @@ var StudioSession = class {
|
|
|
2421
2427
|
});
|
|
2422
2428
|
} finally {
|
|
2423
2429
|
for (const remove of detach) remove();
|
|
2424
|
-
this.#lastGeneration = generatedFiles;
|
|
2425
2430
|
}
|
|
2426
2431
|
await this.#hooks.callHook("studio:command:end", {
|
|
2427
2432
|
command,
|
|
@@ -2504,23 +2509,39 @@ var StudioSession = class {
|
|
|
2504
2509
|
refuse("a sandbox agent has no project to build a package from");
|
|
2505
2510
|
return;
|
|
2506
2511
|
}
|
|
2507
|
-
const { name, version,
|
|
2512
|
+
const { name, version, bundledDependencies, uploadPath } = data.payload;
|
|
2508
2513
|
if (!name || !version || !uploadPath) {
|
|
2509
2514
|
await this.#warn("Ignored snapshot: the message was missing required fields");
|
|
2510
2515
|
refuse("the message was missing required fields");
|
|
2511
2516
|
return;
|
|
2512
2517
|
}
|
|
2513
|
-
const
|
|
2514
|
-
if (!
|
|
2518
|
+
const generation = this.#lastGeneration;
|
|
2519
|
+
if (!generation) {
|
|
2515
2520
|
await this.#warn("Ignored snapshot: no prior generation to pack");
|
|
2516
2521
|
refuse("no prior generation exists to pack, run a generation first");
|
|
2517
2522
|
return;
|
|
2518
2523
|
}
|
|
2524
|
+
const bundled = new Set(bundledDependencies ?? []);
|
|
2525
|
+
const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency));
|
|
2526
|
+
if (missing.length) {
|
|
2527
|
+
await this.#warn(`Ignored snapshot: missing dependencies: ${missing.join(", ")}`);
|
|
2528
|
+
refuse(`missing dependencies: ${missing.join(", ")}`);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2519
2531
|
try {
|
|
2532
|
+
const files = {};
|
|
2533
|
+
await inParallel({
|
|
2534
|
+
items: [...generation.paths],
|
|
2535
|
+
limit: FILE_READ_CONCURRENCY,
|
|
2536
|
+
run: async (relativePath) => {
|
|
2537
|
+
const content = await generation.storage.readItem(absoluteStoragePath(generation.root, relativePath));
|
|
2538
|
+
if (content !== null) files[relativePath] = content;
|
|
2539
|
+
}
|
|
2540
|
+
});
|
|
2520
2541
|
const { bytes, integrity } = await createSnapshotPackage(files, {
|
|
2521
2542
|
name,
|
|
2522
2543
|
version,
|
|
2523
|
-
peerDependencies: peerDependencies
|
|
2544
|
+
peerDependencies: generation.peerDependencies
|
|
2524
2545
|
});
|
|
2525
2546
|
const { token, studioUrl } = this.#options;
|
|
2526
2547
|
const redirect = await fetch(new URL(uploadPath, studioUrl), {
|
|
@@ -2540,7 +2561,8 @@ var StudioSession = class {
|
|
|
2540
2561
|
jobId: data.jobId,
|
|
2541
2562
|
payload: {
|
|
2542
2563
|
status: "ok",
|
|
2543
|
-
integrity
|
|
2564
|
+
integrity,
|
|
2565
|
+
peerDependencies: generation.peerDependencies
|
|
2544
2566
|
}
|
|
2545
2567
|
});
|
|
2546
2568
|
await this.#hooks.callHook("studio:command:end", {
|
|
@@ -2552,6 +2574,61 @@ var StudioSession = class {
|
|
|
2552
2574
|
refuse(getErrorMessage(error));
|
|
2553
2575
|
}
|
|
2554
2576
|
}
|
|
2577
|
+
async #handleFiles(ws, data, command) {
|
|
2578
|
+
const { client } = this.#options;
|
|
2579
|
+
const refuse = (message) => sendAgentMessage(ws, {
|
|
2580
|
+
type: "agent:files",
|
|
2581
|
+
jobId: data.jobId,
|
|
2582
|
+
payload: {
|
|
2583
|
+
status: "error",
|
|
2584
|
+
message
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
2587
|
+
if (!this.#canRead) {
|
|
2588
|
+
await this.#warn("Ignored files: reading generated files was not granted");
|
|
2589
|
+
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`);
|
|
2590
|
+
return;
|
|
2591
|
+
}
|
|
2592
|
+
if (!Array.isArray(data.payload?.paths)) {
|
|
2593
|
+
await this.#warn("Ignored files: the message carried no paths");
|
|
2594
|
+
refuse("the message carried no paths");
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
const { paths } = data.payload;
|
|
2598
|
+
if (paths.length > 50) {
|
|
2599
|
+
await this.#warn(`Ignored files: requested ${paths.length} paths, more than the 50 allowed per request`);
|
|
2600
|
+
refuse(`at most 50 paths may be requested at once`);
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
const generation = this.#lastGeneration;
|
|
2604
|
+
if (!generation) {
|
|
2605
|
+
await this.#warn("Ignored files: no prior generation to read from");
|
|
2606
|
+
refuse("no prior generation to read from, run a generation first");
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
const requested = paths.filter((path) => generation.paths.has(path));
|
|
2610
|
+
const files = {};
|
|
2611
|
+
await inParallel({
|
|
2612
|
+
items: requested,
|
|
2613
|
+
limit: FILE_READ_CONCURRENCY,
|
|
2614
|
+
run: async (path) => {
|
|
2615
|
+
const content = await generation.storage.readItem(absoluteStoragePath(generation.root, path));
|
|
2616
|
+
if (content !== null) files[path] = content;
|
|
2617
|
+
}
|
|
2618
|
+
});
|
|
2619
|
+
sendAgentMessage(ws, {
|
|
2620
|
+
type: "agent:files",
|
|
2621
|
+
jobId: data.jobId,
|
|
2622
|
+
payload: {
|
|
2623
|
+
status: "ok",
|
|
2624
|
+
files
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
await this.#hooks.callHook("studio:command:end", {
|
|
2628
|
+
command,
|
|
2629
|
+
info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2555
2632
|
};
|
|
2556
2633
|
//#endregion
|
|
2557
2634
|
//#region src/client.ts
|