Story
As a tool consuming the component catalog, I want piped xmd syntax output to
finish completely before the command exits, so I can rely on receiving valid,
complete Markdown or JSON.
Example
These two forms must produce the same bytes:
xmd syntax --json > direct.json
xmd syntax --json | cat > piped.json
cmp direct.json piped.json
jq empty piped.json
The same guarantee applies when the pipe's reader consumes output slowly.
Current gap
xmd syntax --json produces a complete catalog when stdout is a regular file,
but a catalog larger than the pipe buffer can end mid-token when stdout is a
pipe. A consumer such as jq receives truncated JSON even though xmd exits
without reporting a failure.
The current command hands the rendered catalog to process.stdout.write() and
can finish without waiting for an asynchronous pipe write to drain. Registering
additional components made the catalog exceed roughly 64 KiB and exposed the
existing lifetime defect; those components did not cause it.
Issue #632 established xmd syntax --json as the complete structured catalog.
Issue #678 consumes that catalog for generated reference documentation, but
does not own the CLI output lifetime.
Contract
xmd syntax does not complete successfully until stdout has accepted the
complete rendered catalog.
- A regular-file redirect and a pipe receive byte-identical output for the same
invocation, in both Markdown and JSON forms.
- Backpressure changes how long the command takes, not which bytes arrive.
- A sink that closes or refuses the write makes the command fail rather than
silently succeeding with partial output.
- TTY presentation and catalog generation remain unchanged; this story owns
delivery of the already-rendered xmd syntax result, not another catalog
format or a general rewrite of CLI output.
Acceptance
- A real subprocess emits a catalog larger than the platform pipe buffer through
a slow or backpressured pipe, and the received bytes equal direct redirection.
- The piped JSON parses successfully and contains the complete version-1
catalog, including an entry placed beyond the former truncation boundary.
- The default Markdown form is likewise complete through the same boundary.
- Closing the consumer before completion produces a non-zero command outcome
instead of a successful truncated result or a late unhandled write failure.
- The regression fails when the command returns immediately after a
fire-and-forget process.stdout.write().
Evidence
Extend the real CLI coverage in
packages/cli/tests/syntax-cli.test.ts and run:
deno task test packages/cli/tests/syntax-cli.test.ts
Use an actual subprocess and pipe boundary. A small catalog or a mocked
process.stdout.write() alone cannot discriminate the failure.
Story
As a tool consuming the component catalog, I want piped
xmd syntaxoutput tofinish completely before the command exits, so I can rely on receiving valid,
complete Markdown or JSON.
Example
These two forms must produce the same bytes:
The same guarantee applies when the pipe's reader consumes output slowly.
Current gap
xmd syntax --jsonproduces a complete catalog when stdout is a regular file,but a catalog larger than the pipe buffer can end mid-token when stdout is a
pipe. A consumer such as
jqreceives truncated JSON even thoughxmdexitswithout reporting a failure.
The current command hands the rendered catalog to
process.stdout.write()andcan finish without waiting for an asynchronous pipe write to drain. Registering
additional components made the catalog exceed roughly 64 KiB and exposed the
existing lifetime defect; those components did not cause it.
Issue #632 established
xmd syntax --jsonas the complete structured catalog.Issue #678 consumes that catalog for generated reference documentation, but
does not own the CLI output lifetime.
Contract
xmd syntaxdoes not complete successfully until stdout has accepted thecomplete rendered catalog.
invocation, in both Markdown and JSON forms.
silently succeeding with partial output.
delivery of the already-rendered
xmd syntaxresult, not another catalogformat or a general rewrite of CLI output.
Acceptance
a slow or backpressured pipe, and the received bytes equal direct redirection.
catalog, including an entry placed beyond the former truncation boundary.
instead of a successful truncated result or a late unhandled write failure.
fire-and-forget
process.stdout.write().Evidence
Extend the real CLI coverage in
packages/cli/tests/syntax-cli.test.tsand run:deno task test packages/cli/tests/syntax-cli.test.tsUse an actual subprocess and pipe boundary. A small catalog or a mocked
process.stdout.write()alone cannot discriminate the failure.