command-stream 0.8.2 → 0.9.0
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/js/examples/01-basic-streaming.mjs +14 -0
- package/js/examples/02-async-iterator.mjs +9 -0
- package/js/examples/03-file-and-console.mjs +16 -0
- package/js/examples/04-claude-jq-pipe.mjs +16 -0
- package/js/examples/CI-DEBUG-README.md +138 -0
- package/js/examples/README-examples.mjs +111 -0
- package/js/examples/README.md +345 -0
- package/js/examples/STREAMING_INTERFACES_SUMMARY.md +116 -0
- package/js/examples/add-test-timeouts.js +107 -0
- package/js/examples/ansi-default-preserved.mjs +11 -0
- package/js/examples/ansi-global-config.mjs +12 -0
- package/js/examples/ansi-reset-default.mjs +12 -0
- package/js/examples/ansi-strip-utils.mjs +12 -0
- package/js/examples/baseline-child-process.mjs +23 -0
- package/js/examples/baseline-claude-test.mjs +47 -0
- package/js/examples/baseline-working.mjs +34 -0
- package/js/examples/capture-mirror-comparison.mjs +23 -0
- package/js/examples/capture-mirror-default.mjs +11 -0
- package/js/examples/capture-mirror-performance.mjs +16 -0
- package/js/examples/capture-mirror-show-only.mjs +16 -0
- package/js/examples/capture-mirror-silent-processing.mjs +16 -0
- package/js/examples/ci-debug-baseline-vs-library.mjs +265 -0
- package/js/examples/ci-debug-es-module-loading.mjs +184 -0
- package/js/examples/ci-debug-signal-handling.mjs +225 -0
- package/js/examples/ci-debug-stdout-buffering.mjs +94 -0
- package/js/examples/ci-debug-test-timeouts.mjs +259 -0
- package/js/examples/claude-exact-file-output.mjs +20 -0
- package/js/examples/claude-exact-jq.mjs +13 -0
- package/js/examples/claude-exact-streaming.mjs +13 -0
- package/js/examples/claude-jq-pipeline.mjs +13 -0
- package/js/examples/claude-json-stream.mjs +39 -0
- package/js/examples/claude-streaming-basic.mjs +99 -0
- package/js/examples/claude-streaming-demo.mjs +126 -0
- package/js/examples/claude-streaming-final.mjs +20 -0
- package/js/examples/cleanup-verification-test.mjs +115 -0
- package/js/examples/colors-buffer-processing.mjs +14 -0
- package/js/examples/colors-default-preserved.mjs +15 -0
- package/js/examples/colors-per-command-config.mjs +15 -0
- package/js/examples/colors-strip-ansi.mjs +13 -0
- package/js/examples/commandstream-jq.mjs +29 -0
- package/js/examples/commandstream-working.mjs +23 -0
- package/js/examples/comprehensive-streams-demo.mjs +121 -0
- package/js/examples/ctrl-c-concurrent-processes.mjs +20 -0
- package/js/examples/ctrl-c-long-running-command.mjs +20 -0
- package/js/examples/ctrl-c-real-system-command.mjs +17 -0
- package/js/examples/ctrl-c-sleep-command.mjs +17 -0
- package/js/examples/ctrl-c-stdin-forwarding.mjs +20 -0
- package/js/examples/ctrl-c-virtual-command.mjs +17 -0
- package/js/examples/debug-already-started.mjs +20 -0
- package/js/examples/debug-ansi-processing.mjs +42 -0
- package/js/examples/debug-basic-streaming.mjs +44 -0
- package/js/examples/debug-buildshellcommand.mjs +82 -0
- package/js/examples/debug-child-process.mjs +43 -0
- package/js/examples/debug-child-state.mjs +55 -0
- package/js/examples/debug-chunking.mjs +38 -0
- package/js/examples/debug-command-parsing.mjs +61 -0
- package/js/examples/debug-complete-consolidation.mjs +97 -0
- package/js/examples/debug-echo-args.mjs +22 -0
- package/js/examples/debug-emit-timing.mjs +28 -0
- package/js/examples/debug-end-event.mjs +56 -0
- package/js/examples/debug-errexit.mjs +16 -0
- package/js/examples/debug-event-emission.mjs +40 -0
- package/js/examples/debug-event-timing.mjs +67 -0
- package/js/examples/debug-event-vs-result.mjs +30 -0
- package/js/examples/debug-exact-command.mjs +28 -0
- package/js/examples/debug-exact-test-scenario.mjs +44 -0
- package/js/examples/debug-execution-path.mjs +27 -0
- package/js/examples/debug-exit-command.mjs +38 -0
- package/js/examples/debug-exit-virtual.mjs +54 -0
- package/js/examples/debug-finish-consolidation.mjs +44 -0
- package/js/examples/debug-force-cleanup.mjs +47 -0
- package/js/examples/debug-getter-basic.mjs +13 -0
- package/js/examples/debug-getter-direct.mjs +23 -0
- package/js/examples/debug-getter-internals.mjs +61 -0
- package/js/examples/debug-handler-detection.mjs +65 -0
- package/js/examples/debug-idempotent-finish.mjs +54 -0
- package/js/examples/debug-idempotent-kill.mjs +58 -0
- package/js/examples/debug-interpolation-individual.mjs +88 -0
- package/js/examples/debug-interpolation-issue.mjs +101 -0
- package/js/examples/debug-jq-streaming.mjs +57 -0
- package/js/examples/debug-jq-tty-colors.mjs +168 -0
- package/js/examples/debug-kill-cleanup.mjs +56 -0
- package/js/examples/debug-kill-method.mjs +33 -0
- package/js/examples/debug-listener-interference.mjs +38 -0
- package/js/examples/debug-listener-lifecycle.mjs +50 -0
- package/js/examples/debug-listener-timing.mjs +62 -0
- package/js/examples/debug-listeners-property.mjs +34 -0
- package/js/examples/debug-map-methods.mjs +39 -0
- package/js/examples/debug-not-awaited-cleanup.mjs +106 -0
- package/js/examples/debug-off-method.mjs +28 -0
- package/js/examples/debug-option-merging.mjs +17 -0
- package/js/examples/debug-options.mjs +36 -0
- package/js/examples/debug-output.mjs +25 -0
- package/js/examples/debug-pattern-matching.mjs +69 -0
- package/js/examples/debug-pipeline-cat.mjs +28 -0
- package/js/examples/debug-pipeline-cleanup.mjs +71 -0
- package/js/examples/debug-pipeline-error-detailed.mjs +78 -0
- package/js/examples/debug-pipeline-error.mjs +65 -0
- package/js/examples/debug-pipeline-issue.mjs +26 -0
- package/js/examples/debug-pipeline-method.mjs +22 -0
- package/js/examples/debug-pipeline-stream.mjs +66 -0
- package/js/examples/debug-pipeline.mjs +14 -0
- package/js/examples/debug-process-exit-trace.mjs +41 -0
- package/js/examples/debug-process-path.mjs +38 -0
- package/js/examples/debug-property-check.mjs +29 -0
- package/js/examples/debug-resource-cleanup.mjs +83 -0
- package/js/examples/debug-shell-args.mjs +103 -0
- package/js/examples/debug-sigint-child-handler.mjs +44 -0
- package/js/examples/debug-sigint-forwarding.mjs +61 -0
- package/js/examples/debug-sigint-handler-install.mjs +79 -0
- package/js/examples/debug-sigint-handler-order.mjs +85 -0
- package/js/examples/debug-sigint-listeners.mjs +48 -0
- package/js/examples/debug-sigint-start-pattern.mjs +62 -0
- package/js/examples/debug-sigint-timer.mjs +40 -0
- package/js/examples/debug-simple-command.mjs +49 -0
- package/js/examples/debug-simple-getter.mjs +30 -0
- package/js/examples/debug-simple.mjs +39 -0
- package/js/examples/debug-simplified-finished.mjs +102 -0
- package/js/examples/debug-stack-overflow.mjs +25 -0
- package/js/examples/debug-stdin-simple.mjs +28 -0
- package/js/examples/debug-stdin.mjs +28 -0
- package/js/examples/debug-stream-emitter-isolated.mjs +45 -0
- package/js/examples/debug-stream-emitter.mjs +25 -0
- package/js/examples/debug-stream-events.mjs +70 -0
- package/js/examples/debug-stream-generator.mjs +23 -0
- package/js/examples/debug-stream-getter-issue.mjs +37 -0
- package/js/examples/debug-stream-getter.mjs +19 -0
- package/js/examples/debug-stream-internals.mjs +64 -0
- package/js/examples/debug-stream-method.mjs +46 -0
- package/js/examples/debug-stream-object.mjs +58 -0
- package/js/examples/debug-stream-properties.mjs +37 -0
- package/js/examples/debug-stream-timing.mjs +28 -0
- package/js/examples/debug-streaming.mjs +24 -0
- package/js/examples/debug-test-isolation.mjs +54 -0
- package/js/examples/debug-test-state.mjs +27 -0
- package/js/examples/debug-user-sigint.mjs +19 -0
- package/js/examples/debug-virtual-disable.mjs +21 -0
- package/js/examples/debug-virtual-vs-real.mjs +68 -0
- package/js/examples/debug-with-trace.mjs +23 -0
- package/js/examples/debug_parent_stream.mjs +22 -0
- package/js/examples/emulate-claude-stream.mjs +30 -0
- package/js/examples/emulated-streaming-direct.mjs +22 -0
- package/js/examples/emulated-streaming-jq-pipe.mjs +22 -0
- package/js/examples/emulated-streaming-sh-pipe.mjs +23 -0
- package/js/examples/events-build-process.mjs +73 -0
- package/js/examples/events-concurrent-streams.mjs +51 -0
- package/js/examples/events-error-handling.mjs +59 -0
- package/js/examples/events-file-monitoring.mjs +66 -0
- package/js/examples/events-interactive-simulation.mjs +69 -0
- package/js/examples/events-log-processing.mjs +72 -0
- package/js/examples/events-network-monitoring.mjs +68 -0
- package/js/examples/events-ping-basic.mjs +37 -0
- package/js/examples/events-progress-tracking.mjs +55 -0
- package/js/examples/events-stdin-input.mjs +34 -0
- package/js/examples/example-ansi-ls.mjs +39 -0
- package/js/examples/example-top.mjs +27 -0
- package/js/examples/final-ping-stdin-proof.mjs +88 -0
- package/js/examples/final-test-shell-operators.mjs +123 -0
- package/js/examples/final-working-examples.mjs +162 -0
- package/js/examples/gh-auth-test.mjs +103 -0
- package/js/examples/gh-delete-hang-test.mjs +79 -0
- package/js/examples/gh-gist-creation-test.mjs +276 -0
- package/js/examples/gh-gist-minimal-test.mjs +89 -0
- package/js/examples/gh-hang-exact-original.mjs +83 -0
- package/js/examples/gh-hang-reproduction.mjs +151 -0
- package/js/examples/gh-hang-test-with-redirect.mjs +45 -0
- package/js/examples/gh-hang-test-without-redirect.mjs +43 -0
- package/js/examples/gh-minimal-hang-check.mjs +33 -0
- package/js/examples/gh-operations-with-cd.mjs +187 -0
- package/js/examples/gh-output-test.mjs +102 -0
- package/js/examples/gh-reproduce-hang.mjs +41 -0
- package/js/examples/git-operations-with-cd.mjs +186 -0
- package/js/examples/interactive-math-calc.mjs +45 -0
- package/js/examples/interactive-top-fixed.mjs +25 -0
- package/js/examples/interactive-top-improved.mjs +72 -0
- package/js/examples/interactive-top-pty-logging.mjs +69 -0
- package/js/examples/interactive-top-pty.mjs +27 -0
- package/js/examples/interactive-top-with-logging.mjs +56 -0
- package/js/examples/interactive-top.mjs +29 -0
- package/js/examples/jq-color-demo.mjs +53 -0
- package/js/examples/jq-colors-streaming.mjs +42 -0
- package/js/examples/manual-ctrl-c-test.mjs +50 -0
- package/js/examples/methods-multiple-options.mjs +25 -0
- package/js/examples/methods-run-basic.mjs +10 -0
- package/js/examples/methods-start-basic.mjs +10 -0
- package/js/examples/node-compat-data-events.mjs +36 -0
- package/js/examples/node-compat-readable-event.mjs +29 -0
- package/js/examples/node-compat-small-buffer.mjs +33 -0
- package/js/examples/options-capture-false.mjs +12 -0
- package/js/examples/options-combined-settings.mjs +13 -0
- package/js/examples/options-custom-input.mjs +16 -0
- package/js/examples/options-default-behavior.mjs +10 -0
- package/js/examples/options-maximum-performance.mjs +15 -0
- package/js/examples/options-mirror-false.mjs +10 -0
- package/js/examples/options-performance-mode.mjs +13 -0
- package/js/examples/options-performance-optimization.mjs +14 -0
- package/js/examples/options-run-alias-demo.mjs +15 -0
- package/js/examples/options-run-alias.mjs +10 -0
- package/js/examples/options-silent-execution.mjs +14 -0
- package/js/examples/options-streaming-capture.mjs +24 -0
- package/js/examples/options-streaming-multiple.mjs +35 -0
- package/js/examples/options-streaming-silent.mjs +21 -0
- package/js/examples/options-streaming-stdin.mjs +21 -0
- package/js/examples/ping-streaming-filtered.mjs +22 -0
- package/js/examples/ping-streaming-interruptible.mjs +47 -0
- package/js/examples/ping-streaming-silent.mjs +24 -0
- package/js/examples/ping-streaming-simple.mjs +13 -0
- package/js/examples/ping-streaming-statistics.mjs +49 -0
- package/js/examples/ping-streaming-timestamps.mjs +22 -0
- package/js/examples/ping-streaming.mjs +48 -0
- package/js/examples/prove-ping-stdin-limitation.mjs +94 -0
- package/js/examples/readme-example.mjs +39 -0
- package/js/examples/realtime-json-stream.mjs +143 -0
- package/js/examples/reliable-stdin-commands.mjs +135 -0
- package/js/examples/reproduce-issue-135-v2.mjs +15 -0
- package/js/examples/reproduce-issue-135.mjs +17 -0
- package/js/examples/shell-cd-behavior.mjs +88 -0
- package/js/examples/sigint-forwarding-test.mjs +60 -0
- package/js/examples/sigint-handler-test.mjs +72 -0
- package/js/examples/simple-async-test.mjs +49 -0
- package/js/examples/simple-claude-test.mjs +17 -0
- package/js/examples/simple-event-test.mjs +33 -0
- package/js/examples/simple-jq-streaming.mjs +48 -0
- package/js/examples/simple-stream-demo.mjs +35 -0
- package/js/examples/simple-test-sleep.js +30 -0
- package/js/examples/simple-working-stdin.mjs +30 -0
- package/js/examples/streaming-behavior-test.mjs +116 -0
- package/js/examples/streaming-direct-command.mjs +21 -0
- package/js/examples/streaming-filtered-output.mjs +33 -0
- package/js/examples/streaming-grep-pipeline.mjs +21 -0
- package/js/examples/streaming-interactive-stdin.mjs +24 -0
- package/js/examples/streaming-jq-pipeline.mjs +23 -0
- package/js/examples/streaming-multistage-pipeline.mjs +23 -0
- package/js/examples/streaming-pipes-event-pattern.mjs +27 -0
- package/js/examples/streaming-pipes-multistage.mjs +22 -0
- package/js/examples/streaming-pipes-realtime-jq.mjs +23 -0
- package/js/examples/streaming-progress-tracking.mjs +34 -0
- package/js/examples/streaming-reusable-configs.mjs +52 -0
- package/js/examples/streaming-silent-capture.mjs +20 -0
- package/js/examples/streaming-test-simple.mjs +70 -0
- package/js/examples/streaming-virtual-pipeline.mjs +18 -0
- package/js/examples/syntax-basic-comparison.mjs +31 -0
- package/js/examples/syntax-basic-options.mjs +12 -0
- package/js/examples/syntax-combined-options.mjs +19 -0
- package/js/examples/syntax-command-chaining.mjs +12 -0
- package/js/examples/syntax-custom-directory.mjs +10 -0
- package/js/examples/syntax-custom-environment.mjs +13 -0
- package/js/examples/syntax-custom-stdin.mjs +10 -0
- package/js/examples/syntax-mixed-regular.mjs +11 -0
- package/js/examples/syntax-mixed-usage.mjs +15 -0
- package/js/examples/syntax-multiple-listeners.mjs +87 -0
- package/js/examples/syntax-piping-comparison.mjs +32 -0
- package/js/examples/syntax-reusable-config.mjs +16 -0
- package/js/examples/syntax-reusable-configs.mjs +21 -0
- package/js/examples/syntax-silent-operations.mjs +10 -0
- package/js/examples/syntax-stdin-option.mjs +12 -0
- package/js/examples/temp-sigint-test.mjs +21 -0
- package/js/examples/test-actual-buildshell.mjs +44 -0
- package/js/examples/test-async-streams-working.mjs +102 -0
- package/js/examples/test-async-streams.mjs +90 -0
- package/js/examples/test-auth-parse.mjs +74 -0
- package/js/examples/test-auto-quoting.mjs +57 -0
- package/js/examples/test-auto-start-fix.mjs +95 -0
- package/js/examples/test-baseline-sigint.mjs +38 -0
- package/js/examples/test-buffer-behavior.mjs +39 -0
- package/js/examples/test-buffers-simple.mjs +35 -0
- package/js/examples/test-bun-specific-issue.mjs +106 -0
- package/js/examples/test-bun-streaming.mjs +81 -0
- package/js/examples/test-cat-direct.mjs +41 -0
- package/js/examples/test-cat-pipe.mjs +34 -0
- package/js/examples/test-cd-behavior.mjs +42 -0
- package/js/examples/test-child-process-timing.mjs +53 -0
- package/js/examples/test-child-sigint-handler.mjs +62 -0
- package/js/examples/test-cleanup-simple.mjs +21 -0
- package/js/examples/test-comprehensive-tracing.mjs +58 -0
- package/js/examples/test-correct-space-handling.mjs +46 -0
- package/js/examples/test-ctrl-c-debug.mjs +44 -0
- package/js/examples/test-ctrl-c-inherit.mjs +30 -0
- package/js/examples/test-ctrl-c-sleep.mjs +31 -0
- package/js/examples/test-ctrl-c.mjs +17 -0
- package/js/examples/test-debug-new-options.mjs +55 -0
- package/js/examples/test-debug-pty.mjs +49 -0
- package/js/examples/test-debug-tee.mjs +38 -0
- package/js/examples/test-debug.mjs +25 -0
- package/js/examples/test-direct-jq.mjs +47 -0
- package/js/examples/test-direct-pipe-reading.mjs +119 -0
- package/js/examples/test-direct-pipe.sh +28 -0
- package/js/examples/test-double-quoting-prevention.mjs +138 -0
- package/js/examples/test-edge-cases-quoting.mjs +89 -0
- package/js/examples/test-events.mjs +37 -0
- package/js/examples/test-explicit-stdio.mjs +51 -0
- package/js/examples/test-final-streaming.mjs +71 -0
- package/js/examples/test-fix.mjs +71 -0
- package/js/examples/test-incremental-streaming.mjs +46 -0
- package/js/examples/test-individual-spawn.mjs +35 -0
- package/js/examples/test-inherit-stdout-not-stdin.mjs +133 -0
- package/js/examples/test-injection-protection.mjs +77 -0
- package/js/examples/test-interactive-streaming.mjs +140 -0
- package/js/examples/test-interactive-top.md +24 -0
- package/js/examples/test-interactive.mjs +17 -0
- package/js/examples/test-interpolation.mjs +14 -0
- package/js/examples/test-interrupt.mjs +40 -0
- package/js/examples/test-issue-135-comprehensive.mjs +41 -0
- package/js/examples/test-issue12-detailed.mjs +89 -0
- package/js/examples/test-issue12-exact.mjs +33 -0
- package/js/examples/test-jq-color.mjs +57 -0
- package/js/examples/test-jq-colors.mjs +41 -0
- package/js/examples/test-jq-compact.mjs +33 -0
- package/js/examples/test-jq-native.sh +10 -0
- package/js/examples/test-jq-pipeline-behavior.mjs +80 -0
- package/js/examples/test-jq-realtime.mjs +40 -0
- package/js/examples/test-manual-start.mjs +54 -0
- package/js/examples/test-mixed-quoting.mjs +88 -0
- package/js/examples/test-multi-stream.mjs +50 -0
- package/js/examples/test-multistage-debug.mjs +44 -0
- package/js/examples/test-native-spawn-vs-command-stream.mjs +154 -0
- package/js/examples/test-no-parse-pipeline.mjs +33 -0
- package/js/examples/test-non-virtual.mjs +52 -0
- package/js/examples/test-operators.mjs +53 -0
- package/js/examples/test-parent-continues.mjs +44 -0
- package/js/examples/test-path-interpolation.mjs +86 -0
- package/js/examples/test-ping-kill-and-stdin.mjs +98 -0
- package/js/examples/test-ping.mjs +12 -0
- package/js/examples/test-pty-spawn.mjs +101 -0
- package/js/examples/test-pty.mjs +38 -0
- package/js/examples/test-quote-behavior-summary.mjs +110 -0
- package/js/examples/test-quote-edge-cases.mjs +69 -0
- package/js/examples/test-quote-parsing.mjs +23 -0
- package/js/examples/test-raw-function.mjs +153 -0
- package/js/examples/test-raw-streaming.mjs +47 -0
- package/js/examples/test-readme-examples.mjs +142 -0
- package/js/examples/test-real-cat.mjs +28 -0
- package/js/examples/test-real-commands.mjs +21 -0
- package/js/examples/test-real-shell.mjs +31 -0
- package/js/examples/test-real-stdin-commands.mjs +160 -0
- package/js/examples/test-runner-batched.mjs +98 -0
- package/js/examples/test-runner-simple.mjs +80 -0
- package/js/examples/test-runner.mjs +67 -0
- package/js/examples/test-scope-parse.mjs +31 -0
- package/js/examples/test-sh-pipeline.mjs +24 -0
- package/js/examples/test-shell-detection.mjs +71 -0
- package/js/examples/test-shell-parser.mjs +37 -0
- package/js/examples/test-sigint-behavior.mjs +241 -0
- package/js/examples/test-sigint-handling.sh +14 -0
- package/js/examples/test-simple-pipe.mjs +12 -0
- package/js/examples/test-simple-streaming.mjs +32 -0
- package/js/examples/test-sleep-stdin.js +27 -0
- package/js/examples/test-sleep.mjs +56 -0
- package/js/examples/test-smart-quoting.mjs +180 -0
- package/js/examples/test-spaces-in-path.mjs +48 -0
- package/js/examples/test-special-chars-quoting.mjs +54 -0
- package/js/examples/test-stdin-after-start.mjs +39 -0
- package/js/examples/test-stdin-simple.mjs +67 -0
- package/js/examples/test-stdin-timing.mjs +74 -0
- package/js/examples/test-stdio-combinations.mjs +124 -0
- package/js/examples/test-stream-access.mjs +84 -0
- package/js/examples/test-stream-cleanup.mjs +27 -0
- package/js/examples/test-stream-readers.mjs +152 -0
- package/js/examples/test-streaming-final.mjs +57 -0
- package/js/examples/test-streaming-interfaces.mjs +141 -0
- package/js/examples/test-streaming-timing.mjs +27 -0
- package/js/examples/test-streaming.mjs +32 -0
- package/js/examples/test-streams-stdin-comprehensive.mjs +134 -0
- package/js/examples/test-streams-stdin-ctrl-c.mjs +96 -0
- package/js/examples/test-template-literal.mjs +26 -0
- package/js/examples/test-template-vs-interpolation.mjs +49 -0
- package/js/examples/test-timing.mjs +41 -0
- package/js/examples/test-top-inherit-stdout-stdin-control.mjs +123 -0
- package/js/examples/test-top-quit-stdin.mjs +118 -0
- package/js/examples/test-trace-option.mjs +21 -0
- package/js/examples/test-user-double-quotes.mjs +36 -0
- package/js/examples/test-user-single-quotes.mjs +36 -0
- package/js/examples/test-verbose.mjs +18 -0
- package/js/examples/test-verbose2.mjs +32 -0
- package/js/examples/test-virtual-streaming.mjs +125 -0
- package/js/examples/test-waiting-command.mjs +52 -0
- package/js/examples/test-waiting-commands.mjs +83 -0
- package/js/examples/test-watch-mode.mjs +104 -0
- package/js/examples/test-yes-cancellation.mjs +26 -0
- package/js/examples/test-yes-detailed.mjs +58 -0
- package/js/examples/test-yes-trace.mjs +28 -0
- package/js/examples/trace-abort-controller.mjs +30 -0
- package/js/examples/trace-error-handling.mjs +22 -0
- package/js/examples/trace-pipeline-command.mjs +22 -0
- package/js/examples/trace-signal-handling.mjs +35 -0
- package/js/examples/trace-simple-command.mjs +18 -0
- package/js/examples/trace-stderr-output.mjs +22 -0
- package/js/examples/verify-fix-both-runtimes.mjs +73 -0
- package/js/examples/verify-issue12-fixed.mjs +78 -0
- package/js/examples/which-command-common-commands.mjs +19 -0
- package/js/examples/which-command-gh-test.mjs +23 -0
- package/js/examples/which-command-nonexistent.mjs +20 -0
- package/js/examples/which-command-system-comparison.mjs +28 -0
- package/js/examples/working-example.mjs +13 -0
- package/js/examples/working-stdin-examples.mjs +138 -0
- package/js/examples/working-streaming-demo.mjs +49 -0
- package/{src → js/src}/$.mjs +20 -4
- package/{src → js/src}/$.utils.mjs +14 -2
- package/js/tests/$.features.test.mjs +283 -0
- package/js/tests/$.test.mjs +935 -0
- package/js/tests/builtin-commands.test.mjs +387 -0
- package/js/tests/bun-shell-path-fix.test.mjs +115 -0
- package/js/tests/bun.features.test.mjs +189 -0
- package/js/tests/cd-virtual-command.test.mjs +622 -0
- package/js/tests/cleanup-verification.test.mjs +127 -0
- package/js/tests/ctrl-c-baseline.test.mjs +207 -0
- package/js/tests/ctrl-c-basic.test.mjs +220 -0
- package/js/tests/ctrl-c-library.test.mjs +197 -0
- package/js/tests/ctrl-c-signal.test.mjs +915 -0
- package/js/tests/examples.test.mjs +252 -0
- package/js/tests/execa.features.test.mjs +198 -0
- package/js/tests/gh-commands.test.mjs +164 -0
- package/js/tests/gh-gist-operations.test.mjs +221 -0
- package/js/tests/git-gh-cd.test.mjs +466 -0
- package/js/tests/interactive-option.test.mjs +114 -0
- package/js/tests/interactive-streaming.test.mjs +307 -0
- package/js/tests/issue-135-final.test.mjs +58 -0
- package/js/tests/jq-color-behavior.test.mjs +140 -0
- package/js/tests/jq.test.mjs +318 -0
- package/js/tests/options-examples.test.mjs +106 -0
- package/js/tests/options-syntax.test.mjs +112 -0
- package/js/tests/path-interpolation.test.mjs +412 -0
- package/js/tests/pipe.test.mjs +291 -0
- package/js/tests/raw-function.test.mjs +266 -0
- package/js/tests/readme-examples.test.mjs +427 -0
- package/js/tests/resource-cleanup-internals.test.mjs +669 -0
- package/js/tests/shell-settings.test.mjs +279 -0
- package/js/tests/sigint-cleanup-isolated.test.mjs +151 -0
- package/js/tests/sigint-cleanup.test.mjs +118 -0
- package/js/tests/start-run-edge-cases.test.mjs +152 -0
- package/js/tests/start-run-options.test.mjs +181 -0
- package/js/tests/stderr-output-handling.test.mjs +279 -0
- package/js/tests/streaming-interfaces.test.mjs +194 -0
- package/js/tests/sync.test.mjs +297 -0
- package/js/tests/system-pipe.test.mjs +226 -0
- package/js/tests/test-cleanup.mjs +200 -0
- package/js/tests/test-helper-fixed.mjs +148 -0
- package/js/tests/test-helper-v2.mjs +118 -0
- package/js/tests/test-helper.mjs +171 -0
- package/js/tests/test-sigint-child.js +15 -0
- package/js/tests/text-method.test.mjs +225 -0
- package/js/tests/virtual.test.mjs +364 -0
- package/js/tests/yes-command-cleanup.test.mjs +208 -0
- package/js/tests/zx.features.test.mjs +233 -0
- package/package.json +13 -12
- package/rust/Cargo.lock +947 -0
- package/rust/Cargo.toml +47 -0
- package/rust/src/commands/basename.rs +69 -0
- package/rust/src/commands/cat.rs +123 -0
- package/rust/src/commands/cd.rs +67 -0
- package/rust/src/commands/cp.rs +187 -0
- package/rust/src/commands/dirname.rs +57 -0
- package/rust/src/commands/echo.rs +73 -0
- package/rust/src/commands/env.rs +33 -0
- package/rust/src/commands/exit.rs +36 -0
- package/rust/src/commands/false.rs +24 -0
- package/rust/src/commands/ls.rs +182 -0
- package/rust/src/commands/mkdir.rs +98 -0
- package/rust/src/commands/mod.rs +200 -0
- package/rust/src/commands/mv.rs +180 -0
- package/rust/src/commands/pwd.rs +28 -0
- package/rust/src/commands/rm.rs +150 -0
- package/rust/src/commands/seq.rs +179 -0
- package/rust/src/commands/sleep.rs +97 -0
- package/rust/src/commands/test.rs +204 -0
- package/rust/src/commands/touch.rs +99 -0
- package/rust/src/commands/true.rs +24 -0
- package/rust/src/commands/which.rs +87 -0
- package/rust/src/commands/yes.rs +99 -0
- package/rust/src/lib.rs +492 -0
- package/rust/src/main.rs +37 -0
- package/rust/src/shell_parser.rs +565 -0
- package/rust/src/utils.rs +335 -0
- package/rust/tests/builtin_commands.rs +549 -0
- package/rust/tests/process_runner.rs +286 -0
- package/rust/tests/shell_parser.rs +296 -0
- package/rust/tests/utils.rs +282 -0
- package/rust/tests/virtual_commands.rs +199 -0
- /package/{src → js/src}/commands/$.basename.mjs +0 -0
- /package/{src → js/src}/commands/$.cat.mjs +0 -0
- /package/{src → js/src}/commands/$.cd.mjs +0 -0
- /package/{src → js/src}/commands/$.cp.mjs +0 -0
- /package/{src → js/src}/commands/$.dirname.mjs +0 -0
- /package/{src → js/src}/commands/$.echo.mjs +0 -0
- /package/{src → js/src}/commands/$.env.mjs +0 -0
- /package/{src → js/src}/commands/$.exit.mjs +0 -0
- /package/{src → js/src}/commands/$.false.mjs +0 -0
- /package/{src → js/src}/commands/$.ls.mjs +0 -0
- /package/{src → js/src}/commands/$.mkdir.mjs +0 -0
- /package/{src → js/src}/commands/$.mv.mjs +0 -0
- /package/{src → js/src}/commands/$.pwd.mjs +0 -0
- /package/{src → js/src}/commands/$.rm.mjs +0 -0
- /package/{src → js/src}/commands/$.seq.mjs +0 -0
- /package/{src → js/src}/commands/$.sleep.mjs +0 -0
- /package/{src → js/src}/commands/$.test.mjs +0 -0
- /package/{src → js/src}/commands/$.touch.mjs +0 -0
- /package/{src → js/src}/commands/$.true.mjs +0 -0
- /package/{src → js/src}/commands/$.which.mjs +0 -0
- /package/{src → js/src}/commands/$.yes.mjs +0 -0
- /package/{src → js/src}/shell-parser.mjs +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
|
|
2
|
+
import { isWindows } from './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
|
|
3
|
+
import { $, register, unregister, enableVirtualCommands } from '../src/$.mjs';
|
|
4
|
+
import { trace } from '../src/$.utils.mjs';
|
|
5
|
+
import { rmSync, existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
|
|
8
|
+
// Test directory for safe file operations
|
|
9
|
+
const TEST_DIR = 'test-builtin-commands';
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
// Enable virtual commands for these tests
|
|
13
|
+
enableVirtualCommands();
|
|
14
|
+
|
|
15
|
+
// Create clean test directory
|
|
16
|
+
if (existsSync(TEST_DIR)) {
|
|
17
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
mkdirSync(TEST_DIR);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
// Clean up test directory
|
|
24
|
+
if (existsSync(TEST_DIR)) {
|
|
25
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('Built-in Commands (Bun.$ compatible)', () => {
|
|
30
|
+
describe('File Reading Commands', () => {
|
|
31
|
+
test('cat should read file contents', async () => {
|
|
32
|
+
const testFile = join(TEST_DIR, 'test.txt');
|
|
33
|
+
writeFileSync(testFile, 'Hello World\nLine 2\n');
|
|
34
|
+
|
|
35
|
+
const result = await $`cat ${testFile}`;
|
|
36
|
+
expect(result.code).toBe(0);
|
|
37
|
+
expect(result.stdout).toBe('Hello World\nLine 2\n');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('cat should read from stdin when no files provided', async () => {
|
|
41
|
+
const result = await $`echo "input" | cat`;
|
|
42
|
+
expect(result.code).toBe(0);
|
|
43
|
+
expect(result.stdout).toBe('input\n');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('cat should handle non-existent files', async () => {
|
|
47
|
+
const result = await $`cat nonexistent.txt`;
|
|
48
|
+
expect(result.code).toBe(1);
|
|
49
|
+
expect(result.stderr).toContain('No such file or directory');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('Directory Listing Commands', () => {
|
|
54
|
+
test('ls should list directory contents', async () => {
|
|
55
|
+
writeFileSync(join(TEST_DIR, 'file1.txt'), 'content');
|
|
56
|
+
writeFileSync(join(TEST_DIR, 'file2.txt'), 'content');
|
|
57
|
+
mkdirSync(join(TEST_DIR, 'subdir'));
|
|
58
|
+
|
|
59
|
+
const result = await $`ls ${TEST_DIR}`;
|
|
60
|
+
expect(result.code).toBe(0);
|
|
61
|
+
expect(result.stdout).toContain('file1.txt');
|
|
62
|
+
expect(result.stdout).toContain('file2.txt');
|
|
63
|
+
expect(result.stdout).toContain('subdir');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('ls should support -a flag for hidden files', async () => {
|
|
67
|
+
writeFileSync(join(TEST_DIR, '.hidden'), 'content');
|
|
68
|
+
writeFileSync(join(TEST_DIR, 'visible.txt'), 'content');
|
|
69
|
+
|
|
70
|
+
const result = await $`ls -a ${TEST_DIR}`;
|
|
71
|
+
expect(result.code).toBe(0);
|
|
72
|
+
expect(result.stdout).toContain('.hidden');
|
|
73
|
+
expect(result.stdout).toContain('visible.txt');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('ls should support -l flag for long format', async () => {
|
|
77
|
+
writeFileSync(join(TEST_DIR, 'test.txt'), 'content');
|
|
78
|
+
|
|
79
|
+
const result = await $`ls -l ${TEST_DIR}`;
|
|
80
|
+
expect(result.code).toBe(0);
|
|
81
|
+
expect(result.stdout).toContain('-rw-r--r--');
|
|
82
|
+
expect(result.stdout).toContain('test.txt');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('File Creation Commands', () => {
|
|
87
|
+
test('mkdir should create directories', async () => {
|
|
88
|
+
const result = await $`mkdir ${join(TEST_DIR, 'newdir')}`;
|
|
89
|
+
expect(result.code).toBe(0);
|
|
90
|
+
expect(existsSync(join(TEST_DIR, 'newdir'))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('mkdir -p should create parent directories', async () => {
|
|
94
|
+
const result = await $`mkdir -p ${join(TEST_DIR, 'parent', 'child')}`;
|
|
95
|
+
expect(result.code).toBe(0);
|
|
96
|
+
expect(existsSync(join(TEST_DIR, 'parent', 'child'))).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('touch should create new files', async () => {
|
|
100
|
+
const testFile = join(TEST_DIR, 'touched.txt');
|
|
101
|
+
const result = await $`touch ${testFile}`;
|
|
102
|
+
expect(result.code).toBe(0);
|
|
103
|
+
expect(existsSync(testFile)).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('touch should update existing file timestamps', async () => {
|
|
107
|
+
const testFile = join(TEST_DIR, 'existing.txt');
|
|
108
|
+
writeFileSync(testFile, 'content');
|
|
109
|
+
const oldStat = require('fs').statSync(testFile);
|
|
110
|
+
|
|
111
|
+
// Wait a bit to ensure timestamp difference
|
|
112
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
113
|
+
|
|
114
|
+
const result = await $`touch ${testFile}`;
|
|
115
|
+
expect(result.code).toBe(0);
|
|
116
|
+
|
|
117
|
+
const newStat = require('fs').statSync(testFile);
|
|
118
|
+
expect(newStat.mtime.getTime()).toBeGreaterThan(oldStat.mtime.getTime());
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('File Removal Commands', () => {
|
|
123
|
+
test('rm should remove files', async () => {
|
|
124
|
+
const testFile = join(TEST_DIR, 'to-remove.txt');
|
|
125
|
+
writeFileSync(testFile, 'content');
|
|
126
|
+
|
|
127
|
+
const result = await $`rm ${testFile}`;
|
|
128
|
+
expect(result.code).toBe(0);
|
|
129
|
+
expect(existsSync(testFile)).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('rm should fail on directories without -r', async () => {
|
|
133
|
+
const testDir = join(TEST_DIR, 'to-remove-dir');
|
|
134
|
+
mkdirSync(testDir);
|
|
135
|
+
|
|
136
|
+
const result = await $`rm ${testDir}`;
|
|
137
|
+
expect(result.code).toBe(1);
|
|
138
|
+
expect(result.stderr).toContain('Is a directory');
|
|
139
|
+
expect(existsSync(testDir)).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('rm -r should remove directories recursively', async () => {
|
|
143
|
+
const testDir = join(TEST_DIR, 'to-remove-recursive');
|
|
144
|
+
mkdirSync(testDir);
|
|
145
|
+
writeFileSync(join(testDir, 'file.txt'), 'content');
|
|
146
|
+
|
|
147
|
+
const result = await $`rm -r ${testDir}`;
|
|
148
|
+
expect(result.code).toBe(0);
|
|
149
|
+
expect(existsSync(testDir)).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('rm -f should suppress errors on non-existent files', async () => {
|
|
153
|
+
const result = await $`rm -f nonexistent.txt`;
|
|
154
|
+
expect(result.code).toBe(0);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('File Copy/Move Commands', () => {
|
|
159
|
+
test('cp should copy files', async () => {
|
|
160
|
+
const source = join(TEST_DIR, 'source.txt');
|
|
161
|
+
const dest = join(TEST_DIR, 'dest.txt');
|
|
162
|
+
writeFileSync(source, 'test content');
|
|
163
|
+
|
|
164
|
+
const result = await $`cp ${source} ${dest}`;
|
|
165
|
+
expect(result.code).toBe(0);
|
|
166
|
+
expect(existsSync(dest)).toBe(true);
|
|
167
|
+
expect(readFileSync(dest, 'utf8')).toBe('test content');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('cp -r should copy directories recursively', async () => {
|
|
171
|
+
const sourceDir = join(TEST_DIR, 'source-dir');
|
|
172
|
+
const destDir = join(TEST_DIR, 'dest-dir');
|
|
173
|
+
mkdirSync(sourceDir);
|
|
174
|
+
writeFileSync(join(sourceDir, 'file.txt'), 'content');
|
|
175
|
+
|
|
176
|
+
const result = await $`cp -r ${sourceDir} ${destDir}`;
|
|
177
|
+
expect(result.code).toBe(0);
|
|
178
|
+
expect(existsSync(destDir)).toBe(true);
|
|
179
|
+
expect(existsSync(join(destDir, 'file.txt'))).toBe(true);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('mv should move/rename files', async () => {
|
|
183
|
+
const source = join(TEST_DIR, 'source.txt');
|
|
184
|
+
const dest = join(TEST_DIR, 'dest.txt');
|
|
185
|
+
writeFileSync(source, 'test content');
|
|
186
|
+
|
|
187
|
+
const result = await $`mv ${source} ${dest}`;
|
|
188
|
+
expect(result.code).toBe(0);
|
|
189
|
+
expect(existsSync(source)).toBe(false);
|
|
190
|
+
expect(existsSync(dest)).toBe(true);
|
|
191
|
+
expect(readFileSync(dest, 'utf8')).toBe('test content');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('mv should move files to directory', async () => {
|
|
195
|
+
const source = join(TEST_DIR, 'source.txt');
|
|
196
|
+
const destDir = join(TEST_DIR, 'dest-dir');
|
|
197
|
+
writeFileSync(source, 'test content');
|
|
198
|
+
mkdirSync(destDir);
|
|
199
|
+
|
|
200
|
+
const result = await $`mv ${source} ${destDir}`;
|
|
201
|
+
expect(result.code).toBe(0);
|
|
202
|
+
expect(existsSync(source)).toBe(false);
|
|
203
|
+
expect(existsSync(join(destDir, 'source.txt'))).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('Path Utility Commands', () => {
|
|
208
|
+
test('basename should extract filename', async () => {
|
|
209
|
+
const result = await $`basename /path/to/file.txt`;
|
|
210
|
+
expect(result.code).toBe(0);
|
|
211
|
+
expect(result.stdout.trim()).toBe('file.txt');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('basename should remove suffix', async () => {
|
|
215
|
+
const result = await $`basename /path/to/file.txt .txt`;
|
|
216
|
+
expect(result.code).toBe(0);
|
|
217
|
+
expect(result.stdout.trim()).toBe('file');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('dirname should extract directory path', async () => {
|
|
221
|
+
const result = await $`dirname /path/to/file.txt`;
|
|
222
|
+
expect(result.code).toBe(0);
|
|
223
|
+
expect(result.stdout.trim()).toBe('/path/to');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Sequence Generation Commands', () => {
|
|
228
|
+
test('seq should generate number sequence', async () => {
|
|
229
|
+
const result = await $`seq 1 3`;
|
|
230
|
+
expect(result.code).toBe(0);
|
|
231
|
+
expect(result.stdout).toBe('1\n2\n3\n');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('seq should handle single argument', async () => {
|
|
235
|
+
const result = await $`seq 3`;
|
|
236
|
+
expect(result.code).toBe(0);
|
|
237
|
+
expect(result.stdout).toBe('1\n2\n3\n');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('seq should handle step argument', async () => {
|
|
241
|
+
const result = await $`seq 1 2 5`;
|
|
242
|
+
expect(result.code).toBe(0);
|
|
243
|
+
expect(result.stdout).toBe('1\n3\n5\n');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe('Streaming Commands', () => {
|
|
248
|
+
test('yes should output repeatedly', async () => {
|
|
249
|
+
// Test with a limit to avoid infinite output
|
|
250
|
+
const chunks = [];
|
|
251
|
+
let count = 0;
|
|
252
|
+
|
|
253
|
+
for await (const chunk of $`yes hello`.stream()) {
|
|
254
|
+
chunks.push(chunk.data.toString());
|
|
255
|
+
count++;
|
|
256
|
+
if (count >= 3) {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
expect(chunks.length).toBe(3);
|
|
262
|
+
chunks.forEach((chunk) => {
|
|
263
|
+
expect(chunk).toBe('hello\n');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('yes should default to "y"', async () => {
|
|
268
|
+
const chunks = [];
|
|
269
|
+
let count = 0;
|
|
270
|
+
|
|
271
|
+
for await (const chunk of $`yes`.stream()) {
|
|
272
|
+
chunks.push(chunk.data.toString());
|
|
273
|
+
count++;
|
|
274
|
+
if (count >= 2) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
expect(chunks.length).toBe(2);
|
|
280
|
+
chunks.forEach((chunk) => {
|
|
281
|
+
expect(chunk).toBe('y\n');
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('Command Location (which)', () => {
|
|
287
|
+
// Skip on Windows - uses Unix 'which sh' command
|
|
288
|
+
test.skipIf(isWindows)(
|
|
289
|
+
'which should find existing system commands',
|
|
290
|
+
async () => {
|
|
291
|
+
// Test with a command that should definitely exist on all systems
|
|
292
|
+
const result = await $`which sh`;
|
|
293
|
+
expect(result.code).toBe(0);
|
|
294
|
+
expect(result.stdout).toMatch(/\/.*sh/); // Should contain path to sh
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
test('which should find node/bun executable', async () => {
|
|
299
|
+
// Test with node or bun depending on the environment
|
|
300
|
+
const command = typeof Bun !== 'undefined' ? 'bun' : 'node';
|
|
301
|
+
const result = await $`which ${command}`;
|
|
302
|
+
expect(result.code).toBe(0);
|
|
303
|
+
expect(result.stdout).toMatch(new RegExp(`.*${command}`));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('which should find homebrew-installed commands (if available)', async () => {
|
|
307
|
+
// Test with gh (GitHub CLI) which is commonly installed via homebrew
|
|
308
|
+
const result = await $`which gh`;
|
|
309
|
+
|
|
310
|
+
// Enable verbose mode to see debug info if this test fails
|
|
311
|
+
if (result.code !== 0) {
|
|
312
|
+
trace('BuiltinTest', 'DEBUG: which gh failed');
|
|
313
|
+
trace('BuiltinTest', () => `Exit code: ${result.code}`);
|
|
314
|
+
trace('BuiltinTest', () => `Stdout: ${JSON.stringify(result.stdout)}`);
|
|
315
|
+
trace('BuiltinTest', () => `Stderr: ${JSON.stringify(result.stderr)}`);
|
|
316
|
+
trace('BuiltinTest', () => `PATH: ${process.env.PATH}`);
|
|
317
|
+
|
|
318
|
+
// Try to find gh manually to confirm it exists
|
|
319
|
+
const manualCheck = await $`/usr/bin/which gh`.catch(() => ({
|
|
320
|
+
code: 1,
|
|
321
|
+
stdout: '',
|
|
322
|
+
stderr: 'manual which failed',
|
|
323
|
+
}));
|
|
324
|
+
trace(
|
|
325
|
+
'BuiltinTest',
|
|
326
|
+
() => `Manual /usr/bin/which result: ${manualCheck}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// If gh is installed, it should return 0, otherwise skip this test
|
|
331
|
+
// Note: We can't guarantee gh is installed on all systems
|
|
332
|
+
if (result.code === 0) {
|
|
333
|
+
expect(result.stdout).toMatch(/.*gh/);
|
|
334
|
+
} else {
|
|
335
|
+
trace(
|
|
336
|
+
'BuiltinTest',
|
|
337
|
+
'Skipping gh test - command not found or which implementation bug'
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('which should return non-zero for non-existent commands', async () => {
|
|
343
|
+
const result = await $`which nonexistent-command-12345`;
|
|
344
|
+
expect(result.code).toBe(1);
|
|
345
|
+
expect(result.stderr).toContain('no nonexistent-command-12345 in PATH');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('which should find built-in virtual commands', async () => {
|
|
349
|
+
const result = await $`which echo`;
|
|
350
|
+
expect(result.code).toBe(0);
|
|
351
|
+
expect(result.stdout).toContain('shell builtin');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('which should handle missing arguments', async () => {
|
|
355
|
+
const result = await $`which`;
|
|
356
|
+
expect(result.code).toBe(1);
|
|
357
|
+
expect(result.stderr).toContain('missing operand');
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
describe('Error Handling', () => {
|
|
362
|
+
test('commands should return proper exit codes', async () => {
|
|
363
|
+
const success = await $`true`;
|
|
364
|
+
expect(success.code).toBe(0);
|
|
365
|
+
|
|
366
|
+
const failure = await $`false`;
|
|
367
|
+
expect(failure.code).toBe(1);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('commands should provide helpful error messages', async () => {
|
|
371
|
+
const result = await $`cat nonexistent.txt`;
|
|
372
|
+
expect(result.code).toBe(1);
|
|
373
|
+
expect(result.stderr).toContain('cat:');
|
|
374
|
+
expect(result.stderr).toContain('nonexistent.txt');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('commands should handle missing operands', async () => {
|
|
378
|
+
const mkdirResult = await $`mkdir`;
|
|
379
|
+
expect(mkdirResult.code).toBe(1);
|
|
380
|
+
expect(mkdirResult.stderr).toContain('missing operand');
|
|
381
|
+
|
|
382
|
+
const rmResult = await $`rm`;
|
|
383
|
+
expect(rmResult.code).toBe(1);
|
|
384
|
+
expect(rmResult.stderr).toContain('missing operand');
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Bun runtime shell path issue fix
|
|
3
|
+
*
|
|
4
|
+
* This addresses GitHub issue #42: Bun runtime shell path issues
|
|
5
|
+
* The issue was that string interpolation in template literals was incorrectly quoting
|
|
6
|
+
* complete command strings, causing shell commands to fail execution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { test, expect, describe } from 'bun:test';
|
|
10
|
+
import { $ } from '../src/$.mjs';
|
|
11
|
+
|
|
12
|
+
// Test both Bun and Node.js runtimes
|
|
13
|
+
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
14
|
+
const runtime = isBun ? 'Bun' : 'Node.js';
|
|
15
|
+
|
|
16
|
+
// Platform detection - Some tests use Unix-specific paths and commands
|
|
17
|
+
const isWindows = process.platform === 'win32';
|
|
18
|
+
|
|
19
|
+
// Skip on Windows - tests reference /bin/sh and Unix paths
|
|
20
|
+
describe.skipIf(isWindows)(`String interpolation fix for ${runtime}`, () => {
|
|
21
|
+
test('Template literal without interpolation should work', async () => {
|
|
22
|
+
const result = await $`echo hello`;
|
|
23
|
+
expect(result.stdout.toString().trim()).toBe('hello');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('String interpolation with complete command should work', async () => {
|
|
27
|
+
const cmd = 'echo hello';
|
|
28
|
+
const result = await $`${cmd}`;
|
|
29
|
+
expect(result.stdout.toString().trim()).toBe('hello');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('String interpolation with command and args should work', async () => {
|
|
33
|
+
const cmd = 'echo "hello world"';
|
|
34
|
+
const result = await $`${cmd}`;
|
|
35
|
+
expect(result.stdout.toString().trim()).toBe('hello world');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('Mixed template literal with interpolation should work', async () => {
|
|
39
|
+
const arg = 'hello';
|
|
40
|
+
const result = await $`echo ${arg}`;
|
|
41
|
+
expect(result.stdout.toString().trim()).toBe('hello');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Complex shell commands via interpolation should work', async () => {
|
|
45
|
+
const cmd = 'echo hello | wc -w';
|
|
46
|
+
const result = await $`${cmd}`;
|
|
47
|
+
expect(result.stdout.toString().trim()).toBe('1');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('Shell operators in interpolated commands should work', async () => {
|
|
51
|
+
const cmd = 'test -f /bin/sh && echo "sh exists"';
|
|
52
|
+
const result = await $`${cmd}`;
|
|
53
|
+
expect(result.stdout.toString().trim()).toBe('sh exists');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('Commands with special characters should work', async () => {
|
|
57
|
+
const cmd = 'echo "test $HOME" | head -1';
|
|
58
|
+
const result = await $`${cmd}`;
|
|
59
|
+
expect(result.stdout.toString()).toContain('test ');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('Multiple argument interpolation should still quote properly', async () => {
|
|
63
|
+
const arg1 = 'hello';
|
|
64
|
+
const arg2 = 'world with spaces';
|
|
65
|
+
const result = await $`echo ${arg1} ${arg2}`;
|
|
66
|
+
expect(result.stdout.toString().trim()).toBe('hello world with spaces');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Empty command string should not cause issues', async () => {
|
|
70
|
+
const cmd = '';
|
|
71
|
+
// This should not fail catastrophically
|
|
72
|
+
try {
|
|
73
|
+
await $`${cmd}`;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// It's expected to fail, but not with our quoting issue
|
|
76
|
+
expect(error.message).not.toContain('not found');
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('Command with unsafe characters should be handled correctly', async () => {
|
|
81
|
+
// This tests that we don't break security when fixing the quoting issue
|
|
82
|
+
const unsafeCmd = 'echo "safe"; echo "also safe"';
|
|
83
|
+
const result = await $`${unsafeCmd}`;
|
|
84
|
+
const lines = result.stdout.toString().trim().split('\n');
|
|
85
|
+
expect(lines).toContain('safe');
|
|
86
|
+
expect(lines).toContain('also safe');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Additional runtime-specific tests
|
|
91
|
+
// Skip on Windows - uses 'pwd' which outputs Unix-style paths with '/'
|
|
92
|
+
if (isBun) {
|
|
93
|
+
describe.skipIf(isWindows)('Bun-specific shell path tests', () => {
|
|
94
|
+
test('Bun.spawn compatibility is maintained', async () => {
|
|
95
|
+
const result = await $`pwd`;
|
|
96
|
+
expect(result.stdout.toString().trim()).toContain('/');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('Bun runtime detection works correctly', async () => {
|
|
100
|
+
// This is more of a meta-test to ensure our runtime detection is correct
|
|
101
|
+
expect(typeof globalThis.Bun).toBe('object');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
describe.skipIf(isWindows)('Node.js-specific shell path tests', () => {
|
|
106
|
+
test('Node.js child_process compatibility is maintained', async () => {
|
|
107
|
+
const result = await $`pwd`;
|
|
108
|
+
expect(result.stdout.toString().trim()).toContain('/');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Node.js runtime detection works correctly', async () => {
|
|
112
|
+
expect(typeof process.version).toBe('string');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { test, expect, describe } from 'bun:test';
|
|
2
|
+
import './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
|
|
3
|
+
|
|
4
|
+
describe('Bun.$ Feature Validation', () => {
|
|
5
|
+
describe('Runtime Support', () => {
|
|
6
|
+
test('should only work in Bun runtime', () => {
|
|
7
|
+
expect(typeof Bun).toBe('object');
|
|
8
|
+
expect(typeof Bun.$).toBe('function');
|
|
9
|
+
}, 30000);
|
|
10
|
+
|
|
11
|
+
test('should be Bun-specific (conceptual test)', () => {
|
|
12
|
+
// Bun.$ is Bun-specific and won't work in Node.js
|
|
13
|
+
// This test documents the Bun-only limitation
|
|
14
|
+
// Since we're running in Bun, we just validate it exists
|
|
15
|
+
expect(typeof Bun.$).toBe('function');
|
|
16
|
+
// In Node.js environment, Bun would be undefined
|
|
17
|
+
}, 30000);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('Template Literals', () => {
|
|
21
|
+
test('should support $`cmd` syntax', async () => {
|
|
22
|
+
const result = await Bun.$`echo "bun template literal test"`;
|
|
23
|
+
expect(result.exitCode).toBe(0);
|
|
24
|
+
expect(result.text().trim()).toBe('bun template literal test');
|
|
25
|
+
}, 30000);
|
|
26
|
+
|
|
27
|
+
test('should support variable interpolation', async () => {
|
|
28
|
+
const message = 'bun interpolation test';
|
|
29
|
+
const result = await Bun.$`echo ${message}`;
|
|
30
|
+
expect(result.text().trim()).toContain('bun interpolation test');
|
|
31
|
+
}, 30000);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('Real-time Streaming', () => {
|
|
35
|
+
test('should NOT provide live streaming (buffers only)', async () => {
|
|
36
|
+
// Bun.$ buffers output and returns it all at once
|
|
37
|
+
// This test documents the buffering behavior
|
|
38
|
+
const result = await Bun.$`echo "bun buffered test"`;
|
|
39
|
+
|
|
40
|
+
// Result is returned as complete buffer, not streamed
|
|
41
|
+
expect(typeof result.text).toBe('function');
|
|
42
|
+
expect(result.text().trim()).toBe('bun buffered test');
|
|
43
|
+
|
|
44
|
+
// No streaming interface available
|
|
45
|
+
expect(result.stream).toBeUndefined();
|
|
46
|
+
expect(typeof result[Symbol.asyncIterator]).toBe('undefined');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('Async Iteration', () => {
|
|
51
|
+
test('should NOT support for await iteration', async () => {
|
|
52
|
+
const result = await Bun.$`echo "no async iteration"`;
|
|
53
|
+
|
|
54
|
+
// Bun.$ result is not async iterable
|
|
55
|
+
expect(typeof result[Symbol.asyncIterator]).toBe('undefined');
|
|
56
|
+
expect(result.stream).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('EventEmitter Pattern', () => {
|
|
61
|
+
test('should NOT support .on() events', async () => {
|
|
62
|
+
const result = await Bun.$`echo "no events"`;
|
|
63
|
+
|
|
64
|
+
// Bun.$ result doesn't have EventEmitter interface
|
|
65
|
+
expect(typeof result.on).toBe('undefined');
|
|
66
|
+
expect(typeof result.emit).toBe('undefined');
|
|
67
|
+
expect(typeof result.addEventListener).toBe('undefined');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('Mixed Patterns', () => {
|
|
72
|
+
test('should NOT support events + await (only await)', async () => {
|
|
73
|
+
// Bun.$ only supports await pattern, no events
|
|
74
|
+
const result = await Bun.$`echo "await only"`;
|
|
75
|
+
|
|
76
|
+
expect(result.text().trim()).toBe('await only');
|
|
77
|
+
expect(typeof result.on).toBe('undefined');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('Shell Injection Protection', () => {
|
|
82
|
+
test('should have built-in injection protection', async () => {
|
|
83
|
+
const dangerous = "'; rm -rf /; echo 'hacked";
|
|
84
|
+
const result = await Bun.$`echo ${dangerous}`;
|
|
85
|
+
|
|
86
|
+
expect(result.text().trim()).toBe(dangerous);
|
|
87
|
+
expect(result.exitCode).toBe(0);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('Cross-platform Support', () => {
|
|
92
|
+
test('should work cross-platform', async () => {
|
|
93
|
+
const result = await Bun.$`echo "cross-platform"`;
|
|
94
|
+
expect(result.exitCode).toBe(0);
|
|
95
|
+
expect(result.text().trim()).toBe('cross-platform');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('Performance', () => {
|
|
100
|
+
test('should be very fast (built-in to Bun)', async () => {
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
const result = await Bun.$`echo "performance test"`;
|
|
103
|
+
const endTime = Date.now();
|
|
104
|
+
|
|
105
|
+
expect(result.exitCode).toBe(0);
|
|
106
|
+
// Windows shell spawning is slower, allow more time
|
|
107
|
+
const maxTime = process.platform === 'win32' ? 500 : 100;
|
|
108
|
+
expect(endTime - startTime).toBeLessThan(maxTime); // Should be very fast
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('Memory Efficiency', () => {
|
|
113
|
+
test('should buffer in memory (not streaming)', async () => {
|
|
114
|
+
// Bun.$ buffers output in memory
|
|
115
|
+
const result = await Bun.$`echo "memory buffering"`;
|
|
116
|
+
|
|
117
|
+
// All output is available immediately as buffer
|
|
118
|
+
expect(result.text().trim()).toBe('memory buffering');
|
|
119
|
+
expect(typeof result.text()).toBe('string');
|
|
120
|
+
expect(result.stdout).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('Error Handling', () => {
|
|
125
|
+
test('should throw exception on error by default', async () => {
|
|
126
|
+
try {
|
|
127
|
+
await Bun.$`exit 42`;
|
|
128
|
+
expect(true).toBe(false); // Should not reach here
|
|
129
|
+
} catch (error) {
|
|
130
|
+
expect(error).toBeDefined();
|
|
131
|
+
expect(error.exitCode).toBe(42);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('should support nothrow option', async () => {
|
|
136
|
+
const result = await Bun.$`exit 42`.nothrow();
|
|
137
|
+
expect(result.exitCode).toBe(42);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Stdin Support', () => {
|
|
142
|
+
test('should support pipe operations', async () => {
|
|
143
|
+
// Bun.$ supports sophisticated pipe operations
|
|
144
|
+
const result = await Bun.$`echo "pipe test"`.text();
|
|
145
|
+
expect(result.trim()).toBe('pipe test');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should support input through echo/pipe (conceptual)', async () => {
|
|
149
|
+
// Bun.$ supports pipe operations differently
|
|
150
|
+
// const result = await Bun.$`echo ${input} | cat`;
|
|
151
|
+
const input = 'stdin input test';
|
|
152
|
+
const result = await Bun.$`echo ${input}`;
|
|
153
|
+
expect(result.text().trim()).toBe(input);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('Built-in Commands', () => {
|
|
158
|
+
test('should have built-in echo command', async () => {
|
|
159
|
+
// Bun.$ implements some commands natively for performance
|
|
160
|
+
const result = await Bun.$`echo "built-in echo"`;
|
|
161
|
+
expect(result.exitCode).toBe(0);
|
|
162
|
+
expect(result.text().trim()).toBe('built-in echo');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should have built-in cd and other commands', async () => {
|
|
166
|
+
// Bun.$ has built-in implementations of common commands
|
|
167
|
+
// This test validates that built-ins are available
|
|
168
|
+
const result = await Bun.$`pwd`;
|
|
169
|
+
expect(result.exitCode).toBe(0);
|
|
170
|
+
expect(result.text().trim().length).toBeGreaterThan(0);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('Bundle Size', () => {
|
|
175
|
+
test('should have 0KB bundle size (built into Bun)', () => {
|
|
176
|
+
// Bun.$ is built into the Bun runtime, no additional bundle size
|
|
177
|
+
expect(typeof Bun.$).toBe('function');
|
|
178
|
+
// This is a conceptual test - Bun.$ adds 0 bytes to bundle
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('TypeScript Support', () => {
|
|
183
|
+
test('should have built-in TypeScript support', () => {
|
|
184
|
+
// Bun has native TypeScript support including for Bun.$
|
|
185
|
+
expect(typeof Bun.$).toBe('function');
|
|
186
|
+
// In a TypeScript file, Bun.$ would have full type definitions
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|