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,266 @@
1
+ import { test, expect, describe, beforeEach } from 'bun:test';
2
+ import './test-helper.mjs';
3
+ import { $, raw, shell, disableVirtualCommands } from '../src/$.mjs';
4
+
5
+ // Disable virtual commands for consistent system command behavior
6
+ beforeEach(() => {
7
+ shell.errexit(false);
8
+ disableVirtualCommands();
9
+ });
10
+
11
+ describe('raw() function - Disable auto-escape', () => {
12
+ describe('basic functionality', () => {
13
+ test('should create raw object with string', () => {
14
+ const result = raw('test command');
15
+ expect(result).toEqual({ raw: 'test command' });
16
+ });
17
+
18
+ test('should convert numbers to string', () => {
19
+ expect(raw(123)).toEqual({ raw: '123' });
20
+ });
21
+
22
+ test('should convert boolean to string', () => {
23
+ expect(raw(true)).toEqual({ raw: 'true' });
24
+ expect(raw(false)).toEqual({ raw: 'false' });
25
+ });
26
+
27
+ test('should handle empty string', () => {
28
+ expect(raw('')).toEqual({ raw: '' });
29
+ });
30
+ });
31
+
32
+ describe('command execution with raw()', () => {
33
+ test('should execute simple raw command', async () => {
34
+ const cmd = raw('echo "hello world"');
35
+ const result = await $`${cmd}`;
36
+ expect(result.stdout.trim()).toBe('hello world');
37
+ expect(result.code).toBe(0);
38
+ });
39
+
40
+ test('should execute command with && operator', async () => {
41
+ const cmd = raw('echo "step1" && echo "step2"');
42
+ const result = await $`${cmd}`;
43
+ expect(result.stdout).toContain('step1');
44
+ expect(result.stdout).toContain('step2');
45
+ expect(result.code).toBe(0);
46
+ });
47
+
48
+ test('should execute command with || operator', async () => {
49
+ const cmd = raw('false || echo "fallback"');
50
+ const result = await $`${cmd}`;
51
+ expect(result.stdout.trim()).toBe('fallback');
52
+ });
53
+
54
+ test('should execute command with pipe', async () => {
55
+ const cmd = raw('echo "hello world" | wc -w');
56
+ const result = await $`${cmd}`;
57
+ expect(result.stdout.trim()).toBe('2');
58
+ expect(result.code).toBe(0);
59
+ });
60
+
61
+ test('should execute command with semicolon', async () => {
62
+ const cmd = raw('echo "first"; echo "second"');
63
+ const result = await $`${cmd}`;
64
+ expect(result.stdout).toContain('first');
65
+ expect(result.stdout).toContain('second');
66
+ expect(result.code).toBe(0);
67
+ });
68
+
69
+ test('should handle command with subshell', async () => {
70
+ const cmd = raw('echo "outer: $(echo inner)"');
71
+ const result = await $`${cmd}`;
72
+ expect(result.stdout.trim()).toBe('outer: inner');
73
+ expect(result.code).toBe(0);
74
+ });
75
+
76
+ test('should handle command with wildcards', async () => {
77
+ const cmd = raw('echo test-*.mjs | head -c 20');
78
+ const result = await $`${cmd}`;
79
+ // Should execute wildcard expansion
80
+ expect(result.code).toBe(0);
81
+ expect(result.stdout.length).toBeGreaterThan(0);
82
+ });
83
+ });
84
+
85
+ describe('combining raw() with safe interpolation', () => {
86
+ test('should mix raw command with safe variable', async () => {
87
+ const safeInput = 'test';
88
+ const result = await $`${raw('echo "prefix:"')} ${safeInput}`;
89
+ expect(result.stdout.trim()).toBe('prefix: test');
90
+ });
91
+
92
+ test('should safely quote user input when mixed with raw', async () => {
93
+ const userInput = 'test; rm -rf /';
94
+ const result = await $`${raw('echo "User said:"')} ${userInput}`;
95
+ // User input should be safely quoted even when mixed with raw
96
+ expect(result.stdout).toContain('User said:');
97
+ expect(result.stdout).toContain('test; rm -rf /');
98
+ // Command should not execute the rm part
99
+ expect(result.code).toBe(0);
100
+ });
101
+
102
+ test('should handle multiple raw() calls in one command', async () => {
103
+ const cmd1 = raw('echo "part1"');
104
+ const cmd2 = raw('&& echo "part2"');
105
+ const result = await $`${cmd1} ${cmd2}`;
106
+ expect(result.stdout).toContain('part1');
107
+ expect(result.stdout).toContain('part2');
108
+ });
109
+ });
110
+
111
+ describe('comparison with normal interpolation', () => {
112
+ test('raw() executes shell operators, normal interpolation escapes them', async () => {
113
+ const cmdString = 'echo "test" && echo "test2"';
114
+
115
+ // With raw() - executes both commands
116
+ const rawResult = await $`${raw(cmdString)}`;
117
+ expect(rawResult.stdout).toContain('test');
118
+ expect(rawResult.stdout).toContain('test2');
119
+
120
+ // Without raw() - treats as literal string
121
+ const normalResult = await $`echo ${cmdString}`;
122
+ expect(normalResult.stdout.trim()).toBe(cmdString);
123
+ expect(normalResult.stdout).not.toMatch(/test\s+test2/); // Should be one line
124
+ });
125
+
126
+ test('raw() allows pipes, normal interpolation escapes them', async () => {
127
+ const cmdString = 'echo "hello world" | wc -w';
128
+
129
+ // With raw() - executes pipe
130
+ const rawResult = await $`${raw(cmdString)}`;
131
+ expect(rawResult.stdout.trim()).toBe('2');
132
+
133
+ // Without raw() - treats pipe as literal
134
+ const normalResult = await $`echo ${cmdString}`;
135
+ expect(normalResult.stdout).toContain('|');
136
+ expect(normalResult.stdout).toContain('wc');
137
+ });
138
+
139
+ test('raw() allows command substitution, normal interpolation escapes it', async () => {
140
+ const cmdString = '$(echo test)';
141
+
142
+ // With raw() - executes substitution
143
+ const rawResult = await $`echo ${raw(cmdString)}`;
144
+ expect(rawResult.stdout.trim()).toBe('test');
145
+
146
+ // Without raw() - treats as literal
147
+ const normalResult = await $`echo ${cmdString}`;
148
+ expect(normalResult.stdout.trim()).toBe('$(echo test)');
149
+ });
150
+ });
151
+
152
+ describe('edge cases', () => {
153
+ test('should handle raw() with quotes in command', async () => {
154
+ const cmd = raw('echo "single\'quote" "double\\"quote"');
155
+ const result = await $`${cmd}`;
156
+ expect(result.code).toBe(0);
157
+ expect(result.stdout).toContain('single');
158
+ expect(result.stdout).toContain('quote');
159
+ });
160
+
161
+ test('should handle raw() with environment variables', async () => {
162
+ const cmd = raw('echo $HOME');
163
+ const result = await $`${cmd}`;
164
+ expect(result.stdout.trim().length).toBeGreaterThan(0);
165
+ expect(result.stdout.trim()).not.toBe('$HOME');
166
+ });
167
+
168
+ test('should handle raw() with newlines', async () => {
169
+ const cmd = raw('echo "line1"\necho "line2"');
170
+ const result = await $`${cmd}`;
171
+ expect(result.stdout).toContain('line1');
172
+ expect(result.stdout).toContain('line2');
173
+ });
174
+
175
+ test('should handle raw() at different positions', async () => {
176
+ // At start
177
+ const result1 = await $`${raw('echo "start"')} end`;
178
+ expect(result1.stdout.trim()).toContain('start');
179
+
180
+ // In middle
181
+ const result2 = await $`echo start ${raw('&& echo "middle"')} end`;
182
+ expect(result2.stdout).toContain('middle');
183
+
184
+ // At end
185
+ const result3 = await $`echo start ${raw('&& echo "end"')}`;
186
+ expect(result3.stdout).toContain('end');
187
+ });
188
+ });
189
+
190
+ describe('practical use cases', () => {
191
+ test('configuration-based commands', async () => {
192
+ const config = {
193
+ buildCommand: raw('echo "Building..." && echo "Done"'),
194
+ testCommand: raw('echo "Testing..." && echo "Passed"'),
195
+ };
196
+
197
+ const buildResult = await $`${config.buildCommand}`;
198
+ expect(buildResult.stdout).toContain('Building');
199
+ expect(buildResult.stdout).toContain('Done');
200
+
201
+ const testResult = await $`${config.testCommand}`;
202
+ expect(testResult.stdout).toContain('Testing');
203
+ expect(testResult.stdout).toContain('Passed');
204
+ });
205
+
206
+ test('complex shell pipelines', async () => {
207
+ const pipeline = raw('seq 1 10 | head -n 3 | tail -n 1');
208
+ const result = await $`${pipeline}`;
209
+ expect(result.stdout.trim()).toBe('3');
210
+ });
211
+
212
+ test('conditional execution chains', async () => {
213
+ const successChain = raw('true && echo "success"');
214
+ const result1 = await $`${successChain}`;
215
+ expect(result1.stdout.trim()).toBe('success');
216
+
217
+ const failChain = raw('false && echo "should not print"');
218
+ const result2 = await $`${failChain}`;
219
+ expect(result2.stdout.trim()).toBe('');
220
+ });
221
+ });
222
+
223
+ describe('security demonstrations', () => {
224
+ test('normal interpolation prevents injection', async () => {
225
+ const malicious = '; rm -rf /tmp/test';
226
+ const result = await $`echo ${malicious}`;
227
+
228
+ // Should safely output the string, not execute rm
229
+ expect(result.stdout).toContain('; rm -rf /tmp/test');
230
+ expect(result.code).toBe(0);
231
+ });
232
+
233
+ test('raw() would execute injection (demonstration only)', async () => {
234
+ // We demonstrate the danger without actually running dangerous commands
235
+ const dangerous = raw(
236
+ 'echo "safe" ; echo "This would be dangerous with rm -rf"'
237
+ );
238
+ const result = await $`${dangerous}`;
239
+
240
+ // Both parts execute because raw() disables escaping
241
+ expect(result.stdout).toContain('safe');
242
+ expect(result.stdout).toContain('dangerous');
243
+ });
244
+ });
245
+
246
+ describe('error handling', () => {
247
+ test('should handle failing commands in raw()', async () => {
248
+ const cmd = raw('false');
249
+ const result = await $`${cmd}`;
250
+ expect(result.code).toBe(1);
251
+ });
252
+
253
+ test('should handle non-existent commands in raw()', async () => {
254
+ const cmd = raw('nonexistent-command-12345');
255
+ const result = await $`${cmd}`;
256
+ expect(result.code).not.toBe(0);
257
+ });
258
+
259
+ test('should handle syntax errors in raw()', async () => {
260
+ // Invalid shell syntax
261
+ const cmd = raw('echo "unclosed quote');
262
+ const result = await $`${cmd}`;
263
+ expect(result.code).not.toBe(0);
264
+ });
265
+ });
266
+ });
@@ -0,0 +1,427 @@
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
+ import { beforeTestCleanup, afterTestCleanup } from './test-cleanup.mjs';
3
+ import {
4
+ $,
5
+ sh,
6
+ create,
7
+ shell,
8
+ set,
9
+ unset,
10
+ disableVirtualCommands,
11
+ } from '../src/$.mjs';
12
+
13
+ // Helper function to setup shell settings for README tests
14
+ function setupShellForReadme() {
15
+ shell.errexit(false);
16
+ shell.verbose(false);
17
+ shell.xtrace(false);
18
+ shell.pipefail(false);
19
+ shell.nounset(false);
20
+ // Disable virtual commands for these tests to ensure system command behavior
21
+ disableVirtualCommands();
22
+ }
23
+
24
+ describe('README Examples and Use Cases', () => {
25
+ beforeEach(async () => {
26
+ await beforeTestCleanup();
27
+ setupShellForReadme();
28
+ });
29
+
30
+ afterEach(async () => {
31
+ await afterTestCleanup();
32
+ });
33
+
34
+ describe('1. Classic Await Pattern', () => {
35
+ test('should work like README example: await $`ls -la`', async () => {
36
+ const result = await $`echo "hello world"`;
37
+ expect(result.stdout.trim()).toBe('hello world');
38
+ expect(result.code).toBe(0);
39
+ expect(typeof result.stdout).toBe('string');
40
+ });
41
+ });
42
+
43
+ describe('2. Async Iteration Pattern', () => {
44
+ test('should work like README example: for await streaming', async () => {
45
+ const chunks = [];
46
+
47
+ for await (const chunk of $`echo "line1"; echo "line2"`.stream()) {
48
+ if (chunk.type === 'stdout') {
49
+ chunks.push(chunk.data.toString());
50
+ }
51
+ }
52
+
53
+ const output = chunks.join('');
54
+ expect(output).toContain('line1');
55
+ expect(output).toContain('line2');
56
+ });
57
+ });
58
+
59
+ describe('3. EventEmitter Pattern', () => {
60
+ test('should work like README example: .on() events', async () => {
61
+ const events = [];
62
+
63
+ const result = await new Promise((resolve) => {
64
+ const cmd = $`sh -c "echo 'stdout'; echo 'stderr' >&2"`;
65
+ cmd
66
+ .on('data', (chunk) => {
67
+ if (chunk.type === 'stdout') {
68
+ events.push('stdout-data');
69
+ }
70
+ })
71
+ .on('stderr', (chunk) => events.push('stderr'))
72
+ .on('end', (result) => {
73
+ events.push('end');
74
+ resolve(result);
75
+ })
76
+ .on('exit', (code) => events.push(`exit-${code}`));
77
+
78
+ // Start the command
79
+ cmd.then().catch(() => {});
80
+ });
81
+
82
+ expect(events).toContain('stdout-data');
83
+ expect(events).toContain('stderr');
84
+ expect(events).toContain('end');
85
+ expect(events).toContain('exit-0');
86
+ expect(result.code).toBe(0);
87
+ });
88
+ });
89
+
90
+ describe('4. Mixed Pattern', () => {
91
+ test('should work like README example: events + await', async () => {
92
+ const process = $`echo "streaming data"`;
93
+ const chunks = [];
94
+
95
+ // Handle real-time events
96
+ process.on('data', (chunk) => {
97
+ chunks.push(chunk);
98
+ });
99
+
100
+ // Still get the final result
101
+ const result = await process;
102
+
103
+ expect(result.stdout.trim()).toBe('streaming data');
104
+ expect(chunks.length).toBeGreaterThan(0);
105
+ expect(chunks[0]).toHaveProperty('type');
106
+ expect(chunks[0]).toHaveProperty('data');
107
+ });
108
+ });
109
+
110
+ describe('5. Shell Replacement (.sh → .mjs)', () => {
111
+ test('should work like README example: shell settings', async () => {
112
+ // Test the exact example from README
113
+ shell.errexit(true);
114
+
115
+ await $`mkdir -p /tmp/test-build`;
116
+
117
+ // This should work
118
+ const result1 = await $`echo "build success"`;
119
+ expect(result1.code).toBe(0);
120
+
121
+ // set +e equivalent: allow errors
122
+ shell.errexit(false);
123
+ const cleanup = await $`ls /nonexistent-dir-12345`; // Won't throw if fails
124
+ expect(cleanup.code).not.toBe(0); // Should fail but not throw
125
+
126
+ // set -e again for critical operations
127
+ shell.errexit(true);
128
+ await $`echo "critical operation"`;
129
+
130
+ // Other bash-like settings
131
+ shell.verbose(true);
132
+ shell.xtrace(true);
133
+
134
+ // Or use the bash-style API
135
+ set('e');
136
+ expect(shell.settings().errexit).toBe(true);
137
+
138
+ unset('e');
139
+ expect(shell.settings().errexit).toBe(false);
140
+
141
+ set('x');
142
+ expect(shell.settings().xtrace).toBe(true);
143
+
144
+ set('verbose');
145
+ expect(shell.settings().verbose).toBe(true);
146
+ });
147
+ });
148
+
149
+ describe('6. Default Behavior', () => {
150
+ test('should work like README example: stdout/stderr capture + mirroring', async () => {
151
+ // This command will:
152
+ // 1. Print "Hello" to your terminal (stdout→stdout)
153
+ // 2. Print "Error!" to your terminal (stderr→stderr)
154
+ // 3. Capture both outputs for programmatic access
155
+ const result = await $`sh -c "echo 'Hello'; echo 'Error!' >&2"`;
156
+
157
+ expect(result.stdout.trim()).toBe('Hello');
158
+ expect(result.stderr.trim()).toBe('Error!');
159
+ expect(result.code).toBe(0);
160
+ });
161
+ });
162
+
163
+ describe('7. Options Override', () => {
164
+ test('should work like README example: sh() with options', async () => {
165
+ // Disable terminal output but still capture
166
+ const result = await sh('echo "silent"', { mirror: false });
167
+ expect(result.stdout.trim()).toBe('silent');
168
+
169
+ // Custom stdin input
170
+ const custom = await sh('cat', { stdin: 'custom input' });
171
+ expect(custom.stdout.trim()).toBe('custom input');
172
+ });
173
+
174
+ test('should work like README example: create() with defaults', async () => {
175
+ // Create custom $ with different defaults
176
+ const quiet$ = create({ mirror: false });
177
+ const result = await quiet$`echo "silent"`;
178
+ expect(result.stdout.trim()).toBe('silent');
179
+
180
+ // Disable both mirroring and capturing for performance
181
+ const perfResult = await sh('echo "performance"', {
182
+ mirror: false,
183
+ capture: false,
184
+ });
185
+ expect(perfResult.stdout).toBeUndefined();
186
+ });
187
+ });
188
+
189
+ describe('8. Real-world: Session ID Extraction', () => {
190
+ test('should work like README example: JSON parsing from streaming', async () => {
191
+ let sessionId = null;
192
+ let logFile = null;
193
+ const logData = [];
194
+
195
+ // Simulate a command that outputs JSON with session_id
196
+ const cmd = $`sh -c 'echo "{\\"session_id\\":\\"test-123\\",\\"status\\":\\"started\\"}"; echo "{\\"data\\":\\"some log data\\"}"'`;
197
+
198
+ let chunkCount = 0;
199
+ for await (const chunk of cmd.stream()) {
200
+ chunkCount++;
201
+ // Handle both possible chunk formats
202
+ const isStdout = chunk.type === 'stdout';
203
+ const hasData = chunk.data !== undefined;
204
+
205
+ if (isStdout && hasData) {
206
+ const data = chunk.data.toString();
207
+
208
+ // Extract session ID from output
209
+ if (!sessionId && data.includes('session_id')) {
210
+ try {
211
+ // Split by }{ to handle concatenated JSON objects
212
+ const jsonStrings = data.replace(/\}\{/g, '}\n{').split('\n');
213
+
214
+ for (const jsonStr of jsonStrings) {
215
+ const trimmed = jsonStr.trim();
216
+ if (trimmed && trimmed.includes('session_id')) {
217
+ const parsed = JSON.parse(trimmed);
218
+ if (parsed.session_id) {
219
+ sessionId = parsed.session_id;
220
+ logFile = `${sessionId}.log`;
221
+ break;
222
+ }
223
+ }
224
+ }
225
+ } catch (e) {
226
+ // Handle JSON parse errors silently
227
+ }
228
+ }
229
+
230
+ // Write to log data (simulating file writes)
231
+ if (sessionId) {
232
+ logData.push(data);
233
+ }
234
+ }
235
+ }
236
+
237
+ // If no chunks were received, try to get the result directly
238
+ if (chunkCount === 0) {
239
+ const result = await cmd;
240
+ if (result.stdout && result.stdout.includes('session_id')) {
241
+ const lines = result.stdout.split('\n');
242
+ for (const line of lines) {
243
+ if (line.trim() && line.includes('session_id')) {
244
+ try {
245
+ const parsed = JSON.parse(line);
246
+ if (parsed.session_id) {
247
+ sessionId = parsed.session_id;
248
+ logFile = `${sessionId}.log`;
249
+ break;
250
+ }
251
+ } catch (e) {
252
+ // Handle JSON parse errors
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ expect(sessionId).toBe('test-123');
260
+ expect(logFile).toBe('test-123.log');
261
+ // Adjust expectation based on whether streaming worked
262
+ if (chunkCount > 0) {
263
+ expect(logData.length).toBeGreaterThan(0);
264
+ }
265
+ });
266
+ });
267
+
268
+ describe('9. Real-world: Progress Monitoring', () => {
269
+ test('should work like README example: progress parsing from stdout', async () => {
270
+ let progress = 0;
271
+ let completed = false;
272
+
273
+ const parseProgress = (output) => {
274
+ const match = output.match(/Progress: (\d+)%/);
275
+ return match ? parseInt(match[1]) : 0;
276
+ };
277
+
278
+ const updateProgressBar = (prog) => {
279
+ progress = prog;
280
+ };
281
+
282
+ // Simulate a download command with progress output
283
+ await new Promise((resolve) => {
284
+ const cmd = $`sh -c 'echo "Starting download"; echo "Progress: 25%"; echo "Progress: 50%"; echo "Progress: 100%"; echo "Done!"'`;
285
+ cmd
286
+ .on('stdout', (chunk) => {
287
+ const output = chunk.toString();
288
+ if (output.includes('Progress:')) {
289
+ const newProgress = parseProgress(output);
290
+ if (newProgress > 0) {
291
+ updateProgressBar(newProgress);
292
+ }
293
+ }
294
+ })
295
+ .on('end', (result) => {
296
+ completed = true;
297
+ resolve(result);
298
+ });
299
+
300
+ // Start the command
301
+ cmd.then().catch(() => {});
302
+ });
303
+
304
+ expect(progress).toBeGreaterThan(0);
305
+ expect(completed).toBe(true);
306
+ });
307
+ });
308
+
309
+ describe('10. API Documentation Examples', () => {
310
+ test('should match ProcessRunner events from API docs', async () => {
311
+ const events = [];
312
+
313
+ const result = await new Promise((resolve) => {
314
+ const cmd = $`sh -c "echo 'stdout'; echo 'stderr' >&2; exit 0"`;
315
+ cmd
316
+ .on('data', (chunk) => events.push(`data-${chunk.type}`))
317
+ .on('stdout', (chunk) => events.push('stdout'))
318
+ .on('stderr', (chunk) => events.push('stderr'))
319
+ .on('end', (result) => {
320
+ events.push('end');
321
+ resolve(result);
322
+ })
323
+ .on('exit', (code) => events.push(`exit-${code}`));
324
+
325
+ // Start the command
326
+ cmd.then().catch(() => {});
327
+ });
328
+
329
+ expect(events).toContain('data-stdout');
330
+ expect(events).toContain('data-stderr');
331
+ expect(events).toContain('stdout');
332
+ expect(events).toContain('stderr');
333
+ expect(events).toContain('end');
334
+ expect(events).toContain('exit-0');
335
+ });
336
+
337
+ test('should match Result Object from API docs', async () => {
338
+ const result = await $`sh -c "echo 'output'; echo 'error' >&2; exit 0"`;
339
+
340
+ // Verify result object structure from API docs
341
+ expect(typeof result.code).toBe('number');
342
+ expect(typeof result.stdout).toBe('string');
343
+ expect(typeof result.stderr).toBe('string');
344
+ expect(typeof result.stdin).toBe('string');
345
+ expect(result.child).toBeDefined();
346
+
347
+ expect(result.code).toBe(0);
348
+ expect(result.stdout.trim()).toBe('output');
349
+ expect(result.stderr.trim()).toBe('error');
350
+ });
351
+
352
+ test('should verify default options from API docs', async () => {
353
+ const cmd = $`echo test`;
354
+
355
+ // Verify default options structure from API docs
356
+ expect(cmd.options.mirror).toBe(true);
357
+ expect(cmd.options.capture).toBe(true);
358
+ expect(cmd.options.stdin).toBe('inherit');
359
+ });
360
+ });
361
+
362
+ describe('Smart Quoting & Security (from README)', () => {
363
+ test('safe strings are NOT quoted', () => {
364
+ const name = 'hello';
365
+ const cmd = '/usr/bin/node';
366
+
367
+ const testCmd1 = $({ mirror: false })`echo ${name}`;
368
+ expect(testCmd1.spec.command).toBe('echo hello');
369
+
370
+ const testCmd2 = $({ mirror: false })`${cmd} --version`;
371
+ expect(testCmd2.spec.command).toBe('/usr/bin/node --version');
372
+ });
373
+
374
+ test('dangerous strings are automatically quoted', () => {
375
+ const userInput = 'test; rm -rf /';
376
+ const pathWithSpaces = '/my path/file';
377
+
378
+ const testCmd1 = $({ mirror: false })`echo ${userInput}`;
379
+ expect(testCmd1.spec.command).toBe("echo 'test; rm -rf /'");
380
+
381
+ const testCmd2 = $({ mirror: false })`echo ${pathWithSpaces}`;
382
+ expect(testCmd2.spec.command).toBe("echo '/my path/file'");
383
+ });
384
+
385
+ test('user-provided quotes are preserved', () => {
386
+ const quotedPath = "'/path with spaces/file'";
387
+ const doubleQuoted = '"/path with spaces/file"';
388
+
389
+ const testCmd1 = $({ mirror: false })`cat ${quotedPath}`;
390
+ expect(testCmd1.spec.command).toBe("cat '/path with spaces/file'");
391
+
392
+ const testCmd2 = $({ mirror: false })`cat ${doubleQuoted}`;
393
+ expect(testCmd2.spec.command).toBe('cat \'"/path with spaces/file"\'');
394
+ });
395
+
396
+ test('shell injection attempts are neutralized', () => {
397
+ const dangerous = "'; rm -rf /; echo '";
398
+ const cmdSubstitution = '$(whoami)';
399
+ const varExpansion = '$HOME';
400
+ const complex = '`cat /etc/passwd`';
401
+
402
+ const testCmd1 = $({ mirror: false })`echo ${dangerous}`;
403
+ expect(testCmd1.spec.command).toContain('rm -rf');
404
+ expect(testCmd1.spec.command).toMatch(/^echo '/);
405
+
406
+ const testCmd2 = $({ mirror: false })`echo ${cmdSubstitution}`;
407
+ expect(testCmd2.spec.command).toBe("echo '$(whoami)'");
408
+
409
+ const testCmd3 = $({ mirror: false })`echo ${varExpansion}`;
410
+ expect(testCmd3.spec.command).toBe("echo '$HOME'");
411
+
412
+ const testCmd4 = $({ mirror: false })`echo ${complex}`;
413
+ expect(testCmd4.spec.command).toBe("echo '`cat /etc/passwd`'");
414
+ });
415
+
416
+ test('actual execution prevents injection', async () => {
417
+ const varExpansion = '$HOME';
418
+ const cmdSubstitution = '$(echo INJECTED)';
419
+
420
+ const result1 = await $`echo ${varExpansion}`;
421
+ expect(result1.stdout.trim()).toBe('$HOME'); // Literal, not expanded
422
+
423
+ const result2 = await $`echo ${cmdSubstitution}`;
424
+ expect(result2.stdout.trim()).toBe('$(echo INJECTED)'); // Literal, not executed
425
+ });
426
+ });
427
+ });