Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ jobs:
node --test js/tests/node-terminal-artifacts.mjs
node --test js/tests/terminal-changes.test.mjs
node --test js/tests/core-entry.test.mjs
node --test js/tests/command-builder.node.test.mjs
node --test js/tests/node-commonjs-entry.mjs
node --test js/tests/node-process-regressions.mjs
node --test js/tests/windows-tree-kill.test.mjs
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ The [stream processing guide](./docs/stream-processing-guide.md) covers both
languages, deadline batching, backpressure, live analytics, and comparisons
with existing streaming APIs.

The [complete API and feature comparison](./js/docs/COMPARISON.md) covers the
tracked JavaScript and Rust alternatives; the [builder guide](./js/docs/COMMAND-BUILDER.md)
documents the command construction and execution styles.

## Repository Layout

| Path | Purpose |
Expand Down
39 changes: 39 additions & 0 deletions dev/log/issues/34/pulls/113/analysis/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Issue 34 / PR 113 acceptance evidence

Sources reviewed: the complete [issue](https://github.com/link-foundation/command-stream/issues/34), its empty comment collection, the PR description, all conversation comments (including the [latest quality/comparison request](https://github.com/link-foundation/command-stream/pull/113#issuecomment-6383907892)), inline comments and reviews, and merged PRs 225 and 236. There are no required sub-issues. The implementation is reconciled with main commit `2f8cea139a1b00f3067b11785616f3f2ea00ecc4`.

| Requirement | Implementation and verification |
| --- | --- |
| Exact `$.command('cat', path).pipe($.command.stdout('inherit'), $.command.exitCode).run()` example | `js/src/command-builder.mjs`; test `the exact issue example inherits stdout and returns an exit code` in `js/tests/command-builder.test.mjs` executes a real temporary file. |
| Avoid quoting and shell injection; produce a runnable string | Native exec specification, no shell reparsing; quote executable and each argument for POSIX rendering. Tests round-trip empty strings, spaces, quotes, newlines, operators and command substitutions, plus executable paths containing special characters. Windows runs argv directly; rendered text is explicitly POSIX. |
| Immediate execution and reusable descriptions | Await/run, synchronous execution, and runner/stream access; immutable copies retain argument/configuration state. Tests execute descriptions repeatedly and verify result/exit-code selection. |
| Support API styles and configurations from the PR feedback | Variadic and argv-array factories, constructor, fluent methods, functional transforms, option objects, nested factory defaults, configured tags and `create`; all ProcessOptions pass through the existing runner. Tests cover environment precedence/removal, cwd, buffered/live stdin, stdio, capture/mirror, overrides, deadlines, abort, events, pipelines and streams. Node tests cover full/core ESM/CommonJS; strict TypeScript tests cover the public exports and signatures. |
| Rust counterpart and the referenced Rust Command patterns | Owned fluent/functional composition, env/envs/clear/removal, stdio, cwd, async and sync execution using one native command configuration. A 160 KiB stdin/stdout test verifies concurrent draining instead of blocking on a full pipe. |
| Resolve conflicts without removing current features | Merge main into the prepared branch, retain the monorepo layout and current core/compatibility implementations; only the obsolete draft builder tests/examples are replaced. Compare the final diff against main and run the existing suites. |
| Clear docs and comparison across all features | `js/docs/COMMAND-BUILDER.md` documents every builder operation and runner option, precedence, execution contracts and safety boundaries. `js/docs/COMPARISON.md` covers process, shell, streams, lifecycle, compatibility, terminal, translation and development features with primary-source links, covering all twelve projects in the pinned JavaScript audit, both issue references, and the fourteen-project Rust audit. `rust/docs/COMPARISON.md` now gives a per-project construction, I/O, configuration, lifecycle and tooling comparison for all fourteen Rust projects. Generated feature examples provide further per-feature alternatives. Both audits identify implemented features accurately and preserve exact upstream conformance limits. |
| Competitive claims supported by evidence | Credit competitors' argv safety, fluent options, streams, transforms, plugins and native process controls. Link import-budget checks, compatibility corpora, feature examples and benchmarks. No unmeasured claim that one library universally outperforms every competitor; the comparison records specialized contracts and runtime limits explicitly. |
| Prepare release and review | JavaScript minor changeset and Rust minor changelog fragment; update PR title/body, preserve `Fixes #34`, verify current-SHA checks and mark ready without merging. |

## Reproduced failures and root causes

Before implementation, the focused JavaScript suite failed on missing fluent methods, array/options construction, preset factories and per-run configuration. The main builder exposed only a subset of those entry styles and ignored run arguments. Preset env objects also remained caller-owned until command construction; a separate failing regression demonstrated that mutation leak before snapshots were added.

Review found another env-preset failure: `command.with({env: {A: 'a'}})('echo', [])` dereferenced an absent options argument. The array/preset regression was run failing before `mergeOptions` received its default override object.

The Rust reproduction initially failed to compile with E0599 at `.pipe` and `.env_clear`. Adding synchronous execution and sharing native command configuration across sync/async resolves those missing contracts while retaining literal OsString arguments and signal exit status normalization.

All regression cases remain in the normal automated suites. Investigation logs are kept under `.git/issue-34/` locally; fresh remote failures, if any, are downloaded under `ci-logs/` and checked against commit timestamps and SHA.

## Follow-up quality audit

- New failing runtime tests demonstrated that synchronous native execution always piped stdio, ignored `capture:false`, and failed to forward inherited stdin. The extracted shared sync module now applies native stdio modes, emits only piped data, and discards disabled capture. Node subprocess tests verify output appears exactly once and parent stdin reaches the child; Bun tests cover ignored output and events with capture disabled.
- A failing mutation regression showed that `preferLocal` directory arrays remained caller-owned. Options snapshots now also copy/freeze those arrays and ANSI configuration, with regressions for presets and direct construction. Buffers and AbortSignals remain explicitly shared resources.
- Failing strict TypeScript assertions exposed the old variadic `any` fallback: long pipelines could erase exit-code selection and join incompatible result types. Typed overloads now validate up to twenty transforms, including generic configuration after selection; homogeneous transforms can be composed without that limit or pipelines can be chained. A separate failing test at the twentieth-transform boundary caught a shadowed type parameter in the draft overload; its input and output types now remain independent.
- Rust's environment changes now replay in configuration order through native Command rather than a case-sensitive unordered map. A cross-platform fixture verifies replacement/removal, re-addition and Windows case-insensitive versus Unix case-sensitive names in sync and async executions. The Windows branch is verified by the hosted Windows matrix, not inferred from a Linux run.
- Environment-clear/removal accounting was stale in both audits. Three Deno units and ten Rust units now map to executable public invariants in the paired decisions/dispositions ledgers. Other entries state which core/adapter implementations exist and which exact upstream contracts are still unported; adding an option is not treated as proof of full upstream conformance.

The complete comparison uses the published twelve-project JavaScript and fourteen-project Rust discovery sets, plus Effect as the issue reference. Universal superiority over every open-source project is not established: specialized native handles, expect sessions, persistent result caching and domain return adapters remain documented differences. No unmeasured performance ranking or complete upstream API conformance is claimed.

Validation logs from this continuation live in `.git/issue-34/`. The first full Rust attempt failed at `rust/tests/cancelled_output.rs:105` (exit -1 versus 0), while its immediate focused rerun passed; the same intermittent cancellation-test behavior is recorded in `dev/log/issues/219/pulls/222/README.md`. The full serial rerun passed 962 tests with 7 ignored, without increasing that test's deadlines. Rust formatting, all-target/all-feature Clippy with warnings denied, and rustdoc with warnings denied also pass. JavaScript lint, formatting, duplication and strict type checks pass, including the twentieth-transform regression. All 37 feature examples pass parity checks in JavaScript and Rust, generated documentation is current (39 files), and documentation validation passes all 251 checks including the new Rust comparison.

The final full Bun run passed 2,899 tests with 10 skipped and no failures (66,897 assertions across 183 files). The standalone Node builder suite passed all 7 tests. Hosted checks must be verified against the new commit SHA before the PR is marked ready; the PR description records their final status without treating the earlier commit's passing checks as current evidence.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ All 37 examples were executed in JavaScript and Rust. JavaScript was checked in
| [Execa compatibility mode](features/execa-compat.md) | ✓ | — | — | ✓ | — | — |
| [ShellJS compatibility mode](features/shelljs-compat.md) | ✓ | — | — | — | ✓ | — |
| [Native head, tail, sort and uniq](features/native-text.md) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| [Build commands with literal arguments](features/command-builder.md) | ✓ | — | — | ✓ | — | ✓ |
| [Build commands with literal arguments](features/command-builder.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Process streams incrementally](features/stream-processing.md) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Track virtual terminal frames](features/terminal-buffer.md) | ✓ | — | — | — | — | — |
| [Compose and extend virtual commands](features/command-packages.md) | ✓ | — | — | — | — | — |
Expand Down
12 changes: 9 additions & 3 deletions docs/features/command-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,15 @@ exit code: 7

