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.
Files changed (500) hide show
  1. package/js/examples/01-basic-streaming.mjs +14 -0
  2. package/js/examples/02-async-iterator.mjs +9 -0
  3. package/js/examples/03-file-and-console.mjs +16 -0
  4. package/js/examples/04-claude-jq-pipe.mjs +16 -0
  5. package/js/examples/CI-DEBUG-README.md +138 -0
  6. package/js/examples/README-examples.mjs +111 -0
  7. package/js/examples/README.md +345 -0
  8. package/js/examples/STREAMING_INTERFACES_SUMMARY.md +116 -0
  9. package/js/examples/add-test-timeouts.js +107 -0
  10. package/js/examples/ansi-default-preserved.mjs +11 -0
  11. package/js/examples/ansi-global-config.mjs +12 -0
  12. package/js/examples/ansi-reset-default.mjs +12 -0
  13. package/js/examples/ansi-strip-utils.mjs +12 -0
  14. package/js/examples/baseline-child-process.mjs +23 -0
  15. package/js/examples/baseline-claude-test.mjs +47 -0
  16. package/js/examples/baseline-working.mjs +34 -0
  17. package/js/examples/capture-mirror-comparison.mjs +23 -0
  18. package/js/examples/capture-mirror-default.mjs +11 -0
  19. package/js/examples/capture-mirror-performance.mjs +16 -0
  20. package/js/examples/capture-mirror-show-only.mjs +16 -0
  21. package/js/examples/capture-mirror-silent-processing.mjs +16 -0
  22. package/js/examples/ci-debug-baseline-vs-library.mjs +265 -0
  23. package/js/examples/ci-debug-es-module-loading.mjs +184 -0
  24. package/js/examples/ci-debug-signal-handling.mjs +225 -0
  25. package/js/examples/ci-debug-stdout-buffering.mjs +94 -0
  26. package/js/examples/ci-debug-test-timeouts.mjs +259 -0
  27. package/js/examples/claude-exact-file-output.mjs +20 -0
  28. package/js/examples/claude-exact-jq.mjs +13 -0
  29. package/js/examples/claude-exact-streaming.mjs +13 -0
  30. package/js/examples/claude-jq-pipeline.mjs +13 -0
  31. package/js/examples/claude-json-stream.mjs +39 -0
  32. package/js/examples/claude-streaming-basic.mjs +99 -0
  33. package/js/examples/claude-streaming-demo.mjs +126 -0
  34. package/js/examples/claude-streaming-final.mjs +20 -0
  35. package/js/examples/cleanup-verification-test.mjs +115 -0
  36. package/js/examples/colors-buffer-processing.mjs +14 -0
  37. package/js/examples/colors-default-preserved.mjs +15 -0
  38. package/js/examples/colors-per-command-config.mjs +15 -0
  39. package/js/examples/colors-strip-ansi.mjs +13 -0
  40. package/js/examples/commandstream-jq.mjs +29 -0
  41. package/js/examples/commandstream-working.mjs +23 -0
  42. package/js/examples/comprehensive-streams-demo.mjs +121 -0
  43. package/js/examples/ctrl-c-concurrent-processes.mjs +20 -0
  44. package/js/examples/ctrl-c-long-running-command.mjs +20 -0
  45. package/js/examples/ctrl-c-real-system-command.mjs +17 -0
  46. package/js/examples/ctrl-c-sleep-command.mjs +17 -0
  47. package/js/examples/ctrl-c-stdin-forwarding.mjs +20 -0
  48. package/js/examples/ctrl-c-virtual-command.mjs +17 -0
  49. package/js/examples/debug-already-started.mjs +20 -0
  50. package/js/examples/debug-ansi-processing.mjs +42 -0
  51. package/js/examples/debug-basic-streaming.mjs +44 -0
  52. package/js/examples/debug-buildshellcommand.mjs +82 -0
  53. package/js/examples/debug-child-process.mjs +43 -0
  54. package/js/examples/debug-child-state.mjs +55 -0
  55. package/js/examples/debug-chunking.mjs +38 -0
  56. package/js/examples/debug-command-parsing.mjs +61 -0
  57. package/js/examples/debug-complete-consolidation.mjs +97 -0
  58. package/js/examples/debug-echo-args.mjs +22 -0
  59. package/js/examples/debug-emit-timing.mjs +28 -0
  60. package/js/examples/debug-end-event.mjs +56 -0
  61. package/js/examples/debug-errexit.mjs +16 -0
  62. package/js/examples/debug-event-emission.mjs +40 -0
  63. package/js/examples/debug-event-timing.mjs +67 -0
  64. package/js/examples/debug-event-vs-result.mjs +30 -0
  65. package/js/examples/debug-exact-command.mjs +28 -0
  66. package/js/examples/debug-exact-test-scenario.mjs +44 -0
  67. package/js/examples/debug-execution-path.mjs +27 -0
  68. package/js/examples/debug-exit-command.mjs +38 -0
  69. package/js/examples/debug-exit-virtual.mjs +54 -0
  70. package/js/examples/debug-finish-consolidation.mjs +44 -0
  71. package/js/examples/debug-force-cleanup.mjs +47 -0
  72. package/js/examples/debug-getter-basic.mjs +13 -0
  73. package/js/examples/debug-getter-direct.mjs +23 -0
  74. package/js/examples/debug-getter-internals.mjs +61 -0
  75. package/js/examples/debug-handler-detection.mjs +65 -0
  76. package/js/examples/debug-idempotent-finish.mjs +54 -0
  77. package/js/examples/debug-idempotent-kill.mjs +58 -0
  78. package/js/examples/debug-interpolation-individual.mjs +88 -0
  79. package/js/examples/debug-interpolation-issue.mjs +101 -0
  80. package/js/examples/debug-jq-streaming.mjs +57 -0
  81. package/js/examples/debug-jq-tty-colors.mjs +168 -0
  82. package/js/examples/debug-kill-cleanup.mjs +56 -0
  83. package/js/examples/debug-kill-method.mjs +33 -0
  84. package/js/examples/debug-listener-interference.mjs +38 -0
  85. package/js/examples/debug-listener-lifecycle.mjs +50 -0
  86. package/js/examples/debug-listener-timing.mjs +62 -0
  87. package/js/examples/debug-listeners-property.mjs +34 -0
  88. package/js/examples/debug-map-methods.mjs +39 -0
  89. package/js/examples/debug-not-awaited-cleanup.mjs +106 -0
  90. package/js/examples/debug-off-method.mjs +28 -0
  91. package/js/examples/debug-option-merging.mjs +17 -0
  92. package/js/examples/debug-options.mjs +36 -0
  93. package/js/examples/debug-output.mjs +25 -0
  94. package/js/examples/debug-pattern-matching.mjs +69 -0
  95. package/js/examples/debug-pipeline-cat.mjs +28 -0
  96. package/js/examples/debug-pipeline-cleanup.mjs +71 -0
  97. package/js/examples/debug-pipeline-error-detailed.mjs +78 -0
  98. package/js/examples/debug-pipeline-error.mjs +65 -0
  99. package/js/examples/debug-pipeline-issue.mjs +26 -0
  100. package/js/examples/debug-pipeline-method.mjs +22 -0
  101. package/js/examples/debug-pipeline-stream.mjs +66 -0
  102. package/js/examples/debug-pipeline.mjs +14 -0
  103. package/js/examples/debug-process-exit-trace.mjs +41 -0
  104. package/js/examples/debug-process-path.mjs +38 -0
  105. package/js/examples/debug-property-check.mjs +29 -0
  106. package/js/examples/debug-resource-cleanup.mjs +83 -0
  107. package/js/examples/debug-shell-args.mjs +103 -0
  108. package/js/examples/debug-sigint-child-handler.mjs +44 -0
  109. package/js/examples/debug-sigint-forwarding.mjs +61 -0
  110. package/js/examples/debug-sigint-handler-install.mjs +79 -0
  111. package/js/examples/debug-sigint-handler-order.mjs +85 -0
  112. package/js/examples/debug-sigint-listeners.mjs +48 -0
  113. package/js/examples/debug-sigint-start-pattern.mjs +62 -0
  114. package/js/examples/debug-sigint-timer.mjs +40 -0
  115. package/js/examples/debug-simple-command.mjs +49 -0
  116. package/js/examples/debug-simple-getter.mjs +30 -0
  117. package/js/examples/debug-simple.mjs +39 -0
  118. package/js/examples/debug-simplified-finished.mjs +102 -0
  119. package/js/examples/debug-stack-overflow.mjs +25 -0
  120. package/js/examples/debug-stdin-simple.mjs +28 -0
  121. package/js/examples/debug-stdin.mjs +28 -0
  122. package/js/examples/debug-stream-emitter-isolated.mjs +45 -0
  123. package/js/examples/debug-stream-emitter.mjs +25 -0
  124. package/js/examples/debug-stream-events.mjs +70 -0
  125. package/js/examples/debug-stream-generator.mjs +23 -0
  126. package/js/examples/debug-stream-getter-issue.mjs +37 -0
  127. package/js/examples/debug-stream-getter.mjs +19 -0
  128. package/js/examples/debug-stream-internals.mjs +64 -0
  129. package/js/examples/debug-stream-method.mjs +46 -0
  130. package/js/examples/debug-stream-object.mjs +58 -0
  131. package/js/examples/debug-stream-properties.mjs +37 -0
  132. package/js/examples/debug-stream-timing.mjs +28 -0
  133. package/js/examples/debug-streaming.mjs +24 -0
  134. package/js/examples/debug-test-isolation.mjs +54 -0
  135. package/js/examples/debug-test-state.mjs +27 -0
  136. package/js/examples/debug-user-sigint.mjs +19 -0
  137. package/js/examples/debug-virtual-disable.mjs +21 -0
  138. package/js/examples/debug-virtual-vs-real.mjs +68 -0
  139. package/js/examples/debug-with-trace.mjs +23 -0
  140. package/js/examples/debug_parent_stream.mjs +22 -0
  141. package/js/examples/emulate-claude-stream.mjs +30 -0
  142. package/js/examples/emulated-streaming-direct.mjs +22 -0
  143. package/js/examples/emulated-streaming-jq-pipe.mjs +22 -0
  144. package/js/examples/emulated-streaming-sh-pipe.mjs +23 -0
  145. package/js/examples/events-build-process.mjs +73 -0
  146. package/js/examples/events-concurrent-streams.mjs +51 -0
  147. package/js/examples/events-error-handling.mjs +59 -0
  148. package/js/examples/events-file-monitoring.mjs +66 -0
  149. package/js/examples/events-interactive-simulation.mjs +69 -0
  150. package/js/examples/events-log-processing.mjs +72 -0
  151. package/js/examples/events-network-monitoring.mjs +68 -0
  152. package/js/examples/events-ping-basic.mjs +37 -0
  153. package/js/examples/events-progress-tracking.mjs +55 -0
  154. package/js/examples/events-stdin-input.mjs +34 -0
  155. package/js/examples/example-ansi-ls.mjs +39 -0
  156. package/js/examples/example-top.mjs +27 -0
  157. package/js/examples/final-ping-stdin-proof.mjs +88 -0
  158. package/js/examples/final-test-shell-operators.mjs +123 -0
  159. package/js/examples/final-working-examples.mjs +162 -0
  160. package/js/examples/gh-auth-test.mjs +103 -0
  161. package/js/examples/gh-delete-hang-test.mjs +79 -0
  162. package/js/examples/gh-gist-creation-test.mjs +276 -0
  163. package/js/examples/gh-gist-minimal-test.mjs +89 -0
  164. package/js/examples/gh-hang-exact-original.mjs +83 -0
  165. package/js/examples/gh-hang-reproduction.mjs +151 -0
  166. package/js/examples/gh-hang-test-with-redirect.mjs +45 -0
  167. package/js/examples/gh-hang-test-without-redirect.mjs +43 -0
  168. package/js/examples/gh-minimal-hang-check.mjs +33 -0
  169. package/js/examples/gh-operations-with-cd.mjs +187 -0
  170. package/js/examples/gh-output-test.mjs +102 -0
  171. package/js/examples/gh-reproduce-hang.mjs +41 -0
  172. package/js/examples/git-operations-with-cd.mjs +186 -0
  173. package/js/examples/interactive-math-calc.mjs +45 -0
  174. package/js/examples/interactive-top-fixed.mjs +25 -0
  175. package/js/examples/interactive-top-improved.mjs +72 -0
  176. package/js/examples/interactive-top-pty-logging.mjs +69 -0
  177. package/js/examples/interactive-top-pty.mjs +27 -0
  178. package/js/examples/interactive-top-with-logging.mjs +56 -0
  179. package/js/examples/interactive-top.mjs +29 -0
  180. package/js/examples/jq-color-demo.mjs +53 -0
  181. package/js/examples/jq-colors-streaming.mjs +42 -0
  182. package/js/examples/manual-ctrl-c-test.mjs +50 -0
  183. package/js/examples/methods-multiple-options.mjs +25 -0
  184. package/js/examples/methods-run-basic.mjs +10 -0
  185. package/js/examples/methods-start-basic.mjs +10 -0
  186. package/js/examples/node-compat-data-events.mjs +36 -0
  187. package/js/examples/node-compat-readable-event.mjs +29 -0
  188. package/js/examples/node-compat-small-buffer.mjs +33 -0
  189. package/js/examples/options-capture-false.mjs +12 -0
  190. package/js/examples/options-combined-settings.mjs +13 -0
  191. package/js/examples/options-custom-input.mjs +16 -0
  192. package/js/examples/options-default-behavior.mjs +10 -0
  193. package/js/examples/options-maximum-performance.mjs +15 -0
  194. package/js/examples/options-mirror-false.mjs +10 -0
  195. package/js/examples/options-performance-mode.mjs +13 -0
  196. package/js/examples/options-performance-optimization.mjs +14 -0
  197. package/js/examples/options-run-alias-demo.mjs +15 -0
  198. package/js/examples/options-run-alias.mjs +10 -0
  199. package/js/examples/options-silent-execution.mjs +14 -0
  200. package/js/examples/options-streaming-capture.mjs +24 -0
  201. package/js/examples/options-streaming-multiple.mjs +35 -0
  202. package/js/examples/options-streaming-silent.mjs +21 -0
  203. package/js/examples/options-streaming-stdin.mjs +21 -0
  204. package/js/examples/ping-streaming-filtered.mjs +22 -0
  205. package/js/examples/ping-streaming-interruptible.mjs +47 -0
  206. package/js/examples/ping-streaming-silent.mjs +24 -0
  207. package/js/examples/ping-streaming-simple.mjs +13 -0
  208. package/js/examples/ping-streaming-statistics.mjs +49 -0
  209. package/js/examples/ping-streaming-timestamps.mjs +22 -0
  210. package/js/examples/ping-streaming.mjs +48 -0
  211. package/js/examples/prove-ping-stdin-limitation.mjs +94 -0
  212. package/js/examples/readme-example.mjs +39 -0
  213. package/js/examples/realtime-json-stream.mjs +143 -0
  214. package/js/examples/reliable-stdin-commands.mjs +135 -0
  215. package/js/examples/reproduce-issue-135-v2.mjs +15 -0
  216. package/js/examples/reproduce-issue-135.mjs +17 -0
  217. package/js/examples/shell-cd-behavior.mjs +88 -0
  218. package/js/examples/sigint-forwarding-test.mjs +60 -0
  219. package/js/examples/sigint-handler-test.mjs +72 -0
  220. package/js/examples/simple-async-test.mjs +49 -0
  221. package/js/examples/simple-claude-test.mjs +17 -0
  222. package/js/examples/simple-event-test.mjs +33 -0
  223. package/js/examples/simple-jq-streaming.mjs +48 -0
  224. package/js/examples/simple-stream-demo.mjs +35 -0
  225. package/js/examples/simple-test-sleep.js +30 -0
  226. package/js/examples/simple-working-stdin.mjs +30 -0
  227. package/js/examples/streaming-behavior-test.mjs +116 -0
  228. package/js/examples/streaming-direct-command.mjs +21 -0
  229. package/js/examples/streaming-filtered-output.mjs +33 -0
  230. package/js/examples/streaming-grep-pipeline.mjs +21 -0
  231. package/js/examples/streaming-interactive-stdin.mjs +24 -0
  232. package/js/examples/streaming-jq-pipeline.mjs +23 -0
  233. package/js/examples/streaming-multistage-pipeline.mjs +23 -0
  234. package/js/examples/streaming-pipes-event-pattern.mjs +27 -0
  235. package/js/examples/streaming-pipes-multistage.mjs +22 -0
  236. package/js/examples/streaming-pipes-realtime-jq.mjs +23 -0
  237. package/js/examples/streaming-progress-tracking.mjs +34 -0
  238. package/js/examples/streaming-reusable-configs.mjs +52 -0
  239. package/js/examples/streaming-silent-capture.mjs +20 -0
  240. package/js/examples/streaming-test-simple.mjs +70 -0
  241. package/js/examples/streaming-virtual-pipeline.mjs +18 -0
  242. package/js/examples/syntax-basic-comparison.mjs +31 -0
  243. package/js/examples/syntax-basic-options.mjs +12 -0
  244. package/js/examples/syntax-combined-options.mjs +19 -0
  245. package/js/examples/syntax-command-chaining.mjs +12 -0
  246. package/js/examples/syntax-custom-directory.mjs +10 -0
  247. package/js/examples/syntax-custom-environment.mjs +13 -0
  248. package/js/examples/syntax-custom-stdin.mjs +10 -0
  249. package/js/examples/syntax-mixed-regular.mjs +11 -0
  250. package/js/examples/syntax-mixed-usage.mjs +15 -0
  251. package/js/examples/syntax-multiple-listeners.mjs +87 -0
  252. package/js/examples/syntax-piping-comparison.mjs +32 -0
  253. package/js/examples/syntax-reusable-config.mjs +16 -0
  254. package/js/examples/syntax-reusable-configs.mjs +21 -0
  255. package/js/examples/syntax-silent-operations.mjs +10 -0
  256. package/js/examples/syntax-stdin-option.mjs +12 -0
  257. package/js/examples/temp-sigint-test.mjs +21 -0
  258. package/js/examples/test-actual-buildshell.mjs +44 -0
  259. package/js/examples/test-async-streams-working.mjs +102 -0
  260. package/js/examples/test-async-streams.mjs +90 -0
  261. package/js/examples/test-auth-parse.mjs +74 -0
  262. package/js/examples/test-auto-quoting.mjs +57 -0
  263. package/js/examples/test-auto-start-fix.mjs +95 -0
  264. package/js/examples/test-baseline-sigint.mjs +38 -0
  265. package/js/examples/test-buffer-behavior.mjs +39 -0
  266. package/js/examples/test-buffers-simple.mjs +35 -0
  267. package/js/examples/test-bun-specific-issue.mjs +106 -0
  268. package/js/examples/test-bun-streaming.mjs +81 -0
  269. package/js/examples/test-cat-direct.mjs +41 -0
  270. package/js/examples/test-cat-pipe.mjs +34 -0
  271. package/js/examples/test-cd-behavior.mjs +42 -0
  272. package/js/examples/test-child-process-timing.mjs +53 -0
  273. package/js/examples/test-child-sigint-handler.mjs +62 -0
  274. package/js/examples/test-cleanup-simple.mjs +21 -0
  275. package/js/examples/test-comprehensive-tracing.mjs +58 -0
  276. package/js/examples/test-correct-space-handling.mjs +46 -0
  277. package/js/examples/test-ctrl-c-debug.mjs +44 -0
  278. package/js/examples/test-ctrl-c-inherit.mjs +30 -0
  279. package/js/examples/test-ctrl-c-sleep.mjs +31 -0
  280. package/js/examples/test-ctrl-c.mjs +17 -0
  281. package/js/examples/test-debug-new-options.mjs +55 -0
  282. package/js/examples/test-debug-pty.mjs +49 -0
  283. package/js/examples/test-debug-tee.mjs +38 -0
  284. package/js/examples/test-debug.mjs +25 -0
  285. package/js/examples/test-direct-jq.mjs +47 -0
  286. package/js/examples/test-direct-pipe-reading.mjs +119 -0
  287. package/js/examples/test-direct-pipe.sh +28 -0
  288. package/js/examples/test-double-quoting-prevention.mjs +138 -0
  289. package/js/examples/test-edge-cases-quoting.mjs +89 -0
  290. package/js/examples/test-events.mjs +37 -0
  291. package/js/examples/test-explicit-stdio.mjs +51 -0
  292. package/js/examples/test-final-streaming.mjs +71 -0
  293. package/js/examples/test-fix.mjs +71 -0
  294. package/js/examples/test-incremental-streaming.mjs +46 -0
  295. package/js/examples/test-individual-spawn.mjs +35 -0
  296. package/js/examples/test-inherit-stdout-not-stdin.mjs +133 -0
  297. package/js/examples/test-injection-protection.mjs +77 -0
  298. package/js/examples/test-interactive-streaming.mjs +140 -0
  299. package/js/examples/test-interactive-top.md +24 -0
  300. package/js/examples/test-interactive.mjs +17 -0
  301. package/js/examples/test-interpolation.mjs +14 -0
  302. package/js/examples/test-interrupt.mjs +40 -0
  303. package/js/examples/test-issue-135-comprehensive.mjs +41 -0
  304. package/js/examples/test-issue12-detailed.mjs +89 -0
  305. package/js/examples/test-issue12-exact.mjs +33 -0
  306. package/js/examples/test-jq-color.mjs +57 -0
  307. package/js/examples/test-jq-colors.mjs +41 -0
  308. package/js/examples/test-jq-compact.mjs +33 -0
  309. package/js/examples/test-jq-native.sh +10 -0
  310. package/js/examples/test-jq-pipeline-behavior.mjs +80 -0
  311. package/js/examples/test-jq-realtime.mjs +40 -0
  312. package/js/examples/test-manual-start.mjs +54 -0
  313. package/js/examples/test-mixed-quoting.mjs +88 -0
  314. package/js/examples/test-multi-stream.mjs +50 -0
  315. package/js/examples/test-multistage-debug.mjs +44 -0
  316. package/js/examples/test-native-spawn-vs-command-stream.mjs +154 -0
  317. package/js/examples/test-no-parse-pipeline.mjs +33 -0
  318. package/js/examples/test-non-virtual.mjs +52 -0
  319. package/js/examples/test-operators.mjs +53 -0
  320. package/js/examples/test-parent-continues.mjs +44 -0
  321. package/js/examples/test-path-interpolation.mjs +86 -0
  322. package/js/examples/test-ping-kill-and-stdin.mjs +98 -0
  323. package/js/examples/test-ping.mjs +12 -0
  324. package/js/examples/test-pty-spawn.mjs +101 -0
  325. package/js/examples/test-pty.mjs +38 -0
  326. package/js/examples/test-quote-behavior-summary.mjs +110 -0
  327. package/js/examples/test-quote-edge-cases.mjs +69 -0
  328. package/js/examples/test-quote-parsing.mjs +23 -0
  329. package/js/examples/test-raw-function.mjs +153 -0
  330. package/js/examples/test-raw-streaming.mjs +47 -0
  331. package/js/examples/test-readme-examples.mjs +142 -0
  332. package/js/examples/test-real-cat.mjs +28 -0
  333. package/js/examples/test-real-commands.mjs +21 -0
  334. package/js/examples/test-real-shell.mjs +31 -0
  335. package/js/examples/test-real-stdin-commands.mjs +160 -0
  336. package/js/examples/test-runner-batched.mjs +98 -0
  337. package/js/examples/test-runner-simple.mjs +80 -0
  338. package/js/examples/test-runner.mjs +67 -0
  339. package/js/examples/test-scope-parse.mjs +31 -0
  340. package/js/examples/test-sh-pipeline.mjs +24 -0
  341. package/js/examples/test-shell-detection.mjs +71 -0
  342. package/js/examples/test-shell-parser.mjs +37 -0
  343. package/js/examples/test-sigint-behavior.mjs +241 -0
  344. package/js/examples/test-sigint-handling.sh +14 -0
  345. package/js/examples/test-simple-pipe.mjs +12 -0
  346. package/js/examples/test-simple-streaming.mjs +32 -0
  347. package/js/examples/test-sleep-stdin.js +27 -0
  348. package/js/examples/test-sleep.mjs +56 -0
  349. package/js/examples/test-smart-quoting.mjs +180 -0
  350. package/js/examples/test-spaces-in-path.mjs +48 -0
  351. package/js/examples/test-special-chars-quoting.mjs +54 -0
  352. package/js/examples/test-stdin-after-start.mjs +39 -0
  353. package/js/examples/test-stdin-simple.mjs +67 -0
  354. package/js/examples/test-stdin-timing.mjs +74 -0
  355. package/js/examples/test-stdio-combinations.mjs +124 -0
  356. package/js/examples/test-stream-access.mjs +84 -0
  357. package/js/examples/test-stream-cleanup.mjs +27 -0
  358. package/js/examples/test-stream-readers.mjs +152 -0
  359. package/js/examples/test-streaming-final.mjs +57 -0
  360. package/js/examples/test-streaming-interfaces.mjs +141 -0
  361. package/js/examples/test-streaming-timing.mjs +27 -0
  362. package/js/examples/test-streaming.mjs +32 -0
  363. package/js/examples/test-streams-stdin-comprehensive.mjs +134 -0
  364. package/js/examples/test-streams-stdin-ctrl-c.mjs +96 -0
  365. package/js/examples/test-template-literal.mjs +26 -0
  366. package/js/examples/test-template-vs-interpolation.mjs +49 -0
  367. package/js/examples/test-timing.mjs +41 -0
  368. package/js/examples/test-top-inherit-stdout-stdin-control.mjs +123 -0
  369. package/js/examples/test-top-quit-stdin.mjs +118 -0
  370. package/js/examples/test-trace-option.mjs +21 -0
  371. package/js/examples/test-user-double-quotes.mjs +36 -0
  372. package/js/examples/test-user-single-quotes.mjs +36 -0
  373. package/js/examples/test-verbose.mjs +18 -0
  374. package/js/examples/test-verbose2.mjs +32 -0
  375. package/js/examples/test-virtual-streaming.mjs +125 -0
  376. package/js/examples/test-waiting-command.mjs +52 -0
  377. package/js/examples/test-waiting-commands.mjs +83 -0
  378. package/js/examples/test-watch-mode.mjs +104 -0
  379. package/js/examples/test-yes-cancellation.mjs +26 -0
  380. package/js/examples/test-yes-detailed.mjs +58 -0
  381. package/js/examples/test-yes-trace.mjs +28 -0
  382. package/js/examples/trace-abort-controller.mjs +30 -0
  383. package/js/examples/trace-error-handling.mjs +22 -0
  384. package/js/examples/trace-pipeline-command.mjs +22 -0
  385. package/js/examples/trace-signal-handling.mjs +35 -0
  386. package/js/examples/trace-simple-command.mjs +18 -0
  387. package/js/examples/trace-stderr-output.mjs +22 -0
  388. package/js/examples/verify-fix-both-runtimes.mjs +73 -0
  389. package/js/examples/verify-issue12-fixed.mjs +78 -0
  390. package/js/examples/which-command-common-commands.mjs +19 -0
  391. package/js/examples/which-command-gh-test.mjs +23 -0
  392. package/js/examples/which-command-nonexistent.mjs +20 -0
  393. package/js/examples/which-command-system-comparison.mjs +28 -0
  394. package/js/examples/working-example.mjs +13 -0
  395. package/js/examples/working-stdin-examples.mjs +138 -0
  396. package/js/examples/working-streaming-demo.mjs +49 -0
  397. package/{src → js/src}/$.mjs +20 -4
  398. package/{src → js/src}/$.utils.mjs +14 -2
  399. package/js/tests/$.features.test.mjs +283 -0
  400. package/js/tests/$.test.mjs +935 -0
  401. package/js/tests/builtin-commands.test.mjs +387 -0
  402. package/js/tests/bun-shell-path-fix.test.mjs +115 -0
  403. package/js/tests/bun.features.test.mjs +189 -0
  404. package/js/tests/cd-virtual-command.test.mjs +622 -0
  405. package/js/tests/cleanup-verification.test.mjs +127 -0
  406. package/js/tests/ctrl-c-baseline.test.mjs +207 -0
  407. package/js/tests/ctrl-c-basic.test.mjs +220 -0
  408. package/js/tests/ctrl-c-library.test.mjs +197 -0
  409. package/js/tests/ctrl-c-signal.test.mjs +915 -0
  410. package/js/tests/examples.test.mjs +252 -0
  411. package/js/tests/execa.features.test.mjs +198 -0
  412. package/js/tests/gh-commands.test.mjs +164 -0
  413. package/js/tests/gh-gist-operations.test.mjs +221 -0
  414. package/js/tests/git-gh-cd.test.mjs +466 -0
  415. package/js/tests/interactive-option.test.mjs +114 -0
  416. package/js/tests/interactive-streaming.test.mjs +307 -0
  417. package/js/tests/issue-135-final.test.mjs +58 -0
  418. package/js/tests/jq-color-behavior.test.mjs +140 -0
  419. package/js/tests/jq.test.mjs +318 -0
  420. package/js/tests/options-examples.test.mjs +106 -0
  421. package/js/tests/options-syntax.test.mjs +112 -0
  422. package/js/tests/path-interpolation.test.mjs +412 -0
  423. package/js/tests/pipe.test.mjs +291 -0
  424. package/js/tests/raw-function.test.mjs +266 -0
  425. package/js/tests/readme-examples.test.mjs +427 -0
  426. package/js/tests/resource-cleanup-internals.test.mjs +669 -0
  427. package/js/tests/shell-settings.test.mjs +279 -0
  428. package/js/tests/sigint-cleanup-isolated.test.mjs +151 -0
  429. package/js/tests/sigint-cleanup.test.mjs +118 -0
  430. package/js/tests/start-run-edge-cases.test.mjs +152 -0
  431. package/js/tests/start-run-options.test.mjs +181 -0
  432. package/js/tests/stderr-output-handling.test.mjs +279 -0
  433. package/js/tests/streaming-interfaces.test.mjs +194 -0
  434. package/js/tests/sync.test.mjs +297 -0
  435. package/js/tests/system-pipe.test.mjs +226 -0
  436. package/js/tests/test-cleanup.mjs +200 -0
  437. package/js/tests/test-helper-fixed.mjs +148 -0
  438. package/js/tests/test-helper-v2.mjs +118 -0
  439. package/js/tests/test-helper.mjs +171 -0
  440. package/js/tests/test-sigint-child.js +15 -0
  441. package/js/tests/text-method.test.mjs +225 -0
  442. package/js/tests/virtual.test.mjs +364 -0
  443. package/js/tests/yes-command-cleanup.test.mjs +208 -0
  444. package/js/tests/zx.features.test.mjs +233 -0
  445. package/package.json +13 -12
  446. package/rust/Cargo.lock +947 -0
  447. package/rust/Cargo.toml +47 -0
  448. package/rust/src/commands/basename.rs +69 -0
  449. package/rust/src/commands/cat.rs +123 -0
  450. package/rust/src/commands/cd.rs +67 -0
  451. package/rust/src/commands/cp.rs +187 -0
  452. package/rust/src/commands/dirname.rs +57 -0
  453. package/rust/src/commands/echo.rs +73 -0
  454. package/rust/src/commands/env.rs +33 -0
  455. package/rust/src/commands/exit.rs +36 -0
  456. package/rust/src/commands/false.rs +24 -0
  457. package/rust/src/commands/ls.rs +182 -0
  458. package/rust/src/commands/mkdir.rs +98 -0
  459. package/rust/src/commands/mod.rs +200 -0
  460. package/rust/src/commands/mv.rs +180 -0
  461. package/rust/src/commands/pwd.rs +28 -0
  462. package/rust/src/commands/rm.rs +150 -0
  463. package/rust/src/commands/seq.rs +179 -0
  464. package/rust/src/commands/sleep.rs +97 -0
  465. package/rust/src/commands/test.rs +204 -0
  466. package/rust/src/commands/touch.rs +99 -0
  467. package/rust/src/commands/true.rs +24 -0
  468. package/rust/src/commands/which.rs +87 -0
  469. package/rust/src/commands/yes.rs +99 -0
  470. package/rust/src/lib.rs +492 -0
  471. package/rust/src/main.rs +37 -0
  472. package/rust/src/shell_parser.rs +565 -0
  473. package/rust/src/utils.rs +335 -0
  474. package/rust/tests/builtin_commands.rs +549 -0
  475. package/rust/tests/process_runner.rs +286 -0
  476. package/rust/tests/shell_parser.rs +296 -0
  477. package/rust/tests/utils.rs +282 -0
  478. package/rust/tests/virtual_commands.rs +199 -0
  479. /package/{src → js/src}/commands/$.basename.mjs +0 -0
  480. /package/{src → js/src}/commands/$.cat.mjs +0 -0
  481. /package/{src → js/src}/commands/$.cd.mjs +0 -0
  482. /package/{src → js/src}/commands/$.cp.mjs +0 -0
  483. /package/{src → js/src}/commands/$.dirname.mjs +0 -0
  484. /package/{src → js/src}/commands/$.echo.mjs +0 -0
  485. /package/{src → js/src}/commands/$.env.mjs +0 -0
  486. /package/{src → js/src}/commands/$.exit.mjs +0 -0
  487. /package/{src → js/src}/commands/$.false.mjs +0 -0
  488. /package/{src → js/src}/commands/$.ls.mjs +0 -0
  489. /package/{src → js/src}/commands/$.mkdir.mjs +0 -0
  490. /package/{src → js/src}/commands/$.mv.mjs +0 -0
  491. /package/{src → js/src}/commands/$.pwd.mjs +0 -0
  492. /package/{src → js/src}/commands/$.rm.mjs +0 -0
  493. /package/{src → js/src}/commands/$.seq.mjs +0 -0
  494. /package/{src → js/src}/commands/$.sleep.mjs +0 -0
  495. /package/{src → js/src}/commands/$.test.mjs +0 -0
  496. /package/{src → js/src}/commands/$.touch.mjs +0 -0
  497. /package/{src → js/src}/commands/$.true.mjs +0 -0
  498. /package/{src → js/src}/commands/$.which.mjs +0 -0
  499. /package/{src → js/src}/commands/$.yes.mjs +0 -0
  500. /package/{src → js/src}/shell-parser.mjs +0 -0
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { test, expect, describe } from 'bun:test';
4
+ import { isWindows } from './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
5
+ import { $ } from '../src/$.mjs';
6
+
7
+ describe('Start/Run Options Passing', () => {
8
+ describe('.start() method with options', () => {
9
+ test('should pass capture: false option correctly', async () => {
10
+ const result = await $`echo "test with capture false"`.start({
11
+ capture: false,
12
+ });
13
+ expect(result.stdout).toBeUndefined();
14
+ expect(result.code).toBe(0);
15
+ });
16
+
17
+ test('should pass capture: true option correctly', async () => {
18
+ const result = await $`echo "test with capture true"`.start({
19
+ capture: true,
20
+ });
21
+ expect(result.stdout).toBe('test with capture true\n');
22
+ expect(result.code).toBe(0);
23
+ });
24
+
25
+ test('should pass mirror: false option correctly', async () => {
26
+ // mirror: false should still capture but not show output to console
27
+ const result = await $`echo "test with mirror false"`.start({
28
+ mirror: false,
29
+ });
30
+ expect(result.stdout).toBe('test with mirror false\n');
31
+ expect(result.code).toBe(0);
32
+ });
33
+
34
+ test('should pass both capture and mirror options', async () => {
35
+ const result = await $`echo "test both options"`.start({
36
+ capture: false,
37
+ mirror: false,
38
+ });
39
+ expect(result.stdout).toBeUndefined();
40
+ expect(result.code).toBe(0);
41
+ });
42
+
43
+ test('should pass stdin option correctly', async () => {
44
+ const result = await $`cat`.start({
45
+ stdin: 'custom input data',
46
+ capture: true,
47
+ });
48
+ expect(result.stdout).toBe('custom input data');
49
+ expect(result.code).toBe(0);
50
+ });
51
+
52
+ // Skip on Windows - uses 'ls /tmp' which is Unix-specific
53
+ test.skipIf(isWindows)('should work with real shell commands', async () => {
54
+ const result = await $`ls /tmp`.start({ capture: false });
55
+ expect(result.stdout).toBeUndefined();
56
+ expect(result.code).toBe(0);
57
+ });
58
+
59
+ test('should ignore options if process already started', async () => {
60
+ const runner = $`echo "already started test"`;
61
+
62
+ // Start the process
63
+ const firstResult = await runner.start();
64
+ expect(firstResult.stdout).toBe('already started test\n');
65
+
66
+ // Try to start again with different options - should be ignored
67
+ const secondResult = await runner.start({ capture: false });
68
+ expect(secondResult.stdout).toBe('already started test\n'); // Should still have stdout
69
+ });
70
+ });
71
+
72
+ describe('.run() method (alias for .start())', () => {
73
+ test('should work identically to .start() with capture: false', async () => {
74
+ const result = await $`echo "test with run alias"`.run({
75
+ capture: false,
76
+ });
77
+ expect(result.stdout).toBeUndefined();
78
+ expect(result.code).toBe(0);
79
+ });
80
+
81
+ test('should work identically to .start() with capture: true', async () => {
82
+ const result = await $`echo "test with run alias"`.run({ capture: true });
83
+ expect(result.stdout).toBe('test with run alias\n');
84
+ expect(result.code).toBe(0);
85
+ });
86
+
87
+ test('should work with multiple options', async () => {
88
+ const result = await $`echo "run with multiple options"`.run({
89
+ mirror: false,
90
+ capture: true,
91
+ });
92
+ expect(result.stdout).toBe('run with multiple options\n');
93
+ expect(result.code).toBe(0);
94
+ });
95
+
96
+ test('should work with stdin option', async () => {
97
+ const result = await $`cat`.run({
98
+ stdin: 'run method input',
99
+ capture: true,
100
+ });
101
+ expect(result.stdout).toBe('run method input');
102
+ expect(result.code).toBe(0);
103
+ });
104
+ });
105
+
106
+ describe('Backward compatibility', () => {
107
+ test('direct await should still work with default options', async () => {
108
+ const result = await $`echo "default behavior"`;
109
+ expect(result.stdout).toBe('default behavior\n');
110
+ expect(result.code).toBe(0);
111
+ });
112
+
113
+ test('.start() without options should work identically to direct await', async () => {
114
+ const directResult = await $`echo "no options test"`;
115
+ const startResult = await $`echo "no options test"`.start();
116
+
117
+ expect(directResult.stdout).toBe(startResult.stdout);
118
+ expect(directResult.code).toBe(startResult.code);
119
+ });
120
+
121
+ test('.run() without options should work identically to direct await', async () => {
122
+ const directResult = await $`echo "run no options"`;
123
+ const runResult = await $`echo "run no options"`.run();
124
+
125
+ expect(directResult.stdout).toBe(runResult.stdout);
126
+ expect(directResult.code).toBe(runResult.code);
127
+ });
128
+ });
129
+
130
+ describe('Virtual commands support', () => {
131
+ test('should work with virtual echo command', async () => {
132
+ const result = await $`echo "virtual command test"`.start({
133
+ capture: false,
134
+ });
135
+ expect(result.stdout).toBeUndefined();
136
+ expect(result.code).toBe(0);
137
+ });
138
+
139
+ test('should work with virtual commands using .run()', async () => {
140
+ const result = await $`echo "virtual run test"`.run({
141
+ capture: true,
142
+ mirror: false,
143
+ });
144
+ expect(result.stdout).toBe('virtual run test\n');
145
+ expect(result.code).toBe(0);
146
+ });
147
+ });
148
+
149
+ describe('Edge cases', () => {
150
+ test('should handle empty options object', async () => {
151
+ const result = await $`echo "empty options"`.start({});
152
+ expect(result.stdout).toBe('empty options\n');
153
+ expect(result.code).toBe(0);
154
+ });
155
+
156
+ test('should handle mode option alongside other options', async () => {
157
+ const result = await $`echo "with mode option"`.start({
158
+ mode: 'async',
159
+ capture: false,
160
+ });
161
+ expect(result.stdout).toBeUndefined();
162
+ expect(result.code).toBe(0);
163
+ });
164
+
165
+ test('should reinitialize chunks when capture option changes', async () => {
166
+ const runner = $`echo "chunk reinit test"`;
167
+
168
+ // Verify initial state
169
+ expect(runner.options.capture).toBe(true);
170
+ expect(runner.outChunks).toEqual([]);
171
+
172
+ // Change capture to false
173
+ const result = await runner.start({ capture: false });
174
+
175
+ // Verify chunks were reinitialized
176
+ expect(runner.options.capture).toBe(false);
177
+ expect(runner.outChunks).toBe(null);
178
+ expect(result.stdout).toBeUndefined();
179
+ });
180
+ });
181
+ });
@@ -0,0 +1,279 @@
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
+ import { beforeTestCleanup, afterTestCleanup } from './test-cleanup.mjs';
3
+ import { isWindows } from './test-helper.mjs';
4
+ import { $ } from '../src/$.mjs';
5
+ import { promises as fs } from 'fs';
6
+ import path from 'path';
7
+ import os from 'os';
8
+
9
+ describe('Stderr output handling in $.mjs', () => {
10
+ beforeEach(async () => {
11
+ await beforeTestCleanup();
12
+ });
13
+
14
+ afterEach(async () => {
15
+ await afterTestCleanup();
16
+ });
17
+
18
+ // Skip on Windows - uses sh -c with shell redirection
19
+ test.skipIf(isWindows)(
20
+ 'commands that output to stderr should not hang when captured',
21
+ async () => {
22
+ // Test with a command that writes to stderr
23
+ const result =
24
+ await $`sh -c 'echo "stdout message" && echo "stderr message" >&2'`.run(
25
+ {
26
+ capture: true,
27
+ mirror: false,
28
+ timeout: 5000, // Safety timeout
29
+ }
30
+ );
31
+
32
+ expect(result.code).toBe(0);
33
+ expect(result.stdout).toContain('stdout message');
34
+ expect(result.stderr).toContain('stderr message');
35
+ }
36
+ );
37
+
38
+ // Skip on Windows - uses 'which' command which is Unix-specific
39
+ test.skipIf(isWindows)(
40
+ 'gh commands with progress output to stderr should complete',
41
+ async () => {
42
+ // Check if gh is available
43
+ try {
44
+ await $`which gh`.run({ capture: true, mirror: false });
45
+ } catch {
46
+ console.log('Skipping: gh not available');
47
+ return;
48
+ }
49
+
50
+ // gh version outputs to stderr for progress
51
+ const result = await $`gh version`.run({
52
+ capture: true,
53
+ mirror: false,
54
+ timeout: 5000,
55
+ });
56
+
57
+ expect(result.code).toBe(0);
58
+ expect(result.stdout).toBeDefined();
59
+ // Version info should be in stdout
60
+ expect(result.stdout).toContain('gh version');
61
+ }
62
+ );
63
+
64
+ // Skip on Windows - uses sh -c and 2>&1 shell redirection
65
+ test.skipIf(isWindows)(
66
+ 'capturing with 2>&1 should combine stderr into stdout',
67
+ async () => {
68
+ const result =
69
+ await $`sh -c 'echo "stdout" && echo "stderr" >&2' 2>&1`.run({
70
+ capture: true,
71
+ mirror: false,
72
+ });
73
+
74
+ expect(result.code).toBe(0);
75
+ expect(result.stdout).toContain('stdout');
76
+ expect(result.stdout).toContain('stderr');
77
+ expect(result.stderr).toBe(''); // stderr should be empty since redirected
78
+ }
79
+ );
80
+
81
+ // Skip on Windows - uses sh scripts and chmod
82
+ test.skipIf(isWindows)(
83
+ 'long-running commands with stderr output should not hang',
84
+ async () => {
85
+ // Create a script that outputs to both stdout and stderr over time
86
+ const scriptPath = path.join(os.tmpdir(), 'test-script.sh');
87
+ const scriptContent = `#!/bin/sh
88
+ for i in 1 2 3; do
89
+ echo "stdout: iteration $i"
90
+ echo "stderr: iteration $i" >&2
91
+ sleep 0.1
92
+ done
93
+ `;
94
+ await fs.writeFile(scriptPath, scriptContent);
95
+ await $`chmod +x ${scriptPath}`.run({ capture: true, mirror: false });
96
+
97
+ try {
98
+ const startTime = Date.now();
99
+ const result = await $`${scriptPath}`.run({
100
+ capture: true,
101
+ mirror: false,
102
+ timeout: 5000,
103
+ });
104
+ const duration = Date.now() - startTime;
105
+
106
+ expect(result.code).toBe(0);
107
+ expect(result.stdout).toContain('stdout: iteration 3');
108
+ expect(result.stderr).toContain('stderr: iteration 3');
109
+ expect(duration).toBeLessThan(2000); // Should complete quickly, not hang
110
+ } finally {
111
+ await fs.unlink(scriptPath).catch(() => {});
112
+ }
113
+ }
114
+ );
115
+
116
+ // Skip on Windows - uses 2>&1 shell redirection which doesn't work the same way on Windows
117
+ test.skipIf(isWindows)(
118
+ 'gh gist create with stderr progress should work correctly',
119
+ async () => {
120
+ // Check authentication first
121
+ const authCheck = await $`gh auth status 2>&1`.run({
122
+ capture: true,
123
+ mirror: false,
124
+ });
125
+ if (authCheck.code !== 0) {
126
+ console.log(
127
+ 'Skipping gh gist test - not authenticated (this is OK - we are testing $.mjs, not gh auth)'
128
+ );
129
+ return;
130
+ }
131
+
132
+ // Check if we can actually create gists (not just authenticated)
133
+ const testAccess = await $`gh api user/gists --method HEAD 2>&1`.run({
134
+ capture: true,
135
+ mirror: false,
136
+ });
137
+ if (testAccess.code !== 0) {
138
+ // In CI with GitHub Actions token, we might get 404 or 403 errors
139
+ if (
140
+ testAccess.stdout.includes(
141
+ 'Resource not accessible by integration'
142
+ ) ||
143
+ testAccess.stdout.includes('HTTP 404') ||
144
+ testAccess.stdout.includes('HTTP 403')
145
+ ) {
146
+ console.log(
147
+ 'Skipping gh gist test - limited GitHub Actions token or API access (this is OK - we are testing $.mjs, not gh permissions)'
148
+ );
149
+ return;
150
+ }
151
+ }
152
+
153
+ // Create test file
154
+ const testFile = path.join(os.tmpdir(), 'stderr-test.txt');
155
+ await fs.writeFile(testFile, 'Testing stderr handling\n');
156
+
157
+ let gistId = null;
158
+
159
+ try {
160
+ // Without 2>&1 redirection - capture both streams separately
161
+ const result1 =
162
+ await $`gh gist create ${testFile} --desc "stderr-test-1" --public=false`.run(
163
+ {
164
+ capture: true,
165
+ mirror: false,
166
+ timeout: 10000,
167
+ }
168
+ );
169
+
170
+ expect(result1.code).toBe(0);
171
+ expect(result1.stdout).toBeDefined();
172
+
173
+ // The URL should be in stdout
174
+ const url1 = result1.stdout.trim();
175
+ expect(url1).toContain('gist.github.com');
176
+ gistId = url1.split('/').pop();
177
+
178
+ // Clean up first gist
179
+ await $`gh gist delete ${gistId} --yes`.run({
180
+ capture: true,
181
+ mirror: false,
182
+ });
183
+
184
+ // With 2>&1 redirection - all output in stdout
185
+ const result2 =
186
+ await $`gh gist create ${testFile} --desc "stderr-test-2" --public=false 2>&1`.run(
187
+ {
188
+ capture: true,
189
+ mirror: false,
190
+ timeout: 10000,
191
+ }
192
+ );
193
+
194
+ expect(result2.code).toBe(0);
195
+ expect(result2.stdout).toBeDefined();
196
+
197
+ // Should contain both progress messages and URL
198
+ expect(result2.stdout).toContain('Creating gist');
199
+ expect(result2.stdout).toContain('gist.github.com');
200
+
201
+ // Extract and clean up second gist
202
+ const lines = result2.stdout.trim().split('\n');
203
+ const url2 = lines.find((line) => line.includes('gist.github.com'));
204
+ if (url2) {
205
+ gistId = url2.split('/').pop();
206
+ await $`gh gist delete ${gistId} --yes`.run({
207
+ capture: true,
208
+ mirror: false,
209
+ });
210
+ }
211
+ } finally {
212
+ // Clean up
213
+ await fs.unlink(testFile).catch(() => {});
214
+ if (gistId) {
215
+ await $`gh gist delete ${gistId} --yes`
216
+ .run({ capture: true, mirror: false })
217
+ .catch(() => {});
218
+ }
219
+ }
220
+ }
221
+ );
222
+
223
+ // Skip on Windows - uses sh -c with shell redirection
224
+ test.skipIf(isWindows)(
225
+ 'streaming mode should handle stderr correctly',
226
+ async () => {
227
+ const cmd = $`sh -c 'echo "line1" && echo "err1" >&2 && sleep 0.1 && echo "line2" && echo "err2" >&2'`;
228
+
229
+ const collected = {
230
+ stdout: [],
231
+ stderr: [],
232
+ };
233
+
234
+ for await (const chunk of cmd.stream()) {
235
+ if (chunk.type === 'stdout') {
236
+ collected.stdout.push(chunk.data.toString());
237
+ } else if (chunk.type === 'stderr') {
238
+ collected.stderr.push(chunk.data.toString());
239
+ }
240
+ }
241
+
242
+ const result = await cmd;
243
+
244
+ expect(result.code).toBe(0);
245
+ expect(collected.stdout.join('')).toContain('line1');
246
+ expect(collected.stdout.join('')).toContain('line2');
247
+ expect(collected.stderr.join('')).toContain('err1');
248
+ expect(collected.stderr.join('')).toContain('err2');
249
+ }
250
+ );
251
+
252
+ // Skip on Windows - uses sh -c with shell redirection; also already skipped
253
+ test.skip('timeout should work even with pending stderr', async () => {
254
+ // Command that continuously outputs to stderr
255
+ const startTime = Date.now();
256
+
257
+ try {
258
+ await $`sh -c 'while true; do echo "stderr output" >&2; sleep 0.1; done'`.run(
259
+ {
260
+ capture: true,
261
+ mirror: false,
262
+ timeout: 1000, // 1 second timeout
263
+ }
264
+ );
265
+
266
+ // Should not reach here
267
+ expect(true).toBe(false);
268
+ } catch (error) {
269
+ const duration = Date.now() - startTime;
270
+
271
+ // Should have timed out
272
+ expect(duration).toBeGreaterThanOrEqual(900);
273
+ expect(duration).toBeLessThan(1500);
274
+
275
+ // Error should indicate timeout/killed
276
+ expect(error).toBeDefined();
277
+ }
278
+ });
279
+ });
@@ -0,0 +1,194 @@
1
+ import { test, expect } from 'bun:test';
2
+ import './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
3
+ import { $ } from '../src/$.mjs';
4
+
5
+ // Platform detection - Some tests use Unix utilities (cat, grep, sort, sh)
6
+ const isWindows = process.platform === 'win32';
7
+
8
+ // Skip on Windows - uses 'cat' command
9
+ test.skipIf(isWindows)(
10
+ 'streaming interfaces - basic functionality',
11
+ async () => {
12
+ // Test streams.stdin with cat
13
+ const catCmd = $`cat`;
14
+ const stdin = await catCmd.streams.stdin;
15
+
16
+ if (stdin) {
17
+ stdin.write('Hello from streams.stdin!\n');
18
+ stdin.write('Multiple lines work\n');
19
+ stdin.end();
20
+ }
21
+
22
+ const result = await catCmd;
23
+ expect(result.code).toBe(0);
24
+ expect(result.stdout).toContain('Hello from streams.stdin!');
25
+ expect(result.stdout).toContain('Multiple lines work');
26
+ }
27
+ );
28
+
29
+ test('streaming interfaces - auto-start behavior', async () => {
30
+ const cmd = $`echo "test"`;
31
+
32
+ // Accessing parent objects should not auto-start
33
+ const streams = cmd.streams;
34
+ const buffers = cmd.buffers;
35
+ const strings = cmd.strings;
36
+ expect(cmd.started).toBe(false);
37
+
38
+ // Accessing actual properties should auto-start
39
+ const stdout = cmd.streams.stdout;
40
+ expect(cmd.started).toBe(true);
41
+
42
+ await cmd;
43
+ });
44
+
45
+ // Skip on Windows - uses 'printf' command
46
+ test.skipIf(isWindows)('streaming interfaces - buffers interface', async () => {
47
+ const cmd = $`printf "Binary test"`;
48
+ const buffer = await cmd.buffers.stdout;
49
+
50
+ expect(Buffer.isBuffer(buffer)).toBe(true);
51
+ expect(buffer.toString()).toBe('Binary test');
52
+ });
53
+
54
+ test('streaming interfaces - strings interface', async () => {
55
+ const cmd = $`echo "String test"`;
56
+ const str = await cmd.strings.stdout;
57
+
58
+ expect(typeof str).toBe('string');
59
+ expect(str.trim()).toBe('String test');
60
+ });
61
+
62
+ // Skip on Windows - uses 'sh -c' command
63
+ test.skipIf(isWindows)(
64
+ 'streaming interfaces - mixed stdout/stderr',
65
+ async () => {
66
+ const cmd = $`sh -c 'echo "stdout" && echo "stderr" >&2'`;
67
+
68
+ const [stdout, stderr] = await Promise.all([
69
+ cmd.strings.stdout,
70
+ cmd.strings.stderr,
71
+ ]);
72
+
73
+ expect(stdout.trim()).toBe('stdout');
74
+ expect(stderr.trim()).toBe('stderr');
75
+ }
76
+ );
77
+
78
+ // Skip on Windows - uses Unix signal exit codes (130, 143)
79
+ test.skipIf(isWindows)(
80
+ 'streaming interfaces - kill method works',
81
+ async () => {
82
+ const cmd = $`sleep 10`;
83
+
84
+ // Start the process
85
+ await cmd.streams.stdout;
86
+ expect(cmd.started).toBe(true);
87
+
88
+ // Kill after short delay
89
+ setTimeout(() => cmd.kill(), 100);
90
+
91
+ const result = await cmd;
92
+ expect([130, 143, null]).toContain(result.code); // SIGTERM/SIGINT codes
93
+ },
94
+ 5000
95
+ );
96
+
97
+ // Skip on Windows - uses 'cat' command (not available on Windows)
98
+ test.skipIf(isWindows)(
99
+ 'streaming interfaces - stdin control with cross-platform command',
100
+ async () => {
101
+ // Use 'cat' which works identically on all platforms and waits for input
102
+ const catCmd = $`cat`;
103
+ const stdin = await catCmd.streams.stdin;
104
+
105
+ // Send some data and close stdin
106
+ setTimeout(() => {
107
+ if (stdin && !stdin.destroyed) {
108
+ stdin.write('Hello from stdin!\n');
109
+ stdin.write('Multiple lines work\n');
110
+ setTimeout(() => stdin.end(), 100);
111
+ }
112
+ }, 100);
113
+
114
+ // Backup kill (shouldn't be needed since we close stdin)
115
+ setTimeout(() => {
116
+ if (!catCmd.finished) {
117
+ catCmd.kill();
118
+ }
119
+ }, 2000);
120
+
121
+ const result = await catCmd;
122
+ expect(typeof result.code).toBe('number');
123
+ expect(result.code).toBe(0); // Should exit cleanly when stdin is closed
124
+ expect(result.stdout.length).toBeGreaterThan(0);
125
+ expect(result.stdout).toContain('Hello from stdin!');
126
+ expect(result.stdout).toContain('Multiple lines work');
127
+ },
128
+ 5000
129
+ );
130
+
131
+ test('streaming interfaces - immediate access after completion', async () => {
132
+ const cmd = $`echo "immediate test"`;
133
+ const result = await cmd;
134
+
135
+ // After completion, should return immediate results
136
+ const buffer = cmd.buffers.stdout;
137
+ const string = cmd.strings.stdout;
138
+
139
+ expect(Buffer.isBuffer(buffer)).toBe(true);
140
+ expect(typeof string).toBe('string');
141
+ expect(buffer.toString().trim()).toBe('immediate test');
142
+ expect(string.trim()).toBe('immediate test');
143
+ });
144
+
145
+ test('streaming interfaces - backward compatibility', async () => {
146
+ // Traditional await syntax should still work
147
+ const result = await $`echo "backward compatible"`;
148
+ expect(result.code).toBe(0);
149
+ expect(result.stdout.trim()).toBe('backward compatible');
150
+ });
151
+
152
+ // Skip on Windows - uses 'sort' command with different behavior
153
+ test.skipIf(isWindows)(
154
+ 'streaming interfaces - stdin pipe mode works',
155
+ async () => {
156
+ // Test that stdin: 'pipe' is properly handled vs string data
157
+ const sortCmd = $`sort`;
158
+ const stdin = await sortCmd.streams.stdin;
159
+
160
+ expect(stdin).not.toBe(null);
161
+ expect(typeof stdin.write).toBe('function');
162
+
163
+ stdin.write('zebra\n');
164
+ stdin.write('apple\n');
165
+ stdin.write('banana\n');
166
+ stdin.end();
167
+
168
+ const result = await sortCmd;
169
+ expect(result.code).toBe(0);
170
+ expect(result.stdout).toBe('apple\nbanana\nzebra\n');
171
+ }
172
+ );
173
+
174
+ // Skip on Windows - uses 'grep' command
175
+ test.skipIf(isWindows)(
176
+ 'streaming interfaces - grep filtering via stdin',
177
+ async () => {
178
+ const grepCmd = $`grep "important"`;
179
+ const stdin = await grepCmd.streams.stdin;
180
+
181
+ stdin.write('ignore this line\n');
182
+ stdin.write('important message 1\n');
183
+ stdin.write('skip this too\n');
184
+ stdin.write('another important note\n');
185
+ stdin.end();
186
+
187
+ const result = await grepCmd;
188
+ expect(result.code).toBe(0);
189
+ expect(result.stdout).toContain('important message 1');
190
+ expect(result.stdout).toContain('another important note');
191
+ expect(result.stdout).not.toContain('ignore this');
192
+ expect(result.stdout).not.toContain('skip this');
193
+ }
194
+ );