command-stream 0.0.1 → 0.0.3

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 (3) hide show
  1. package/$.mjs +43 -21
  2. package/README.md +73 -43
  3. package/package.json +1 -1
package/$.mjs CHANGED
@@ -16,6 +16,20 @@ let globalShellSettings = {
16
16
  nounset: false // set -u equivalent: error on undefined variables
17
17
  };
18
18
 
19
+ // Helper function to create result objects with Bun.$ compatibility
20
+ function createResult({ code, stdout = '', stderr = '', stdin = '' }) {
21
+ return {
22
+ code,
23
+ stdout,
24
+ stderr,
25
+ stdin,
26
+ // Bun.$ compatibility method
27
+ async text() {
28
+ return stdout;
29
+ }
30
+ };
31
+ }
32
+
19
33
  // Virtual command registry - unified system for all commands
20
34
  const virtualCommands = new Map();
21
35
 
@@ -219,13 +233,21 @@ class ProcessRunner extends StreamEmitter {
219
233
  const code = await exited;
220
234
  await Promise.all([outPump, errPump, stdinPumpPromise]);
221
235
 
222
- this.result = {
236
+ const resultData = {
223
237
  code,
224
238
  stdout: this.options.capture ? Buffer.concat(this.outChunks).toString('utf8') : undefined,
225
239
  stderr: this.options.capture ? Buffer.concat(this.errChunks).toString('utf8') : undefined,
226
240
  stdin: this.options.capture && this.inChunks ? Buffer.concat(this.inChunks).toString('utf8') : undefined,
227
241
  child: this.child
228
242
  };
243
+
244
+ this.result = {
245
+ ...resultData,
246
+ // Bun.$ compatibility method
247
+ async text() {
248
+ return resultData.stdout || '';
249
+ }
250
+ };
229
251
 
230
252
  this.finished = true;
231
253
  this.emit('end', this.result);
@@ -481,7 +503,7 @@ class ProcessRunner extends StreamEmitter {
481
503
 
482
504
  async _runPipeline(commands) {
483
505
  if (commands.length === 0) {
484
- return { code: 1, stdout: '', stderr: 'No commands in pipeline', stdin: '' };
506
+ return createResult({ code: 1, stdout: '', stderr: 'No commands in pipeline', stdin: '' });
485
507
  }
486
508
 
487
509
  let currentOutput = '';
@@ -565,14 +587,14 @@ class ProcessRunner extends StreamEmitter {
565
587
  this.emit('data', { type: 'stderr', data: buf });
566
588
  }
567
589
 
568
- // Store final result
569
- const finalResult = {
590
+ // Store final result using createResult helper for .text() method compatibility
591
+ const finalResult = createResult({
570
592
  code: result.code,
571
593
  stdout: currentOutput,
572
594
  stderr: result.stderr,
573
595
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
574
596
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
575
- };
597
+ });
576
598
 
577
599
  this.result = finalResult;
578
600
  this.finished = true;
@@ -605,13 +627,13 @@ class ProcessRunner extends StreamEmitter {
605
627
  }
606
628
  } catch (error) {
607
629
  // Handle errors from virtual commands in pipeline
608
- const result = {
630
+ const result = createResult({
609
631
  code: error.code ?? 1,
610
632
  stdout: currentOutput,
611
633
  stderr: error.stderr ?? error.message,
612
634
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
613
635
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
614
- };
636
+ });
615
637
 
616
638
  this.result = result;
617
639
  this.finished = true;
@@ -637,13 +659,13 @@ class ProcessRunner extends StreamEmitter {
637
659
  } else {
638
660
  // For system commands in pipeline, we would need to spawn processes
639
661
  // For now, return an error indicating this isn't supported
640
- const result = {
662
+ const result = createResult({
641
663
  code: 1,
642
664
  stdout: currentOutput,
643
665
  stderr: `Pipeline with system command '${cmd}' not yet supported`,
644
666
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
645
667
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
646
- };
668
+ });
647
669
 
648
670
  this.result = result;
649
671
  this.finished = true;
@@ -684,21 +706,21 @@ class ProcessRunner extends StreamEmitter {
684
706
  const destResult = await destination;
685
707
 
686
708
  // Return the final result with combined information
687
- return {
709
+ return createResult({
688
710
  code: destResult.code,
689
711
  stdout: destResult.stdout,
690
712
  stderr: sourceResult.stderr + destResult.stderr,
691
713
  stdin: sourceResult.stdin
692
- };
714
+ });
693
715
 
694
716
  } catch (error) {
695
- const result = {
717
+ const result = createResult({
696
718
  code: error.code ?? 1,
697
719
  stdout: '',
698
720
  stderr: error.message || 'Pipeline execution failed',
699
721
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
700
722
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
701
- };
723
+ });
702
724
 
703
725
  this.result = result;
704
726
  this.finished = true;
@@ -846,14 +868,14 @@ class ProcessRunner extends StreamEmitter {
846
868
  stderr: 'pipe'
847
869
  });
848
870
 
849
- result = {
871
+ result = createResult({
850
872
  code: proc.exitCode || 0,
851
873
  stdout: proc.stdout?.toString('utf8') || '',
852
874
  stderr: proc.stderr?.toString('utf8') || '',
853
875
  stdin: typeof stdin === 'string' ? stdin :
854
- Buffer.isBuffer(stdin) ? stdin.toString('utf8') : '',
855
- child: proc
856
- };
876
+ Buffer.isBuffer(stdin) ? stdin.toString('utf8') : ''
877
+ });
878
+ result.child = proc;
857
879
  } else {
858
880
  // Use Node's synchronous spawn
859
881
  const cp = require('child_process');
@@ -866,14 +888,14 @@ class ProcessRunner extends StreamEmitter {
866
888
  stdio: ['pipe', 'pipe', 'pipe']
867
889
  });
868
890
 
869
- result = {
891
+ result = createResult({
870
892
  code: proc.status || 0,
871
893
  stdout: proc.stdout || '',
872
894
  stderr: proc.stderr || '',
873
895
  stdin: typeof stdin === 'string' ? stdin :
874
- Buffer.isBuffer(stdin) ? stdin.toString('utf8') : '',
875
- child: proc
876
- };
896
+ Buffer.isBuffer(stdin) ? stdin.toString('utf8') : ''
897
+ });
898
+ result.child = proc;
877
899
  }
878
900
 
879
901
  // Mirror output if requested (but always capture for result)
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # command-$tream
1
+ # [command-$tream](https://github.com/link-foundation/command-stream)
2
2
 
3
3
  $treamable commands executor
4
4
 
5
5
  A modern $ shell utility library with streaming, async iteration, and EventEmitter support, optimized for Bun runtime.
6
6
 
7
+ <img width="2624" height="1320" alt="carbon" src="https://github.com/user-attachments/assets/41cccd6a-f029-4206-b3bc-a85c5dbcf2cf" />
8
+
7
9
  ## Features
8
10
 
9
11
  - 🐚 **Shell-like by Default**: Commands behave exactly like running in terminal (stdout→stdout, stderr→stderr, stdin→stdin)
@@ -12,10 +14,51 @@ A modern $ shell utility library with streaming, async iteration, and EventEmitt
12
14
  - 📡 **Real-time Streaming**: Process command output as it arrives, not after completion
13
15
  - 🔄 **Bun Optimized**: Designed for Bun runtime with Node.js compatibility
14
16
  - ⚡ **Performance**: Memory-efficient streaming prevents large buffer accumulation
15
- - 🎯 **Backward Compatible**: Existing `await $` syntax continues to work
17
+ - 🎯 **Backward Compatible**: Existing `await $` syntax continues to work + Bun.$ `.text()` method
16
18
  - 🛡️ **Type Safe**: Full TypeScript support (coming soon)
17
19
  - 🔧 **Built-in Commands**: 18 essential commands work identically across platforms
18
20
 
21
+ ## Comparison with Other Libraries
22
+
23
+ | Feature | [command-stream](https://github.com/link-foundation/command-stream) | [Bun.$](https://bun.sh/docs/runtime/shell) | [execa](https://github.com/sindresorhus/execa) | [zx](https://github.com/google/zx) |
24
+ |---------|----------------|-------|-------|-----|
25
+ | **Runtime Support** | ✅ Bun + Node.js | 🟡 Bun only | ✅ Node.js | ✅ Node.js |
26
+ | **Template Literals** | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` |
27
+ | **Real-time Streaming** | ✅ Live output | ❌ Buffer only | 🟡 Limited | ❌ Buffer only |
28
+ | **Synchronous Execution** | ✅ `.sync()` with events | ❌ No | ✅ `execaSync` | ❌ No |
29
+ | **Async Iteration** | ✅ `for await (chunk of $.stream())` | ❌ No | ❌ No | ❌ No |
30
+ | **EventEmitter Pattern** | ✅ `.on('data', ...)` | ❌ No | 🟡 Limited events | ❌ No |
31
+ | **Mixed Patterns** | ✅ Events + await/sync | ❌ No | ❌ No | ❌ No |
32
+ | **Bun.$ Compatibility** | ✅ `.text()` method support | ✅ Native API | ❌ No | ❌ No |
33
+ | **Shell Injection Protection** | ✅ Auto-quoting | ✅ Built-in | ✅ Safe by default | ✅ Safe by default |
34
+ | **Cross-platform** | ✅ macOS/Linux/Windows | ✅ Yes | ✅ Yes | ✅ Yes |
35
+ | **Performance** | ⚡ Fast (Bun optimized) | ⚡ Very fast | 🐌 Moderate | 🐌 Slow |
36
+ | **Memory Efficiency** | ✅ Streaming prevents buildup | 🟡 Buffers in memory | 🟡 Buffers in memory | 🟡 Buffers in memory |
37
+ | **Error Handling** | ✅ Configurable (`set -e`/`set +e`, non-zero OK by default) | ✅ Throws on error | ✅ Throws on error | ✅ Throws on error |
38
+ | **Shell Settings** | ✅ `set -e`/`set +e` equivalent | ❌ No | ❌ No | ❌ No |
39
+ | **Stdout Support** | ✅ Real-time streaming + events | ✅ Shell redirection + buffered | ✅ Node.js streams + interleaved | ✅ Readable streams + `.pipe.stdout` |
40
+ | **Stderr Support** | ✅ Real-time streaming + events | ✅ Redirection + `.quiet()` access | ✅ Streams + interleaved output | ✅ Readable streams + `.pipe.stderr` |
41
+ | **Stdin Support** | ✅ string/Buffer/inherit/ignore | ✅ Pipe operations | ✅ Input/output streams | ✅ Basic stdin |
42
+ | **Built-in Commands** | ✅ **18 commands**: cat, ls, mkdir, rm, mv, cp, touch, basename, dirname, seq, yes + all Bun.$ commands | ✅ echo, cd, etc. | ❌ Uses system | ❌ Uses system |
43
+ | **Virtual Commands Engine** | ✅ **Revolutionary**: Register JavaScript functions as shell commands with full pipeline support | ❌ No extensibility | ❌ No custom commands | ❌ No custom commands |
44
+ | **Pipeline/Piping Support** | ✅ **Advanced**: System + Built-ins + Virtual + Mixed + `.pipe()` method | ✅ Standard shell piping | ✅ Programmatic `.pipe()` + multi-destination | ✅ Shell piping + `.pipe()` method |
45
+ | **Bundle Size** | 📦 ~15KB | 🎯 0KB (built-in) | 📦 ~25KB | 📦 ~50KB |
46
+ | **TypeScript** | 🔄 Coming soon | ✅ Built-in | ✅ Full support | ✅ Full support |
47
+ | **License** | ✅ **Unlicense (Public Domain)** | 🟡 MIT (+ LGPL dependencies) | 🟡 MIT | 🟡 Apache 2.0 |
48
+
49
+ ### Why Choose command-stream?
50
+
51
+ - **🆓 Truly Free**: **Unlicense (Public Domain)** - No restrictions, no attribution required, use however you want
52
+ - **🚀 Revolutionary Virtual Commands**: **World's first** fully customizable virtual commands engine - register JavaScript functions as shell commands!
53
+ - **🔗 Advanced Pipeline System**: **Only library** where virtual commands work seamlessly in pipelines with built-ins and system commands
54
+ - **🔧 Built-in Commands**: **18 essential commands** work identically across all platforms - no system dependencies!
55
+ - **📡 Real-time Processing**: Only library with true streaming and async iteration
56
+ - **🔄 Flexible Patterns**: Multiple usage patterns (await, events, iteration, mixed)
57
+ - **🐚 Shell Replacement**: Dynamic error handling with `set -e`/`set +e` equivalents for .sh file replacement
58
+ - **⚡ Bun Optimized**: Designed for Bun with Node.js fallback compatibility
59
+ - **💾 Memory Efficient**: Streaming prevents large buffer accumulation
60
+ - **🛡️ Production Ready**: 266+ tests with comprehensive coverage
61
+
19
62
  ## Built-in Commands (🚀 NEW!)
20
63
 
21
64
  command-stream now includes **18 built-in commands** that work identically to their bash/sh counterparts, providing true cross-platform shell scripting without system dependencies:
@@ -72,46 +115,6 @@ await $`seq 1 5 | cat > numbers.txt`;
72
115
  await $`basename /path/to/file.txt .txt`; // → "file"
73
116
  ```
74
117
 
75
- ## Comparison with Other Libraries
76
-
77
- | Feature | [command-stream](https://github.com/link-foundation/command-stream) | [Bun.$](https://bun.sh/docs/runtime/shell) | [execa](https://github.com/sindresorhus/execa) | [zx](https://github.com/google/zx) |
78
- |---------|----------------|-------|-------|-----|
79
- | **Runtime Support** | ✅ Bun + Node.js | 🟡 Bun only | ✅ Node.js | ✅ Node.js |
80
- | **Template Literals** | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` | ✅ `` $`cmd` `` |
81
- | **Real-time Streaming** | ✅ Live output | ❌ Buffer only | 🟡 Limited | ❌ Buffer only |
82
- | **Synchronous Execution** | ✅ `.sync()` with events | ❌ No | ✅ `execaSync` | ❌ No |
83
- | **Async Iteration** | ✅ `for await (chunk of $.stream())` | ❌ No | ❌ No | ❌ No |
84
- | **EventEmitter Pattern** | ✅ `.on('data', ...)` | ❌ No | 🟡 Limited events | ❌ No |
85
- | **Mixed Patterns** | ✅ Events + await/sync | ❌ No | ❌ No | ❌ No |
86
- | **Shell Injection Protection** | ✅ Auto-quoting | ✅ Built-in | ✅ Safe by default | ✅ Safe by default |
87
- | **Cross-platform** | ✅ macOS/Linux/Windows | ✅ Yes | ✅ Yes | ✅ Yes |
88
- | **Performance** | ⚡ Fast (Bun optimized) | ⚡ Very fast | 🐌 Moderate | 🐌 Slow |
89
- | **Memory Efficiency** | ✅ Streaming prevents buildup | 🟡 Buffers in memory | 🟡 Buffers in memory | 🟡 Buffers in memory |
90
- | **Error Handling** | ✅ Configurable (`set -e`/`set +e`, non-zero OK by default) | ✅ Throws on error | ✅ Throws on error | ✅ Throws on error |
91
- | **Shell Settings** | ✅ `set -e`/`set +e` equivalent | ❌ No | ❌ No | ❌ No |
92
- | **Stdout Support** | ✅ Real-time streaming + events | ✅ Shell redirection + buffered | ✅ Node.js streams + interleaved | ✅ Readable streams + `.pipe.stdout` |
93
- | **Stderr Support** | ✅ Real-time streaming + events | ✅ Redirection + `.quiet()` access | ✅ Streams + interleaved output | ✅ Readable streams + `.pipe.stderr` |
94
- | **Stdin Support** | ✅ string/Buffer/inherit/ignore | ✅ Pipe operations | ✅ Input/output streams | ✅ Basic stdin |
95
- | **Built-in Commands** | ✅ **18 commands**: cat, ls, mkdir, rm, mv, cp, touch, basename, dirname, seq, yes + all Bun.$ commands | ✅ echo, cd, etc. | ❌ Uses system | ❌ Uses system |
96
- | **Virtual Commands Engine** | ✅ **Revolutionary**: Register JavaScript functions as shell commands with full pipeline support | ❌ No extensibility | ❌ No custom commands | ❌ No custom commands |
97
- | **Pipeline/Piping Support** | ✅ **Advanced**: System + Built-ins + Virtual + Mixed + `.pipe()` method | ✅ Standard shell piping | ✅ Programmatic `.pipe()` + multi-destination | ✅ Shell piping + `.pipe()` method |
98
- | **Bundle Size** | 📦 ~15KB | 🎯 0KB (built-in) | 📦 ~25KB | 📦 ~50KB |
99
- | **TypeScript** | 🔄 Coming soon | ✅ Built-in | ✅ Full support | ✅ Full support |
100
- | **License** | ✅ **Unlicense (Public Domain)** | 🟡 MIT (+ LGPL dependencies) | 🟡 MIT | 🟡 Apache 2.0 |
101
-
102
- ### Why Choose command-stream?
103
-
104
- - **🆓 Truly Free**: **Unlicense (Public Domain)** - No restrictions, no attribution required, use however you want
105
- - **🚀 Revolutionary Virtual Commands**: **World's first** fully customizable virtual commands engine - register JavaScript functions as shell commands!
106
- - **🔗 Advanced Pipeline System**: **Only library** where virtual commands work seamlessly in pipelines with built-ins and system commands
107
- - **🔧 Built-in Commands**: **18 essential commands** work identically across all platforms - no system dependencies!
108
- - **📡 Real-time Processing**: Only library with true streaming and async iteration
109
- - **🔄 Flexible Patterns**: Multiple usage patterns (await, events, iteration, mixed)
110
- - **🐚 Shell Replacement**: Dynamic error handling with `set -e`/`set +e` equivalents for .sh file replacement
111
- - **⚡ Bun Optimized**: Designed for Bun with Node.js fallback compatibility
112
- - **💾 Memory Efficient**: Streaming prevents large buffer accumulation
113
- - **🛡️ Production Ready**: 266+ tests with comprehensive coverage
114
-
115
118
  ## Installation
116
119
 
117
120
  ```bash
@@ -693,10 +696,37 @@ All built-in commands support:
693
696
  stdout: string, // Complete stdout output
694
697
  stderr: string, // Complete stderr output
695
698
  stdin: string, // Input sent to process
696
- child: ChildProcess // Original child process object
699
+ child: ChildProcess, // Original child process object
700
+ async text() // Bun.$ compatibility method - returns stdout as string
697
701
  }
698
702
  ```
699
703
 
704
+ #### `.text()` Method (Bun.$ Compatibility)
705
+
706
+ For compatibility with Bun.$, all result objects include an async `.text()` method:
707
+
708
+ ```javascript
709
+ import { $ } from 'command-stream';
710
+
711
+ // Both sync and async execution support .text()
712
+ const result1 = await $`echo "hello world"`;
713
+ const text1 = await result1.text(); // "hello world\n"
714
+
715
+ const result2 = $`echo "sync example"`.sync();
716
+ const text2 = await result2.text(); // "sync example\n"
717
+
718
+ // .text() is equivalent to accessing .stdout
719
+ expect(await result.text()).toBe(result.stdout);
720
+
721
+ // Works with built-in commands
722
+ const result3 = await $`seq 1 3`;
723
+ const text3 = await result3.text(); // "1\n2\n3\n"
724
+
725
+ // Works with .pipe() method
726
+ const result4 = await $`echo "pipe test"`.pipe($`cat`);
727
+ const text4 = await result4.text(); // "pipe test\n"
728
+ ```
729
+
700
730
  ## Testing
701
731
 
702
732
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "command-stream",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Modern $ shell utility library with streaming, async iteration, and EventEmitter support, optimized for Bun runtime",
5
5
  "type": "module",
6
6
  "main": "$.mjs",