Files
rea/src/cli/jsonOutput.ts
T
morluto 810107a2ad fix(cli): stream JSON results at the shared command boundary
Route ordinary CLI results through the bounded JSON writer after command registration, including managed member inspection. Remove JavaScript-specific output plumbing while preserving filters, envelopes, cancellation, and failure exits.

Measure formatted UTF-16 expansion before Incur token counting and return resource_constraint above the runtime string limit, with recovery guidance.

Validate output above the real V8 string limit by SHA-256 and exercise the compiled managed provider. Clean-worktree validation: npm run check; 76 focused tests passed, with 2 optional-provider skips; test discovery passed.

Fixes #1371.
2026-10-10 06:23:26 +08:00

53 lines
1.5 KiB
TypeScript

import type { Writable } from "node:stream";
import { bufferedJsonParts, jsonParts } from "../domain/jsonSerialization.js";
type JsonOutputFormat = "json" | "jsonl";
/** Write ordinary JSON without constructing a document-sized string. */
export const writeJsonOutput = async (
value: unknown,
destination: Writable,
format: JsonOutputFormat = "json",
): Promise<void> => {
for (const part of bufferedJsonParts(jsonOutputParts(value, format)))
await writePart(destination, part);
};
const writePart = (destination: Writable, part: string): Promise<void> =>
new Promise((resolve, reject) => {
const onError = (cause: unknown): void => reject(cause);
destination.once("error", onError);
try {
destination.write(part, (cause) => {
if (cause) {
// A Writable can emit its error after invoking the write callback.
setImmediate(() => destination.off("error", onError));
reject(cause);
} else {
destination.off("error", onError);
resolve();
}
});
} catch (cause: unknown) {
destination.off("error", onError);
reject(cause);
}
});
/** The exact formatted document, including its final newline. */
export function* jsonOutputParts(
value: unknown,
format: JsonOutputFormat,
): Generator<string> {
if (format === "jsonl" && Array.isArray(value)) {
for (const item of value) {
yield* jsonParts(item);
yield "\n";
}
return;
}
yield* jsonParts(value, format === "json");
yield "\n";
}