@beignet/cli 0.0.39 → 0.0.41
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/CHANGELOG.md +18 -0
- package/README.md +110 -16
- package/dist/analysis/source-index.d.ts +38 -0
- package/dist/analysis/source-index.d.ts.map +1 -0
- package/dist/analysis/source-index.js +271 -0
- package/dist/analysis/source-index.js.map +1 -0
- package/dist/analysis/workspace.d.ts +16 -0
- package/dist/analysis/workspace.d.ts.map +1 -0
- package/dist/analysis/workspace.js +134 -0
- package/dist/analysis/workspace.js.map +1 -0
- package/dist/app-map-schema.d.ts +5 -0
- package/dist/app-map-schema.d.ts.map +1 -0
- package/dist/app-map-schema.js +26 -0
- package/dist/app-map-schema.js.map +1 -0
- package/dist/app-map.d.ts +96 -0
- package/dist/app-map.d.ts.map +1 -0
- package/dist/app-map.js +1141 -0
- package/dist/app-map.js.map +1 -0
- package/dist/check.d.ts +7 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +20 -5
- package/dist/check.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/db.d.ts +32 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +182 -14
- package/dist/db.js.map +1 -1
- package/dist/explain.d.ts +98 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +512 -0
- package/dist/explain.js.map +1 -0
- package/dist/framework.d.ts +3 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +6 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +173 -36
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +4 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +2 -0
- package/dist/make/payments.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +157 -3
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +69 -1
- package/dist/mcp.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +10 -0
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +167 -13
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts +6 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +63 -15
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +17 -4
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +10 -4
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +8 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +3 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +55 -1
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shadcn.js +1 -1
- package/dist/templates/shared.js +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +38 -5
- package/src/analysis/source-index.ts +395 -0
- package/src/analysis/workspace.ts +180 -0
- package/src/app-map-schema.ts +28 -0
- package/src/app-map.ts +1705 -0
- package/src/check.ts +27 -4
- package/src/config.ts +7 -3
- package/src/db.ts +232 -14
- package/src/explain.ts +786 -0
- package/src/framework.ts +13 -0
- package/src/index.ts +113 -2
- package/src/inspect.ts +259 -40
- package/src/lib.ts +36 -0
- package/src/make/payments.ts +7 -0
- package/src/make.ts +232 -2
- package/src/mcp.ts +107 -1
- package/src/preflight.ts +14 -0
- package/src/provider-add.ts +211 -12
- package/src/provider-audit.ts +127 -22
- package/src/templates/agents.ts +17 -4
- package/src/templates/base.ts +22 -4
- package/src/templates/index.ts +18 -1
- package/src/templates/server.ts +58 -1
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +1 -1
- package/src/test-helpers/generated-app.ts +1 -1
package/src/check.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type CheckStep = {
|
|
|
22
22
|
status: "passed" | "failed" | "skipped";
|
|
23
23
|
detail?: string;
|
|
24
24
|
output?: string;
|
|
25
|
+
outputTruncated?: boolean;
|
|
25
26
|
durationMs: number;
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -40,11 +41,18 @@ export type CheckAppOptions = {
|
|
|
40
41
|
color?: boolean;
|
|
41
42
|
/** Append a `beignet preflight` step after the doctor step. */
|
|
42
43
|
preflight?: boolean;
|
|
44
|
+
/** Keep only the last N bytes from each package-script output stream. */
|
|
45
|
+
maxOutputBytes?: number;
|
|
46
|
+
/** Cancel the active package script with the owning request. */
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
/** Fail each package script after this many milliseconds. */
|
|
49
|
+
scriptTimeoutMs?: number;
|
|
43
50
|
/** Called after each step completes, for incremental progress output. */
|
|
44
51
|
onStep?: (step: CheckStep) => void;
|
|
45
52
|
};
|
|
46
53
|
|
|
47
54
|
const scriptStepNames = ["lint", "typecheck", "test"] as const;
|
|
55
|
+
const defaultMaxOutputBytes = 64 * 1024;
|
|
48
56
|
|
|
49
57
|
/**
|
|
50
58
|
* Run the whole app validation loop as one command: `beignet lint`,
|
|
@@ -67,17 +75,21 @@ export async function checkApp(
|
|
|
67
75
|
options.onStep?.(step);
|
|
68
76
|
};
|
|
69
77
|
|
|
78
|
+
options.signal?.throwIfAborted();
|
|
70
79
|
record(await lintStep(targetDir, options));
|
|
80
|
+
options.signal?.throwIfAborted();
|
|
71
81
|
const { step: doctorStep, fixes } = await runDoctorStep(targetDir, options);
|
|
72
82
|
record(doctorStep);
|
|
73
83
|
|
|
74
84
|
if (options.preflight) {
|
|
85
|
+
options.signal?.throwIfAborted();
|
|
75
86
|
record(await preflightStep(targetDir));
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
const scripts = await readPackageScripts(targetDir);
|
|
79
90
|
for (const script of scriptStepNames) {
|
|
80
|
-
|
|
91
|
+
options.signal?.throwIfAborted();
|
|
92
|
+
record(await scriptStep(targetDir, runner, script, scripts, options));
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
return {
|
|
@@ -114,7 +126,10 @@ export function formatCheckStep(
|
|
|
114
126
|
.split("\n")
|
|
115
127
|
.map((outputLine) => ` ${outputLine}`)
|
|
116
128
|
.join("\n");
|
|
117
|
-
|
|
129
|
+
const truncation = step.outputTruncated
|
|
130
|
+
? "\n … output truncated; showing the retained tail"
|
|
131
|
+
: "";
|
|
132
|
+
return `${line}\n${indented}${truncation}`;
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
/**
|
|
@@ -203,6 +218,7 @@ async function scriptStep(
|
|
|
203
218
|
runner: string,
|
|
204
219
|
script: (typeof scriptStepNames)[number],
|
|
205
220
|
scripts: Record<string, string>,
|
|
221
|
+
options: CheckAppOptions,
|
|
206
222
|
): Promise<CheckStep> {
|
|
207
223
|
const command = `${runner} run ${script}`;
|
|
208
224
|
if (!scripts[script]) {
|
|
@@ -218,17 +234,24 @@ async function scriptStep(
|
|
|
218
234
|
const startedAt = Date.now();
|
|
219
235
|
const result = await spawnCommand(runner, ["run", script], targetDir, {
|
|
220
236
|
captureOutput: true,
|
|
237
|
+
maxOutputBytes: options.maxOutputBytes ?? defaultMaxOutputBytes,
|
|
238
|
+
signal: options.signal,
|
|
239
|
+
timeoutMs: options.scriptTimeoutMs,
|
|
221
240
|
});
|
|
222
|
-
const failed = result.exitCode !== 0;
|
|
241
|
+
const failed = result.timedOut === true || result.exitCode !== 0;
|
|
242
|
+
const detail = result.timedOut
|
|
243
|
+
? `${command}; timed out after ${options.scriptTimeoutMs}ms`
|
|
244
|
+
: command;
|
|
223
245
|
|
|
224
246
|
return {
|
|
225
247
|
name: script,
|
|
226
248
|
command,
|
|
227
249
|
status: failed ? "failed" : "passed",
|
|
228
|
-
detail
|
|
250
|
+
detail,
|
|
229
251
|
...(failed
|
|
230
252
|
? { output: [result.stdout, result.stderr].filter(Boolean).join("\n") }
|
|
231
253
|
: {}),
|
|
254
|
+
...(failed && result.outputTruncated ? { outputTruncated: true } : {}),
|
|
232
255
|
durationMs: Date.now() - startedAt,
|
|
233
256
|
};
|
|
234
257
|
}
|
package/src/config.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { createJiti } from "jiti";
|
|
|
6
6
|
/**
|
|
7
7
|
* Supported application framework targets for Beignet config.
|
|
8
8
|
*/
|
|
9
|
-
export type BeignetFramework = "next";
|
|
9
|
+
export type BeignetFramework = "next" | "web";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Configurable app paths used by CLI generators, inspection, linting, and
|
|
@@ -306,11 +306,15 @@ function assertConfigObject(
|
|
|
306
306
|
throw new Error(`${configFile} must export a Beignet config object.`);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
if (
|
|
309
|
+
if (
|
|
310
|
+
config.framework !== undefined &&
|
|
311
|
+
config.framework !== "next" &&
|
|
312
|
+
config.framework !== "web"
|
|
313
|
+
) {
|
|
310
314
|
throw new Error(
|
|
311
315
|
`${configFile} uses unsupported framework "${String(
|
|
312
316
|
config.framework,
|
|
313
|
-
)}". Supported
|
|
317
|
+
)}". Supported frameworks: "next", "web".`,
|
|
314
318
|
);
|
|
315
319
|
}
|
|
316
320
|
|
package/src/db.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { type ChildProcess, spawn } from "node:child_process";
|
|
2
2
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
@@ -33,6 +33,29 @@ export type RunDatabaseCommandResult = {
|
|
|
33
33
|
exitCode: number;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
export type SpawnCommandOptions = {
|
|
37
|
+
captureOutput: boolean;
|
|
38
|
+
/** Keep only the last N bytes from each output stream. Unbounded when omitted. */
|
|
39
|
+
maxOutputBytes?: number;
|
|
40
|
+
/** Cancel the child process when the owning request is aborted. */
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
/** Stop the child process after this many milliseconds. Unbounded when omitted. */
|
|
43
|
+
timeoutMs?: number;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type SpawnCommandResult = {
|
|
47
|
+
exitCode: number;
|
|
48
|
+
stdout?: string;
|
|
49
|
+
stderr?: string;
|
|
50
|
+
outputTruncated?: boolean;
|
|
51
|
+
timedOut?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type ProcessTerminationPlan =
|
|
55
|
+
| { kind: "child"; signal: NodeJS.Signals }
|
|
56
|
+
| { kind: "process-group"; pid: number; signal: NodeJS.Signals }
|
|
57
|
+
| { kind: "windows-process-tree"; command: string; args: string[] };
|
|
58
|
+
|
|
36
59
|
export type DatabaseSchemaDialect = "sqlite" | "postgres" | "mysql";
|
|
37
60
|
export type DatabaseSchemaTable = "audit" | "idempotency" | "outbox";
|
|
38
61
|
|
|
@@ -504,34 +527,229 @@ export function spawnCommand(
|
|
|
504
527
|
command: string,
|
|
505
528
|
args: readonly string[],
|
|
506
529
|
cwd: string,
|
|
507
|
-
options:
|
|
508
|
-
): Promise<
|
|
530
|
+
options: SpawnCommandOptions,
|
|
531
|
+
): Promise<SpawnCommandResult> {
|
|
532
|
+
if (options.signal?.aborted) {
|
|
533
|
+
return Promise.reject(commandAbortError(options.signal));
|
|
534
|
+
}
|
|
535
|
+
const timeoutMs =
|
|
536
|
+
options.timeoutMs === undefined
|
|
537
|
+
? undefined
|
|
538
|
+
: normalizePositiveInteger(options.timeoutMs, "Command timeout");
|
|
539
|
+
const maxOutputBytes =
|
|
540
|
+
options.maxOutputBytes === undefined
|
|
541
|
+
? undefined
|
|
542
|
+
: normalizeNonNegativeInteger(
|
|
543
|
+
options.maxOutputBytes,
|
|
544
|
+
"Maximum output bytes",
|
|
545
|
+
);
|
|
546
|
+
|
|
509
547
|
return new Promise((resolve, reject) => {
|
|
548
|
+
let settled = false;
|
|
549
|
+
let timedOut = false;
|
|
550
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
551
|
+
let forceKillTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
552
|
+
const ownsProcessGroup = commandOwnsProcessGroup(process.platform, {
|
|
553
|
+
signal: options.signal,
|
|
554
|
+
timeoutMs,
|
|
555
|
+
});
|
|
510
556
|
const child = spawn(command, args, {
|
|
511
557
|
cwd,
|
|
558
|
+
detached: ownsProcessGroup,
|
|
512
559
|
env: process.env,
|
|
513
560
|
stdio: options.captureOutput ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
514
561
|
});
|
|
515
562
|
|
|
516
|
-
const stdout
|
|
517
|
-
const stderr
|
|
563
|
+
const stdout = createOutputCapture(maxOutputBytes);
|
|
564
|
+
const stderr = createOutputCapture(maxOutputBytes);
|
|
565
|
+
|
|
566
|
+
const cleanup = () => {
|
|
567
|
+
if (timeout) clearTimeout(timeout);
|
|
568
|
+
if (forceKillTimeout) clearTimeout(forceKillTimeout);
|
|
569
|
+
options.signal?.removeEventListener("abort", abort);
|
|
570
|
+
};
|
|
571
|
+
const abort = () => {
|
|
572
|
+
if (settled) return;
|
|
573
|
+
settled = true;
|
|
574
|
+
terminateChild(child, "SIGKILL", ownsProcessGroup);
|
|
575
|
+
cleanup();
|
|
576
|
+
reject(commandAbortError(options.signal));
|
|
577
|
+
};
|
|
518
578
|
|
|
519
579
|
if (options.captureOutput) {
|
|
520
|
-
child.stdout?.on("data", (chunk: Buffer) => stdout.
|
|
521
|
-
child.stderr?.on("data", (chunk: Buffer) => stderr.
|
|
580
|
+
child.stdout?.on("data", (chunk: Buffer) => stdout.append(chunk));
|
|
581
|
+
child.stderr?.on("data", (chunk: Buffer) => stderr.append(chunk));
|
|
522
582
|
}
|
|
523
583
|
|
|
524
|
-
child.on("error",
|
|
525
|
-
|
|
584
|
+
child.on("error", (error) => {
|
|
585
|
+
if (settled) return;
|
|
586
|
+
settled = true;
|
|
587
|
+
cleanup();
|
|
588
|
+
reject(error);
|
|
589
|
+
});
|
|
590
|
+
child.on("close", (code) => {
|
|
591
|
+
if (settled) return;
|
|
592
|
+
settled = true;
|
|
593
|
+
cleanup();
|
|
526
594
|
resolve({
|
|
527
|
-
exitCode: code ?? 1,
|
|
595
|
+
exitCode: timedOut ? 1 : (code ?? 1),
|
|
528
596
|
...(options.captureOutput
|
|
529
597
|
? {
|
|
530
|
-
stdout:
|
|
531
|
-
stderr:
|
|
598
|
+
stdout: stdout.text(),
|
|
599
|
+
stderr: stderr.text(),
|
|
600
|
+
...(stdout.truncated() || stderr.truncated()
|
|
601
|
+
? { outputTruncated: true }
|
|
602
|
+
: {}),
|
|
532
603
|
}
|
|
533
604
|
: {}),
|
|
534
|
-
|
|
535
|
-
|
|
605
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
610
|
+
if (options.signal?.aborted) {
|
|
611
|
+
abort();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (timeoutMs !== undefined) {
|
|
615
|
+
timeout = setTimeout(() => {
|
|
616
|
+
if (settled) return;
|
|
617
|
+
timedOut = true;
|
|
618
|
+
terminateChild(child, "SIGTERM", ownsProcessGroup);
|
|
619
|
+
forceKillTimeout = setTimeout(() => {
|
|
620
|
+
if (!settled) terminateChild(child, "SIGKILL", ownsProcessGroup);
|
|
621
|
+
}, 1_000);
|
|
622
|
+
forceKillTimeout.unref?.();
|
|
623
|
+
}, timeoutMs);
|
|
624
|
+
timeout.unref?.();
|
|
625
|
+
}
|
|
536
626
|
});
|
|
537
627
|
}
|
|
628
|
+
|
|
629
|
+
export function commandOwnsProcessGroup(
|
|
630
|
+
platform: NodeJS.Platform,
|
|
631
|
+
options: Pick<SpawnCommandOptions, "signal" | "timeoutMs">,
|
|
632
|
+
): boolean {
|
|
633
|
+
return (
|
|
634
|
+
platform !== "win32" &&
|
|
635
|
+
(options.signal !== undefined || options.timeoutMs !== undefined)
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
export function commandTerminationPlan(
|
|
640
|
+
platform: NodeJS.Platform,
|
|
641
|
+
pid: number | undefined,
|
|
642
|
+
signal: NodeJS.Signals,
|
|
643
|
+
ownsProcessGroup: boolean,
|
|
644
|
+
): ProcessTerminationPlan {
|
|
645
|
+
if (platform === "win32" && pid !== undefined) {
|
|
646
|
+
return {
|
|
647
|
+
kind: "windows-process-tree",
|
|
648
|
+
command: "taskkill",
|
|
649
|
+
args: ["/PID", String(pid), "/T", "/F"],
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (ownsProcessGroup && pid !== undefined) {
|
|
653
|
+
return { kind: "process-group", pid: -pid, signal };
|
|
654
|
+
}
|
|
655
|
+
return { kind: "child", signal };
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function terminateChild(
|
|
659
|
+
child: ChildProcess,
|
|
660
|
+
signal: NodeJS.Signals,
|
|
661
|
+
ownsProcessGroup: boolean,
|
|
662
|
+
): void {
|
|
663
|
+
const plan = commandTerminationPlan(
|
|
664
|
+
process.platform,
|
|
665
|
+
child.pid,
|
|
666
|
+
signal,
|
|
667
|
+
ownsProcessGroup,
|
|
668
|
+
);
|
|
669
|
+
if (plan.kind === "windows-process-tree") {
|
|
670
|
+
const killer = spawn(plan.command, plan.args, {
|
|
671
|
+
stdio: "ignore",
|
|
672
|
+
windowsHide: true,
|
|
673
|
+
});
|
|
674
|
+
const fallback = () => {
|
|
675
|
+
if (child.exitCode === null) child.kill(signal);
|
|
676
|
+
};
|
|
677
|
+
killer.once("error", fallback);
|
|
678
|
+
killer.once("close", (code) => {
|
|
679
|
+
if (code !== 0) fallback();
|
|
680
|
+
});
|
|
681
|
+
killer.unref();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (plan.kind === "process-group") {
|
|
685
|
+
try {
|
|
686
|
+
process.kill(plan.pid, plan.signal);
|
|
687
|
+
return;
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if ((error as NodeJS.ErrnoException).code === "ESRCH") return;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
child.kill(plan.signal);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function createOutputCapture(maxOutputBytes?: number): {
|
|
696
|
+
append(chunk: Buffer): void;
|
|
697
|
+
text(): string;
|
|
698
|
+
truncated(): boolean;
|
|
699
|
+
} {
|
|
700
|
+
const limit = maxOutputBytes;
|
|
701
|
+
const unboundedChunks: Buffer[] = [];
|
|
702
|
+
let output: Buffer = Buffer.alloc(0);
|
|
703
|
+
let wasTruncated = false;
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
append(chunk) {
|
|
707
|
+
if (limit === undefined) {
|
|
708
|
+
unboundedChunks.push(chunk);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (limit === 0) {
|
|
712
|
+
if (chunk.length > 0) wasTruncated = true;
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (chunk.length >= limit) {
|
|
716
|
+
wasTruncated ||= output.length > 0 || chunk.length > limit;
|
|
717
|
+
output = Buffer.from(chunk.subarray(chunk.length - limit));
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const overflow = output.length + chunk.length - limit;
|
|
721
|
+
if (overflow > 0) {
|
|
722
|
+
wasTruncated = true;
|
|
723
|
+
output = Buffer.concat([output.subarray(overflow), chunk]);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
output = Buffer.concat([output, chunk]);
|
|
727
|
+
},
|
|
728
|
+
text: () =>
|
|
729
|
+
(limit === undefined ? Buffer.concat(unboundedChunks) : output).toString(
|
|
730
|
+
"utf8",
|
|
731
|
+
),
|
|
732
|
+
truncated: () => wasTruncated,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function commandAbortError(signal?: AbortSignal): Error {
|
|
737
|
+
if (signal?.reason instanceof Error) return signal.reason;
|
|
738
|
+
const error = new Error("Command cancelled.");
|
|
739
|
+
error.name = "AbortError";
|
|
740
|
+
return error;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function normalizePositiveInteger(value: number, label: string): number {
|
|
744
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
745
|
+
throw new Error(`${label} must be a positive integer.`);
|
|
746
|
+
}
|
|
747
|
+
return value;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function normalizeNonNegativeInteger(value: number, label: string): number {
|
|
751
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
752
|
+
throw new Error(`${label} must be a non-negative integer.`);
|
|
753
|
+
}
|
|
754
|
+
return value;
|
|
755
|
+
}
|