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,387 @@
1
+ import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
2
+ import { isWindows } from './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
3
+ import { $, register, unregister, enableVirtualCommands } from '../src/$.mjs';
4
+ import { trace } from '../src/$.utils.mjs';
5
+ import { rmSync, existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
6
+ import { join } from 'path';
7
+
8
+ // Test directory for safe file operations
9
+ const TEST_DIR = 'test-builtin-commands';
10
+
11
+ beforeEach(() => {
12
+ // Enable virtual commands for these tests
13
+ enableVirtualCommands();
14
+
15
+ // Create clean test directory
16
+ if (existsSync(TEST_DIR)) {
17
+ rmSync(TEST_DIR, { recursive: true, force: true });
18
+ }
19
+ mkdirSync(TEST_DIR);
20
+ });
21
+
22
+ afterEach(() => {
23
+ // Clean up test directory
24
+ if (existsSync(TEST_DIR)) {
25
+ rmSync(TEST_DIR, { recursive: true, force: true });
26
+ }
27
+ });
28
+
29
+ describe('Built-in Commands (Bun.$ compatible)', () => {
30
+ describe('File Reading Commands', () => {
31
+ test('cat should read file contents', async () => {
32
+ const testFile = join(TEST_DIR, 'test.txt');
33
+ writeFileSync(testFile, 'Hello World\nLine 2\n');
34
+
35
+ const result = await $`cat ${testFile}`;
36
+ expect(result.code).toBe(0);
37
+ expect(result.stdout).toBe('Hello World\nLine 2\n');
38
+ });
39
+
40
+ test('cat should read from stdin when no files provided', async () => {
41
+ const result = await $`echo "input" | cat`;
42
+ expect(result.code).toBe(0);
43
+ expect(result.stdout).toBe('input\n');
44
+ });
45
+
46
+ test('cat should handle non-existent files', async () => {
47
+ const result = await $`cat nonexistent.txt`;
48
+ expect(result.code).toBe(1);
49
+ expect(result.stderr).toContain('No such file or directory');
50
+ });
51
+ });
52
+
53
+ describe('Directory Listing Commands', () => {
54
+ test('ls should list directory contents', async () => {
55
+ writeFileSync(join(TEST_DIR, 'file1.txt'), 'content');
56
+ writeFileSync(join(TEST_DIR, 'file2.txt'), 'content');
57
+ mkdirSync(join(TEST_DIR, 'subdir'));
58
+
59
+ const result = await $`ls ${TEST_DIR}`;
60
+ expect(result.code).toBe(0);
61
+ expect(result.stdout).toContain('file1.txt');
62
+ expect(result.stdout).toContain('file2.txt');
63
+ expect(result.stdout).toContain('subdir');
64
+ });
65
+
66
+ test('ls should support -a flag for hidden files', async () => {
67
+ writeFileSync(join(TEST_DIR, '.hidden'), 'content');
68
+ writeFileSync(join(TEST_DIR, 'visible.txt'), 'content');
69
+
70
+ const result = await $`ls -a ${TEST_DIR}`;
71
+ expect(result.code).toBe(0);
72
+ expect(result.stdout).toContain('.hidden');
73
+ expect(result.stdout).toContain('visible.txt');
74
+ });
75
+
76
+ test('ls should support -l flag for long format', async () => {
77
+ writeFileSync(join(TEST_DIR, 'test.txt'), 'content');
78
+
79
+ const result = await $`ls -l ${TEST_DIR}`;
80
+ expect(result.code).toBe(0);
81
+ expect(result.stdout).toContain('-rw-r--r--');
82
+ expect(result.stdout).toContain('test.txt');
83
+ });
84
+ });
85
+
86
+ describe('File Creation Commands', () => {
87
+ test('mkdir should create directories', async () => {
88
+ const result = await $`mkdir ${join(TEST_DIR, 'newdir')}`;
89
+ expect(result.code).toBe(0);
90
+ expect(existsSync(join(TEST_DIR, 'newdir'))).toBe(true);
91
+ });
92
+
93
+ test('mkdir -p should create parent directories', async () => {
94
+ const result = await $`mkdir -p ${join(TEST_DIR, 'parent', 'child')}`;
95
+ expect(result.code).toBe(0);
96
+ expect(existsSync(join(TEST_DIR, 'parent', 'child'))).toBe(true);
97
+ });
98
+
99
+ test('touch should create new files', async () => {
100
+ const testFile = join(TEST_DIR, 'touched.txt');
101
+ const result = await $`touch ${testFile}`;
102
+ expect(result.code).toBe(0);
103
+ expect(existsSync(testFile)).toBe(true);
104
+ });
105
+
106
+ test('touch should update existing file timestamps', async () => {
107
+ const testFile = join(TEST_DIR, 'existing.txt');
108
+ writeFileSync(testFile, 'content');
109
+ const oldStat = require('fs').statSync(testFile);
110
+
111
+ // Wait a bit to ensure timestamp difference
112
+ await new Promise((resolve) => setTimeout(resolve, 10));
113
+
114
+ const result = await $`touch ${testFile}`;
115
+ expect(result.code).toBe(0);
116
+
117
+ const newStat = require('fs').statSync(testFile);
118
+ expect(newStat.mtime.getTime()).toBeGreaterThan(oldStat.mtime.getTime());
119
+ });
120
+ });
121
+
122
+ describe('File Removal Commands', () => {
123
+ test('rm should remove files', async () => {
124
+ const testFile = join(TEST_DIR, 'to-remove.txt');
125
+ writeFileSync(testFile, 'content');
126
+
127
+ const result = await $`rm ${testFile}`;
128
+ expect(result.code).toBe(0);
129
+ expect(existsSync(testFile)).toBe(false);
130
+ });
131
+
132
+ test('rm should fail on directories without -r', async () => {
133
+ const testDir = join(TEST_DIR, 'to-remove-dir');
134
+ mkdirSync(testDir);
135
+
136
+ const result = await $`rm ${testDir}`;
137
+ expect(result.code).toBe(1);
138
+ expect(result.stderr).toContain('Is a directory');
139
+ expect(existsSync(testDir)).toBe(true);
140
+ });
141
+
142
+ test('rm -r should remove directories recursively', async () => {
143
+ const testDir = join(TEST_DIR, 'to-remove-recursive');
144
+ mkdirSync(testDir);
145
+ writeFileSync(join(testDir, 'file.txt'), 'content');
146
+
147
+ const result = await $`rm -r ${testDir}`;
148
+ expect(result.code).toBe(0);
149
+ expect(existsSync(testDir)).toBe(false);
150
+ });
151
+
152
+ test('rm -f should suppress errors on non-existent files', async () => {
153
+ const result = await $`rm -f nonexistent.txt`;
154
+ expect(result.code).toBe(0);
155
+ });
156
+ });
157
+
158
+ describe('File Copy/Move Commands', () => {
159
+ test('cp should copy files', async () => {
160
+ const source = join(TEST_DIR, 'source.txt');
161
+ const dest = join(TEST_DIR, 'dest.txt');
162
+ writeFileSync(source, 'test content');
163
+
164
+ const result = await $`cp ${source} ${dest}`;
165
+ expect(result.code).toBe(0);
166
+ expect(existsSync(dest)).toBe(true);
167
+ expect(readFileSync(dest, 'utf8')).toBe('test content');
168
+ });
169
+
170
+ test('cp -r should copy directories recursively', async () => {
171
+ const sourceDir = join(TEST_DIR, 'source-dir');
172
+ const destDir = join(TEST_DIR, 'dest-dir');
173
+ mkdirSync(sourceDir);
174
+ writeFileSync(join(sourceDir, 'file.txt'), 'content');
175
+
176
+ const result = await $`cp -r ${sourceDir} ${destDir}`;
177
+ expect(result.code).toBe(0);
178
+ expect(existsSync(destDir)).toBe(true);
179
+ expect(existsSync(join(destDir, 'file.txt'))).toBe(true);
180
+ });
181
+
182
+ test('mv should move/rename files', async () => {
183
+ const source = join(TEST_DIR, 'source.txt');
184
+ const dest = join(TEST_DIR, 'dest.txt');
185
+ writeFileSync(source, 'test content');
186
+
187
+ const result = await $`mv ${source} ${dest}`;
188
+ expect(result.code).toBe(0);
189
+ expect(existsSync(source)).toBe(false);
190
+ expect(existsSync(dest)).toBe(true);
191
+ expect(readFileSync(dest, 'utf8')).toBe('test content');
192
+ });
193
+
194
+ test('mv should move files to directory', async () => {
195
+ const source = join(TEST_DIR, 'source.txt');
196
+ const destDir = join(TEST_DIR, 'dest-dir');
197
+ writeFileSync(source, 'test content');
198
+ mkdirSync(destDir);
199
+
200
+ const result = await $`mv ${source} ${destDir}`;
201
+ expect(result.code).toBe(0);
202
+ expect(existsSync(source)).toBe(false);
203
+ expect(existsSync(join(destDir, 'source.txt'))).toBe(true);
204
+ });
205
+ });
206
+
207
+ describe('Path Utility Commands', () => {
208
+ test('basename should extract filename', async () => {
209
+ const result = await $`basename /path/to/file.txt`;
210
+ expect(result.code).toBe(0);
211
+ expect(result.stdout.trim()).toBe('file.txt');
212
+ });
213
+
214
+ test('basename should remove suffix', async () => {
215
+ const result = await $`basename /path/to/file.txt .txt`;
216
+ expect(result.code).toBe(0);
217
+ expect(result.stdout.trim()).toBe('file');
218
+ });
219
+
220
+ test('dirname should extract directory path', async () => {
221
+ const result = await $`dirname /path/to/file.txt`;
222
+ expect(result.code).toBe(0);
223
+ expect(result.stdout.trim()).toBe('/path/to');
224
+ });
225
+ });
226
+
227
+ describe('Sequence Generation Commands', () => {
228
+ test('seq should generate number sequence', async () => {
229
+ const result = await $`seq 1 3`;
230
+ expect(result.code).toBe(0);
231
+ expect(result.stdout).toBe('1\n2\n3\n');
232
+ });
233
+
234
+ test('seq should handle single argument', async () => {
235
+ const result = await $`seq 3`;
236
+ expect(result.code).toBe(0);
237
+ expect(result.stdout).toBe('1\n2\n3\n');
238
+ });
239
+
240
+ test('seq should handle step argument', async () => {
241
+ const result = await $`seq 1 2 5`;
242
+ expect(result.code).toBe(0);
243
+ expect(result.stdout).toBe('1\n3\n5\n');
244
+ });
245
+ });
246
+
247
+ describe('Streaming Commands', () => {
248
+ test('yes should output repeatedly', async () => {
249
+ // Test with a limit to avoid infinite output
250
+ const chunks = [];
251
+ let count = 0;
252
+
253
+ for await (const chunk of $`yes hello`.stream()) {
254
+ chunks.push(chunk.data.toString());
255
+ count++;
256
+ if (count >= 3) {
257
+ break;
258
+ }
259
+ }
260
+
261
+ expect(chunks.length).toBe(3);
262
+ chunks.forEach((chunk) => {
263
+ expect(chunk).toBe('hello\n');
264
+ });
265
+ });
266
+
267
+ test('yes should default to "y"', async () => {
268
+ const chunks = [];
269
+ let count = 0;
270
+
271
+ for await (const chunk of $`yes`.stream()) {
272
+ chunks.push(chunk.data.toString());
273
+ count++;
274
+ if (count >= 2) {
275
+ break;
276
+ }
277
+ }
278
+
279
+ expect(chunks.length).toBe(2);
280
+ chunks.forEach((chunk) => {
281
+ expect(chunk).toBe('y\n');
282
+ });
283
+ });
284
+ });
285
+
286
+ describe('Command Location (which)', () => {
287
+ // Skip on Windows - uses Unix 'which sh' command
288
+ test.skipIf(isWindows)(
289
+ 'which should find existing system commands',
290
+ async () => {
291
+ // Test with a command that should definitely exist on all systems
292
+ const result = await $`which sh`;
293
+ expect(result.code).toBe(0);
294
+ expect(result.stdout).toMatch(/\/.*sh/); // Should contain path to sh
295
+ }
296
+ );
297
+
298
+ test('which should find node/bun executable', async () => {
299
+ // Test with node or bun depending on the environment
300
+ const command = typeof Bun !== 'undefined' ? 'bun' : 'node';
301
+ const result = await $`which ${command}`;
302
+ expect(result.code).toBe(0);
303
+ expect(result.stdout).toMatch(new RegExp(`.*${command}`));
304
+ });
305
+
306
+ test('which should find homebrew-installed commands (if available)', async () => {
307
+ // Test with gh (GitHub CLI) which is commonly installed via homebrew
308
+ const result = await $`which gh`;
309
+
310
+ // Enable verbose mode to see debug info if this test fails
311
+ if (result.code !== 0) {
312
+ trace('BuiltinTest', 'DEBUG: which gh failed');
313
+ trace('BuiltinTest', () => `Exit code: ${result.code}`);
314
+ trace('BuiltinTest', () => `Stdout: ${JSON.stringify(result.stdout)}`);
315
+ trace('BuiltinTest', () => `Stderr: ${JSON.stringify(result.stderr)}`);
316
+ trace('BuiltinTest', () => `PATH: ${process.env.PATH}`);
317
+
318
+ // Try to find gh manually to confirm it exists
319
+ const manualCheck = await $`/usr/bin/which gh`.catch(() => ({
320
+ code: 1,
321
+ stdout: '',
322
+ stderr: 'manual which failed',
323
+ }));
324
+ trace(
325
+ 'BuiltinTest',
326
+ () => `Manual /usr/bin/which result: ${manualCheck}`
327
+ );
328
+ }
329
+
330
+ // If gh is installed, it should return 0, otherwise skip this test
331
+ // Note: We can't guarantee gh is installed on all systems
332
+ if (result.code === 0) {
333
+ expect(result.stdout).toMatch(/.*gh/);
334
+ } else {
335
+ trace(
336
+ 'BuiltinTest',
337
+ 'Skipping gh test - command not found or which implementation bug'
338
+ );
339
+ }
340
+ });
341
+
342
+ test('which should return non-zero for non-existent commands', async () => {
343
+ const result = await $`which nonexistent-command-12345`;
344
+ expect(result.code).toBe(1);
345
+ expect(result.stderr).toContain('no nonexistent-command-12345 in PATH');
346
+ });
347
+
348
+ test('which should find built-in virtual commands', async () => {
349
+ const result = await $`which echo`;
350
+ expect(result.code).toBe(0);
351
+ expect(result.stdout).toContain('shell builtin');
352
+ });
353
+
354
+ test('which should handle missing arguments', async () => {
355
+ const result = await $`which`;
356
+ expect(result.code).toBe(1);
357
+ expect(result.stderr).toContain('missing operand');
358
+ });
359
+ });
360
+
361
+ describe('Error Handling', () => {
362
+ test('commands should return proper exit codes', async () => {
363
+ const success = await $`true`;
364
+ expect(success.code).toBe(0);
365
+
366
+ const failure = await $`false`;
367
+ expect(failure.code).toBe(1);
368
+ });
369
+
370
+ test('commands should provide helpful error messages', async () => {
371
+ const result = await $`cat nonexistent.txt`;
372
+ expect(result.code).toBe(1);
373
+ expect(result.stderr).toContain('cat:');
374
+ expect(result.stderr).toContain('nonexistent.txt');
375
+ });
376
+
377
+ test('commands should handle missing operands', async () => {
378
+ const mkdirResult = await $`mkdir`;
379
+ expect(mkdirResult.code).toBe(1);
380
+ expect(mkdirResult.stderr).toContain('missing operand');
381
+
382
+ const rmResult = await $`rm`;
383
+ expect(rmResult.code).toBe(1);
384
+ expect(rmResult.stderr).toContain('missing operand');
385
+ });
386
+ });
387
+ });
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Tests for Bun runtime shell path issue fix
3
+ *
4
+ * This addresses GitHub issue #42: Bun runtime shell path issues
5
+ * The issue was that string interpolation in template literals was incorrectly quoting
6
+ * complete command strings, causing shell commands to fail execution.
7
+ */
8
+
9
+ import { test, expect, describe } from 'bun:test';
10
+ import { $ } from '../src/$.mjs';
11
+
12
+ // Test both Bun and Node.js runtimes
13
+ const isBun = typeof globalThis.Bun !== 'undefined';
14
+ const runtime = isBun ? 'Bun' : 'Node.js';
15
+
16
+ // Platform detection - Some tests use Unix-specific paths and commands
17
+ const isWindows = process.platform === 'win32';
18
+
19
+ // Skip on Windows - tests reference /bin/sh and Unix paths
20
+ describe.skipIf(isWindows)(`String interpolation fix for ${runtime}`, () => {
21
+ test('Template literal without interpolation should work', async () => {
22
+ const result = await $`echo hello`;
23
+ expect(result.stdout.toString().trim()).toBe('hello');
24
+ });
25
+
26
+ test('String interpolation with complete command should work', async () => {
27
+ const cmd = 'echo hello';
28
+ const result = await $`${cmd}`;
29
+ expect(result.stdout.toString().trim()).toBe('hello');
30
+ });
31
+
32
+ test('String interpolation with command and args should work', async () => {
33
+ const cmd = 'echo "hello world"';
34
+ const result = await $`${cmd}`;
35
+ expect(result.stdout.toString().trim()).toBe('hello world');
36
+ });
37
+
38
+ test('Mixed template literal with interpolation should work', async () => {
39
+ const arg = 'hello';
40
+ const result = await $`echo ${arg}`;
41
+ expect(result.stdout.toString().trim()).toBe('hello');
42
+ });
43
+
44
+ test('Complex shell commands via interpolation should work', async () => {
45
+ const cmd = 'echo hello | wc -w';
46
+ const result = await $`${cmd}`;
47
+ expect(result.stdout.toString().trim()).toBe('1');
48
+ });
49
+
50
+ test('Shell operators in interpolated commands should work', async () => {
51
+ const cmd = 'test -f /bin/sh && echo "sh exists"';
52
+ const result = await $`${cmd}`;
53
+ expect(result.stdout.toString().trim()).toBe('sh exists');
54
+ });
55
+
56
+ test('Commands with special characters should work', async () => {
57
+ const cmd = 'echo "test $HOME" | head -1';
58
+ const result = await $`${cmd}`;
59
+ expect(result.stdout.toString()).toContain('test ');
60
+ });
61
+
62
+ test('Multiple argument interpolation should still quote properly', async () => {
63
+ const arg1 = 'hello';
64
+ const arg2 = 'world with spaces';
65
+ const result = await $`echo ${arg1} ${arg2}`;
66
+ expect(result.stdout.toString().trim()).toBe('hello world with spaces');
67
+ });
68
+
69
+ test('Empty command string should not cause issues', async () => {
70
+ const cmd = '';
71
+ // This should not fail catastrophically
72
+ try {
73
+ await $`${cmd}`;
74
+ } catch (error) {
75
+ // It's expected to fail, but not with our quoting issue
76
+ expect(error.message).not.toContain('not found');
77
+ }
78
+ });
79
+
80
+ test('Command with unsafe characters should be handled correctly', async () => {
81
+ // This tests that we don't break security when fixing the quoting issue
82
+ const unsafeCmd = 'echo "safe"; echo "also safe"';
83
+ const result = await $`${unsafeCmd}`;
84
+ const lines = result.stdout.toString().trim().split('\n');
85
+ expect(lines).toContain('safe');
86
+ expect(lines).toContain('also safe');
87
+ });
88
+ });
89
+
90
+ // Additional runtime-specific tests
91
+ // Skip on Windows - uses 'pwd' which outputs Unix-style paths with '/'
92
+ if (isBun) {
93
+ describe.skipIf(isWindows)('Bun-specific shell path tests', () => {
94
+ test('Bun.spawn compatibility is maintained', async () => {
95
+ const result = await $`pwd`;
96
+ expect(result.stdout.toString().trim()).toContain('/');
97
+ });
98
+
99
+ test('Bun runtime detection works correctly', async () => {
100
+ // This is more of a meta-test to ensure our runtime detection is correct
101
+ expect(typeof globalThis.Bun).toBe('object');
102
+ });
103
+ });
104
+ } else {
105
+ describe.skipIf(isWindows)('Node.js-specific shell path tests', () => {
106
+ test('Node.js child_process compatibility is maintained', async () => {
107
+ const result = await $`pwd`;
108
+ expect(result.stdout.toString().trim()).toContain('/');
109
+ });
110
+
111
+ test('Node.js runtime detection works correctly', async () => {
112
+ expect(typeof process.version).toBe('string');
113
+ });
114
+ });
115
+ }
@@ -0,0 +1,189 @@
1
+ import { test, expect, describe } from 'bun:test';
2
+ import './test-helper.mjs'; // Automatically sets up beforeEach/afterEach cleanup
3
+
4
+ describe('Bun.$ Feature Validation', () => {
5
+ describe('Runtime Support', () => {
6
+ test('should only work in Bun runtime', () => {
7
+ expect(typeof Bun).toBe('object');
8
+ expect(typeof Bun.$).toBe('function');
9
+ }, 30000);
10
+
11
+ test('should be Bun-specific (conceptual test)', () => {
12
+ // Bun.$ is Bun-specific and won't work in Node.js
13
+ // This test documents the Bun-only limitation
14
+ // Since we're running in Bun, we just validate it exists
15
+ expect(typeof Bun.$).toBe('function');
16
+ // In Node.js environment, Bun would be undefined
17
+ }, 30000);
18
+ });
19
+
20
+ describe('Template Literals', () => {
21
+ test('should support $`cmd` syntax', async () => {
22
+ const result = await Bun.$`echo "bun template literal test"`;
23
+ expect(result.exitCode).toBe(0);
24
+ expect(result.text().trim()).toBe('bun template literal test');
25
+ }, 30000);
26
+
27
+ test('should support variable interpolation', async () => {
28
+ const message = 'bun interpolation test';
29
+ const result = await Bun.$`echo ${message}`;
30
+ expect(result.text().trim()).toContain('bun interpolation test');
31
+ }, 30000);
32
+ });
33
+
34
+ describe('Real-time Streaming', () => {
35
+ test('should NOT provide live streaming (buffers only)', async () => {
36
+ // Bun.$ buffers output and returns it all at once
37
+ // This test documents the buffering behavior
38
+ const result = await Bun.$`echo "bun buffered test"`;
39
+
40
+ // Result is returned as complete buffer, not streamed
41
+ expect(typeof result.text).toBe('function');
42
+ expect(result.text().trim()).toBe('bun buffered test');
43
+
44
+ // No streaming interface available
45
+ expect(result.stream).toBeUndefined();
46
+ expect(typeof result[Symbol.asyncIterator]).toBe('undefined');
47
+ });
48
+ });
49
+
50
+ describe('Async Iteration', () => {
51
+ test('should NOT support for await iteration', async () => {
52
+ const result = await Bun.$`echo "no async iteration"`;
53
+
54
+ // Bun.$ result is not async iterable
55
+ expect(typeof result[Symbol.asyncIterator]).toBe('undefined');
56
+ expect(result.stream).toBeUndefined();
57
+ });
58
+ });
59
+
60
+ describe('EventEmitter Pattern', () => {
61
+ test('should NOT support .on() events', async () => {
62
+ const result = await Bun.$`echo "no events"`;
63
+
64
+ // Bun.$ result doesn't have EventEmitter interface
65
+ expect(typeof result.on).toBe('undefined');
66
+ expect(typeof result.emit).toBe('undefined');
67
+ expect(typeof result.addEventListener).toBe('undefined');
68
+ });
69
+ });
70
+
71
+ describe('Mixed Patterns', () => {
72
+ test('should NOT support events + await (only await)', async () => {
73
+ // Bun.$ only supports await pattern, no events
74
+ const result = await Bun.$`echo "await only"`;
75
+
76
+ expect(result.text().trim()).toBe('await only');
77
+ expect(typeof result.on).toBe('undefined');
78
+ });
79
+ });
80
+
81
+ describe('Shell Injection Protection', () => {
82
+ test('should have built-in injection protection', async () => {
83
+ const dangerous = "'; rm -rf /; echo 'hacked";
84
+ const result = await Bun.$`echo ${dangerous}`;
85
+
86
+ expect(result.text().trim()).toBe(dangerous);
87
+ expect(result.exitCode).toBe(0);
88
+ });
89
+ });
90
+
91
+ describe('Cross-platform Support', () => {
92
+ test('should work cross-platform', async () => {
93
+ const result = await Bun.$`echo "cross-platform"`;
94
+ expect(result.exitCode).toBe(0);
95
+ expect(result.text().trim()).toBe('cross-platform');
96
+ });
97
+ });
98
+
99
+ describe('Performance', () => {
100
+ test('should be very fast (built-in to Bun)', async () => {
101
+ const startTime = Date.now();
102
+ const result = await Bun.$`echo "performance test"`;
103
+ const endTime = Date.now();
104
+
105
+ expect(result.exitCode).toBe(0);
106
+ // Windows shell spawning is slower, allow more time
107
+ const maxTime = process.platform === 'win32' ? 500 : 100;
108
+ expect(endTime - startTime).toBeLessThan(maxTime); // Should be very fast
109
+ });
110
+ });
111
+
112
+ describe('Memory Efficiency', () => {
113
+ test('should buffer in memory (not streaming)', async () => {
114
+ // Bun.$ buffers output in memory
115
+ const result = await Bun.$`echo "memory buffering"`;
116
+
117
+ // All output is available immediately as buffer
118
+ expect(result.text().trim()).toBe('memory buffering');
119
+ expect(typeof result.text()).toBe('string');
120
+ expect(result.stdout).toBeDefined();
121
+ });
122
+ });
123
+
124
+ describe('Error Handling', () => {
125
+ test('should throw exception on error by default', async () => {
126
+ try {
127
+ await Bun.$`exit 42`;
128
+ expect(true).toBe(false); // Should not reach here
129
+ } catch (error) {
130
+ expect(error).toBeDefined();
131
+ expect(error.exitCode).toBe(42);
132
+ }
133
+ });
134
+
135
+ test('should support nothrow option', async () => {
136
+ const result = await Bun.$`exit 42`.nothrow();
137
+ expect(result.exitCode).toBe(42);
138
+ });
139
+ });
140
+
141
+ describe('Stdin Support', () => {
142
+ test('should support pipe operations', async () => {
143
+ // Bun.$ supports sophisticated pipe operations
144
+ const result = await Bun.$`echo "pipe test"`.text();
145
+ expect(result.trim()).toBe('pipe test');
146
+ });
147
+
148
+ test('should support input through echo/pipe (conceptual)', async () => {
149
+ // Bun.$ supports pipe operations differently
150
+ // const result = await Bun.$`echo ${input} | cat`;
151
+ const input = 'stdin input test';
152
+ const result = await Bun.$`echo ${input}`;
153
+ expect(result.text().trim()).toBe(input);
154
+ });
155
+ });
156
+
157
+ describe('Built-in Commands', () => {
158
+ test('should have built-in echo command', async () => {
159
+ // Bun.$ implements some commands natively for performance
160
+ const result = await Bun.$`echo "built-in echo"`;
161
+ expect(result.exitCode).toBe(0);
162
+ expect(result.text().trim()).toBe('built-in echo');
163
+ });
164
+
165
+ test('should have built-in cd and other commands', async () => {
166
+ // Bun.$ has built-in implementations of common commands
167
+ // This test validates that built-ins are available
168
+ const result = await Bun.$`pwd`;
169
+ expect(result.exitCode).toBe(0);
170
+ expect(result.text().trim().length).toBeGreaterThan(0);
171
+ });
172
+ });
173
+
174
+ describe('Bundle Size', () => {
175
+ test('should have 0KB bundle size (built into Bun)', () => {
176
+ // Bun.$ is built into the Bun runtime, no additional bundle size
177
+ expect(typeof Bun.$).toBe('function');
178
+ // This is a conceptual test - Bun.$ adds 0 bytes to bundle
179
+ });
180
+ });
181
+
182
+ describe('TypeScript Support', () => {
183
+ test('should have built-in TypeScript support', () => {
184
+ // Bun has native TypeScript support including for Bun.$
185
+ expect(typeof Bun.$).toBe('function');
186
+ // In a TypeScript file, Bun.$ would have full type definitions
187
+ });
188
+ });
189
+ });