### [Bun.$](https://bun.com/docs/runtime/shell)

Not supported — No command-stream transform API; invoke the executable with separate arguments.
```js
await Bun.spawn(['printf', '%s', 'literal ; text']).exited; // exact argv
```

### [zx](https://github.com/google/zx)

Not supported — No command-stream transform API; invoke the executable with separate arguments.
```js
await $`printf %s ${'literal ; text'}`; // escaped shell interpolation
```

### [execa](https://github.com/sindresorhus/execa)

Expand All @@ -102,7 +106,9 @@ await execa('printf', ['%s', 'literal ; text']);

### [ShellJS](https://github.com/shelljs/shelljs)

Not supported — No command-stream transform API; invoke the executable with separate arguments.
```js
shell.cmd('printf', '%s', 'literal ; text'); // exact argv
```

### [node:child_process](https://nodejs.org/api/child_process.html)

Expand Down
Binary file added docs/screenshots/command-builder-after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/command-builder-before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 3 additions & 9 deletions docs/site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1111,16 +1111,10 @@ <h1>command-stream — feature comparison</h1>
"file": "js/examples/features/command-builder.mjs",
"api": ["command", "CommandBuilder"],
"alternatives": {
"bun-shell": {
"unsupported": "No command-stream transform API; invoke the executable with separate arguments"
},
"zx": {
"unsupported": "No command-stream transform API; invoke the executable with separate arguments"
},
"bun-shell": "await Bun.spawn(['printf', '%s', 'literal ; text']).exited; // exact argv",
"zx": "await $`printf %s ${\"literal ; text\"}`; // escaped shell interpolation",
"execa": "await execa('printf', ['%s', 'literal ; text']);",
"shelljs": {
"unsupported": "No command-stream transform API; invoke the executable with separate arguments"
},
"shelljs": "shell.cmd('printf', '%s', 'literal ; text'); // exact argv",
"child_process": "spawn('printf', ['%s', 'literal ; text']);"
},
"source": "import { command } from '../../src/$.mjs';\nimport { example } from './_harness.mjs';\nawait example(\n { id: 'command-builder', title: 'Build commands with literal arguments' },\n async ({ record }) => {\n const builder = command(\n process.execPath,\n '-e',\n 'console.log(process.argv[1])',\n 'hello ; $HOME'\n );\n record('literal argument', (await (await builder).text()).trim());\n record(\n 'exit code',\n await command(process.execPath, '-e', 'process.exit(7)')\n .pipe(command.exitCode)\n .run()\n );\n }\n);",
Expand Down
9 changes: 9 additions & 0 deletions js/.changeset/complete-command-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'command-stream': minor
---

Complete literal command builders with immutable fluent methods, argv arrays,
factory and shell presets, merged environment layers, per-run overrides and
synchronous execution. Honor inherited/ignored stdio and disabled capture in
sync calls, snapshot nested configuration, and preserve long pipeline types.
Document every API style and correct the library-wide competitor comparison.
Loading
Loading