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,622 @@
|
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
beforeTestCleanup,
|
|
4
|
+
afterTestCleanup,
|
|
5
|
+
originalCwd,
|
|
6
|
+
} from './test-cleanup.mjs';
|
|
7
|
+
import { $, shell, enableVirtualCommands } from '../src/$.mjs';
|
|
8
|
+
import {
|
|
9
|
+
mkdtempSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
existsSync,
|
|
14
|
+
realpathSync,
|
|
15
|
+
} from 'fs';
|
|
16
|
+
import { tmpdir, homedir } from 'os';
|
|
17
|
+
import { join, resolve } from 'path';
|
|
18
|
+
|
|
19
|
+
// Platform detection - Some tests use Unix-specific commands (cat, ln -s, chmod)
|
|
20
|
+
const isWindows = process.platform === 'win32';
|
|
21
|
+
|
|
22
|
+
// Helper to normalize paths (handles macOS /var -> /private/var symlink)
|
|
23
|
+
const normalizePath = (p) => {
|
|
24
|
+
try {
|
|
25
|
+
return realpathSync(p);
|
|
26
|
+
} catch {
|
|
27
|
+
return p;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Helper function to verify we're in the expected directory
|
|
32
|
+
function verifyCwd(expected, message) {
|
|
33
|
+
const actual = normalizePath(process.cwd());
|
|
34
|
+
const expectedNormalized = normalizePath(expected);
|
|
35
|
+
if (actual !== expectedNormalized) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`${message}: Expected cwd to be ${expectedNormalized}, but got ${actual}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Skip on Windows - uses pwd, cat, ln -s, chmod commands
|
|
43
|
+
describe.skipIf(isWindows)('cd Virtual Command - Core Behavior', () => {
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
await beforeTestCleanup();
|
|
46
|
+
shell.errexit(false);
|
|
47
|
+
shell.verbose(false);
|
|
48
|
+
shell.xtrace(false);
|
|
49
|
+
shell.pipefail(false);
|
|
50
|
+
shell.nounset(false);
|
|
51
|
+
enableVirtualCommands();
|
|
52
|
+
// Verify we start in the original directory
|
|
53
|
+
verifyCwd(originalCwd, 'Before test start');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
57
|
+
await afterTestCleanup();
|
|
58
|
+
// Verify we restored to original directory
|
|
59
|
+
verifyCwd(originalCwd, 'After test cleanup');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('should change to absolute path', async () => {
|
|
63
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-test-'));
|
|
64
|
+
const testStartCwd = process.cwd();
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// Verify we start in the original directory
|
|
68
|
+
verifyCwd(originalCwd, 'Test start');
|
|
69
|
+
|
|
70
|
+
const result = await $`cd ${tempDir}`;
|
|
71
|
+
expect(result.code).toBe(0);
|
|
72
|
+
expect(result.stdout).toBe(''); // cd should not output anything
|
|
73
|
+
expect(result.stderr).toBe('');
|
|
74
|
+
|
|
75
|
+
// Verify cd actually changed the directory
|
|
76
|
+
verifyCwd(tempDir, 'After cd to tempDir');
|
|
77
|
+
|
|
78
|
+
const pwd = await $`pwd`;
|
|
79
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(tempDir));
|
|
80
|
+
|
|
81
|
+
// Go back to original
|
|
82
|
+
await $`cd ${testStartCwd}`;
|
|
83
|
+
verifyCwd(testStartCwd, 'After cd back');
|
|
84
|
+
} finally {
|
|
85
|
+
// Ensure we're back in original before cleanup
|
|
86
|
+
if (process.cwd() !== testStartCwd) {
|
|
87
|
+
process.chdir(testStartCwd);
|
|
88
|
+
}
|
|
89
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('should change to relative path', async () => {
|
|
94
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-base-'));
|
|
95
|
+
const subDir = join(baseDir, 'subdir');
|
|
96
|
+
mkdirSync(subDir);
|
|
97
|
+
const originalCwd = process.cwd();
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await $`cd ${baseDir}`;
|
|
101
|
+
|
|
102
|
+
const result = await $`cd subdir`;
|
|
103
|
+
expect(result.code).toBe(0);
|
|
104
|
+
expect(result.stdout).toBe('');
|
|
105
|
+
|
|
106
|
+
const pwd = await $`pwd`;
|
|
107
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
|
|
108
|
+
|
|
109
|
+
await $`cd ${originalCwd}`;
|
|
110
|
+
} finally {
|
|
111
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should handle cd with no arguments (go to home)', async () => {
|
|
116
|
+
const originalCwd = process.cwd();
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const result = await $`cd`;
|
|
120
|
+
expect(result.code).toBe(0);
|
|
121
|
+
expect(result.stdout).toBe('');
|
|
122
|
+
|
|
123
|
+
const pwd = await $`pwd`;
|
|
124
|
+
const home = homedir();
|
|
125
|
+
expect(pwd.stdout.trim()).toBe(home);
|
|
126
|
+
|
|
127
|
+
await $`cd ${originalCwd}`;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
await $`cd ${originalCwd}`;
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should handle cd - (return to previous directory)', async () => {
|
|
135
|
+
const dir1 = mkdtempSync(join(tmpdir(), 'cd-dir1-'));
|
|
136
|
+
const dir2 = mkdtempSync(join(tmpdir(), 'cd-dir2-'));
|
|
137
|
+
const originalCwd = process.cwd();
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await $`cd ${dir1}`;
|
|
141
|
+
const pwd1 = await $`pwd`;
|
|
142
|
+
expect(normalizePath(pwd1.stdout.trim())).toBe(normalizePath(dir1));
|
|
143
|
+
|
|
144
|
+
await $`cd ${dir2}`;
|
|
145
|
+
const pwd2 = await $`pwd`;
|
|
146
|
+
expect(normalizePath(pwd2.stdout.trim())).toBe(normalizePath(dir2));
|
|
147
|
+
|
|
148
|
+
// Note: cd - might not be implemented in virtual command yet
|
|
149
|
+
// This test documents expected behavior
|
|
150
|
+
const result = await $`cd - 2>&1 || echo "not implemented"`;
|
|
151
|
+
|
|
152
|
+
await $`cd ${originalCwd}`;
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(dir1, { recursive: true, force: true });
|
|
155
|
+
rmSync(dir2, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should handle cd .. (parent directory)', async () => {
|
|
160
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-parent-'));
|
|
161
|
+
const subDir = join(baseDir, 'child');
|
|
162
|
+
mkdirSync(subDir);
|
|
163
|
+
const originalCwd = process.cwd();
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await $`cd ${subDir}`;
|
|
167
|
+
|
|
168
|
+
const result = await $`cd ..`;
|
|
169
|
+
expect(result.code).toBe(0);
|
|
170
|
+
expect(result.stdout).toBe('');
|
|
171
|
+
|
|
172
|
+
const pwd = await $`pwd`;
|
|
173
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(baseDir));
|
|
174
|
+
|
|
175
|
+
await $`cd ${originalCwd}`;
|
|
176
|
+
} finally {
|
|
177
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('should handle cd . (current directory)', async () => {
|
|
182
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-dot-'));
|
|
183
|
+
const originalCwd = process.cwd();
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await $`cd ${tempDir}`;
|
|
187
|
+
const pwdBefore = await $`pwd`;
|
|
188
|
+
|
|
189
|
+
const result = await $`cd .`;
|
|
190
|
+
expect(result.code).toBe(0);
|
|
191
|
+
expect(result.stdout).toBe('');
|
|
192
|
+
|
|
193
|
+
const pwdAfter = await $`pwd`;
|
|
194
|
+
expect(pwdAfter.stdout).toBe(pwdBefore.stdout);
|
|
195
|
+
|
|
196
|
+
await $`cd ${originalCwd}`;
|
|
197
|
+
} finally {
|
|
198
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('should fail with non-existent directory', async () => {
|
|
203
|
+
const nonExistent = '/this/path/should/not/exist/at/all';
|
|
204
|
+
const originalCwd = process.cwd();
|
|
205
|
+
|
|
206
|
+
const result = await $`cd ${nonExistent} 2>&1 || echo "failed"`;
|
|
207
|
+
expect(result.stdout).toContain('failed');
|
|
208
|
+
|
|
209
|
+
// Verify we're still in the same directory
|
|
210
|
+
const pwd = await $`pwd`;
|
|
211
|
+
expect(pwd.stdout.trim()).toBe(originalCwd);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('should handle paths with spaces', async () => {
|
|
215
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-spaces-'));
|
|
216
|
+
const dirWithSpaces = join(baseDir, 'my test directory');
|
|
217
|
+
mkdirSync(dirWithSpaces);
|
|
218
|
+
const originalCwd = process.cwd();
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const result = await $`cd ${dirWithSpaces}`;
|
|
222
|
+
expect(result.code).toBe(0);
|
|
223
|
+
expect(result.stdout).toBe('');
|
|
224
|
+
|
|
225
|
+
const pwd = await $`pwd`;
|
|
226
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(
|
|
227
|
+
normalizePath(dirWithSpaces)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await $`cd ${originalCwd}`;
|
|
231
|
+
} finally {
|
|
232
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('should handle special characters in paths', async () => {
|
|
237
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-special-'));
|
|
238
|
+
// Create directory with special characters (but valid for filesystem)
|
|
239
|
+
const specialDir = join(baseDir, 'test-dir_123');
|
|
240
|
+
mkdirSync(specialDir);
|
|
241
|
+
const originalCwd = process.cwd();
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const result = await $`cd ${specialDir}`;
|
|
245
|
+
expect(result.code).toBe(0);
|
|
246
|
+
expect(result.stdout).toBe('');
|
|
247
|
+
|
|
248
|
+
const pwd = await $`pwd`;
|
|
249
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(specialDir));
|
|
250
|
+
|
|
251
|
+
await $`cd ${originalCwd}`;
|
|
252
|
+
} finally {
|
|
253
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Skip on Windows - uses pwd and cat commands
|
|
259
|
+
describe.skipIf(isWindows)('cd Virtual Command - Command Chains', () => {
|
|
260
|
+
beforeEach(async () => {
|
|
261
|
+
await beforeTestCleanup();
|
|
262
|
+
shell.errexit(false);
|
|
263
|
+
shell.verbose(false);
|
|
264
|
+
shell.xtrace(false);
|
|
265
|
+
shell.pipefail(false);
|
|
266
|
+
shell.nounset(false);
|
|
267
|
+
enableVirtualCommands();
|
|
268
|
+
verifyCwd(originalCwd, 'Before test start');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
afterEach(async () => {
|
|
272
|
+
await afterTestCleanup();
|
|
273
|
+
verifyCwd(originalCwd, 'After test cleanup');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('should persist directory change within command chain', async () => {
|
|
277
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-chain-'));
|
|
278
|
+
const originalCwd = process.cwd();
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
// Create a test file in temp directory
|
|
282
|
+
writeFileSync(join(tempDir, 'test.txt'), 'test content');
|
|
283
|
+
|
|
284
|
+
// cd and run command in same chain
|
|
285
|
+
const result = await $`cd ${tempDir} && cat test.txt`;
|
|
286
|
+
expect(result.code).toBe(0);
|
|
287
|
+
expect(result.stdout.trim()).toBe('test content');
|
|
288
|
+
|
|
289
|
+
await $`cd ${originalCwd}`;
|
|
290
|
+
} finally {
|
|
291
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('should handle multiple cd commands in chain', async () => {
|
|
296
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-multi-'));
|
|
297
|
+
const dir1 = join(baseDir, 'dir1');
|
|
298
|
+
const dir2 = join(baseDir, 'dir2');
|
|
299
|
+
mkdirSync(dir1);
|
|
300
|
+
mkdirSync(dir2);
|
|
301
|
+
const originalCwd = process.cwd();
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
writeFileSync(join(dir1, 'file1.txt'), 'content1');
|
|
305
|
+
writeFileSync(join(dir2, 'file2.txt'), 'content2');
|
|
306
|
+
|
|
307
|
+
// Chain multiple cd commands
|
|
308
|
+
const result =
|
|
309
|
+
await $`cd ${dir1} && cat file1.txt && cd ${dir2} && cat file2.txt`;
|
|
310
|
+
expect(result.code).toBe(0);
|
|
311
|
+
expect(result.stdout).toContain('content1');
|
|
312
|
+
expect(result.stdout).toContain('content2');
|
|
313
|
+
|
|
314
|
+
await $`cd ${originalCwd}`;
|
|
315
|
+
} finally {
|
|
316
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('should work with git commands in chain', async () => {
|
|
321
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-git-'));
|
|
322
|
+
const originalCwd = process.cwd();
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const result = await $`cd ${tempDir} && git init`;
|
|
326
|
+
expect(result.code).toBe(0);
|
|
327
|
+
// Git init outputs to stderr
|
|
328
|
+
const output = (result.stdout + result.stderr).toLowerCase();
|
|
329
|
+
expect(output).toContain('initialized');
|
|
330
|
+
|
|
331
|
+
// Verify git repo was created in the right place
|
|
332
|
+
expect(existsSync(join(tempDir, '.git'))).toBe(true);
|
|
333
|
+
|
|
334
|
+
await $`cd ${originalCwd}`;
|
|
335
|
+
} finally {
|
|
336
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('should maintain separate directory context per command when not chained', async () => {
|
|
341
|
+
const dir1 = mkdtempSync(join(tmpdir(), 'cd-ctx1-'));
|
|
342
|
+
const dir2 = mkdtempSync(join(tmpdir(), 'cd-ctx2-'));
|
|
343
|
+
const originalCwd = process.cwd();
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
// First command changes to dir1
|
|
347
|
+
await $`cd ${dir1}`;
|
|
348
|
+
const pwd1 = await $`pwd`;
|
|
349
|
+
expect(normalizePath(pwd1.stdout.trim())).toBe(normalizePath(dir1));
|
|
350
|
+
|
|
351
|
+
// Second separate command should still be in dir1
|
|
352
|
+
const pwd2 = await $`pwd`;
|
|
353
|
+
expect(normalizePath(pwd2.stdout.trim())).toBe(normalizePath(dir1));
|
|
354
|
+
|
|
355
|
+
await $`cd ${originalCwd}`;
|
|
356
|
+
} finally {
|
|
357
|
+
rmSync(dir1, { recursive: true, force: true });
|
|
358
|
+
rmSync(dir2, { recursive: true, force: true });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Skip on Windows - uses subshells (parentheses) and pwd/cat commands
|
|
364
|
+
describe.skipIf(isWindows)('cd Virtual Command - Subshell Behavior', () => {
|
|
365
|
+
beforeEach(async () => {
|
|
366
|
+
await beforeTestCleanup();
|
|
367
|
+
shell.errexit(false);
|
|
368
|
+
shell.verbose(false);
|
|
369
|
+
shell.xtrace(false);
|
|
370
|
+
shell.pipefail(false);
|
|
371
|
+
shell.nounset(false);
|
|
372
|
+
enableVirtualCommands();
|
|
373
|
+
verifyCwd(originalCwd, 'Before test start');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
afterEach(async () => {
|
|
377
|
+
await afterTestCleanup();
|
|
378
|
+
verifyCwd(originalCwd, 'After test cleanup');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('should not affect parent shell when in subshell', async () => {
|
|
382
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-subshell-'));
|
|
383
|
+
const originalCwd = process.cwd();
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
// Run cd in subshell (parentheses)
|
|
387
|
+
await $`(cd ${tempDir})`;
|
|
388
|
+
|
|
389
|
+
// Parent shell should still be in original directory
|
|
390
|
+
const pwd = await $`pwd`;
|
|
391
|
+
expect(pwd.stdout.trim()).toBe(originalCwd);
|
|
392
|
+
} finally {
|
|
393
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('should work in subshell with other commands', async () => {
|
|
398
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-subshell-cmd-'));
|
|
399
|
+
const originalCwd = process.cwd();
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
writeFileSync(join(tempDir, 'test.txt'), 'subshell test');
|
|
403
|
+
|
|
404
|
+
// Run commands in subshell
|
|
405
|
+
const result = await $`(cd ${tempDir} && cat test.txt)`;
|
|
406
|
+
expect(result.code).toBe(0);
|
|
407
|
+
expect(result.stdout.trim()).toBe('subshell test');
|
|
408
|
+
|
|
409
|
+
// Verify we're still in original directory
|
|
410
|
+
const pwd = await $`pwd`;
|
|
411
|
+
expect(pwd.stdout.trim()).toBe(originalCwd);
|
|
412
|
+
} finally {
|
|
413
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Skip on Windows - uses ln -s, chmod, and pwd commands
|
|
419
|
+
describe.skipIf(isWindows)('cd Virtual Command - Edge Cases', () => {
|
|
420
|
+
beforeEach(async () => {
|
|
421
|
+
await beforeTestCleanup();
|
|
422
|
+
shell.errexit(false);
|
|
423
|
+
shell.verbose(false);
|
|
424
|
+
shell.xtrace(false);
|
|
425
|
+
shell.pipefail(false);
|
|
426
|
+
shell.nounset(false);
|
|
427
|
+
enableVirtualCommands();
|
|
428
|
+
verifyCwd(originalCwd, 'Before test start');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
afterEach(async () => {
|
|
432
|
+
await afterTestCleanup();
|
|
433
|
+
verifyCwd(originalCwd, 'After test cleanup');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('should handle symlinks', async () => {
|
|
437
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-symlink-'));
|
|
438
|
+
const realDir = join(baseDir, 'real');
|
|
439
|
+
const linkDir = join(baseDir, 'link');
|
|
440
|
+
mkdirSync(realDir);
|
|
441
|
+
const originalCwd = process.cwd();
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
// Create symlink
|
|
445
|
+
await $`ln -s ${realDir} ${linkDir}`;
|
|
446
|
+
|
|
447
|
+
// cd through symlink
|
|
448
|
+
const result = await $`cd ${linkDir}`;
|
|
449
|
+
expect(result.code).toBe(0);
|
|
450
|
+
|
|
451
|
+
// pwd should show the symlink path (default behavior)
|
|
452
|
+
const pwd = await $`pwd`;
|
|
453
|
+
// Note: behavior may vary between pwd and pwd -P
|
|
454
|
+
expect(pwd.stdout.trim()).toBeTruthy();
|
|
455
|
+
|
|
456
|
+
await $`cd ${originalCwd}`;
|
|
457
|
+
} finally {
|
|
458
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('should handle very long paths', async () => {
|
|
463
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-long-'));
|
|
464
|
+
const originalCwd = process.cwd();
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
// Create deeply nested directory
|
|
468
|
+
let currentPath = baseDir;
|
|
469
|
+
for (let i = 0; i < 10; i++) {
|
|
470
|
+
currentPath = join(currentPath, `level${i}`);
|
|
471
|
+
mkdirSync(currentPath);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const result = await $`cd ${currentPath}`;
|
|
475
|
+
expect(result.code).toBe(0);
|
|
476
|
+
|
|
477
|
+
const pwd = await $`pwd`;
|
|
478
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(currentPath));
|
|
479
|
+
|
|
480
|
+
await $`cd ${originalCwd}`;
|
|
481
|
+
} finally {
|
|
482
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test('should handle permission errors gracefully', async () => {
|
|
487
|
+
// Skip on Windows where permissions work differently
|
|
488
|
+
if (process.platform === 'win32') {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-perm-'));
|
|
493
|
+
const restrictedDir = join(baseDir, 'restricted');
|
|
494
|
+
mkdirSync(restrictedDir);
|
|
495
|
+
const originalCwd = process.cwd();
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
// Remove execute permission
|
|
499
|
+
await $`chmod 000 ${restrictedDir}`;
|
|
500
|
+
|
|
501
|
+
const result =
|
|
502
|
+
await $`cd ${restrictedDir} 2>&1 || echo "permission denied"`;
|
|
503
|
+
expect(result.stdout.toLowerCase()).toContain('denied');
|
|
504
|
+
|
|
505
|
+
// Should still be in original directory
|
|
506
|
+
const pwd = await $`pwd`;
|
|
507
|
+
expect(pwd.stdout.trim()).toBe(originalCwd);
|
|
508
|
+
} finally {
|
|
509
|
+
// Restore permissions for cleanup - need to restore parent dir permission first
|
|
510
|
+
try {
|
|
511
|
+
await $`chmod 755 ${baseDir} 2>/dev/null || true`;
|
|
512
|
+
await $`chmod 755 ${restrictedDir} 2>/dev/null || true`;
|
|
513
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
514
|
+
} catch (e) {
|
|
515
|
+
// If cleanup fails, try with sudo as last resort
|
|
516
|
+
await $`sudo rm -rf ${baseDir} 2>/dev/null || true`.catch(() => {});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
test('should handle cd with trailing slash', async () => {
|
|
522
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'cd-slash-'));
|
|
523
|
+
const originalCwd = process.cwd();
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
// Test with trailing slash
|
|
527
|
+
const result = await $`cd ${tempDir}/`;
|
|
528
|
+
expect(result.code).toBe(0);
|
|
529
|
+
expect(result.stdout).toBe('');
|
|
530
|
+
|
|
531
|
+
const pwd = await $`pwd`;
|
|
532
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(tempDir));
|
|
533
|
+
|
|
534
|
+
await $`cd ${originalCwd}`;
|
|
535
|
+
} finally {
|
|
536
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test('should handle cd with multiple slashes', async () => {
|
|
541
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-slashes-'));
|
|
542
|
+
const subDir = join(baseDir, 'sub');
|
|
543
|
+
mkdirSync(subDir);
|
|
544
|
+
const originalCwd = process.cwd();
|
|
545
|
+
|
|
546
|
+
try {
|
|
547
|
+
// Test with multiple slashes (should normalize)
|
|
548
|
+
const result = await $`cd ${baseDir}//sub///`;
|
|
549
|
+
expect(result.code).toBe(0);
|
|
550
|
+
|
|
551
|
+
const pwd = await $`pwd`;
|
|
552
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
|
|
553
|
+
|
|
554
|
+
await $`cd ${originalCwd}`;
|
|
555
|
+
} finally {
|
|
556
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// Skip on Windows - uses pwd command
|
|
562
|
+
describe.skipIf(isWindows)(
|
|
563
|
+
'cd Virtual Command - Platform Compatibility',
|
|
564
|
+
() => {
|
|
565
|
+
beforeEach(async () => {
|
|
566
|
+
await beforeTestCleanup();
|
|
567
|
+
shell.errexit(false);
|
|
568
|
+
shell.verbose(false);
|
|
569
|
+
shell.xtrace(false);
|
|
570
|
+
shell.pipefail(false);
|
|
571
|
+
shell.nounset(false);
|
|
572
|
+
enableVirtualCommands();
|
|
573
|
+
verifyCwd(originalCwd, 'Before test start');
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
afterEach(async () => {
|
|
577
|
+
await afterTestCleanup();
|
|
578
|
+
verifyCwd(originalCwd, 'After test cleanup');
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test('should handle platform-specific path separators', async () => {
|
|
582
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-platform-'));
|
|
583
|
+
const subDir = join(baseDir, 'cross', 'platform', 'test');
|
|
584
|
+
mkdirSync(subDir, { recursive: true });
|
|
585
|
+
const originalCwd = process.cwd();
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
// Use platform-specific path
|
|
589
|
+
const result = await $`cd ${subDir}`;
|
|
590
|
+
expect(result.code).toBe(0);
|
|
591
|
+
|
|
592
|
+
const pwd = await $`pwd`;
|
|
593
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(subDir));
|
|
594
|
+
|
|
595
|
+
await $`cd ${originalCwd}`;
|
|
596
|
+
} finally {
|
|
597
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test('should normalize paths correctly', async () => {
|
|
602
|
+
const baseDir = mkdtempSync(join(tmpdir(), 'cd-normalize-'));
|
|
603
|
+
const sub1 = join(baseDir, 'sub1');
|
|
604
|
+
const sub2 = join(sub1, 'sub2');
|
|
605
|
+
mkdirSync(sub2, { recursive: true });
|
|
606
|
+
const originalCwd = process.cwd();
|
|
607
|
+
|
|
608
|
+
try {
|
|
609
|
+
// Test path with ./ and ../
|
|
610
|
+
await $`cd ${baseDir}`;
|
|
611
|
+
await $`cd ./sub1/../sub1/sub2`;
|
|
612
|
+
|
|
613
|
+
const pwd = await $`pwd`;
|
|
614
|
+
expect(normalizePath(pwd.stdout.trim())).toBe(normalizePath(sub2));
|
|
615
|
+
|
|
616
|
+
await $`cd ${originalCwd}`;
|
|
617
|
+
} finally {
|
|
618
|
+
rmSync(baseDir, { recursive: true, force: true });
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
);
|