command-stream 0.0.2 → 0.0.4

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 +47 -21
  2. package/README.md +30 -2
  3. package/package.json +1 -1
package/$.mjs CHANGED
@@ -5,6 +5,9 @@
5
5
  // 3. EventEmitter: $`command`.on('data', chunk => ...).on('end', result => ...)
6
6
  // 4. Stream access: $`command`.stdout, $`command`.stderr
7
7
 
8
+ import { createRequire } from 'module';
9
+ import { fileURLToPath } from 'url';
10
+
8
11
  const isBun = typeof globalThis.Bun !== 'undefined';
9
12
 
10
13
  // Global shell settings (like bash set -e / set +e)
@@ -16,6 +19,20 @@ let globalShellSettings = {
16
19
  nounset: false // set -u equivalent: error on undefined variables
17
20
  };
18
21
 
22
+ // Helper function to create result objects with Bun.$ compatibility
23
+ function createResult({ code, stdout = '', stderr = '', stdin = '' }) {
24
+ return {
25
+ code,
26
+ stdout,
27
+ stderr,
28
+ stdin,
29
+ // Bun.$ compatibility method
30
+ async text() {
31
+ return stdout;
32
+ }
33
+ };
34
+ }
35
+
19
36
  // Virtual command registry - unified system for all commands
20
37
  const virtualCommands = new Map();
21
38
 
@@ -219,13 +236,21 @@ class ProcessRunner extends StreamEmitter {
219
236
  const code = await exited;
220
237
  await Promise.all([outPump, errPump, stdinPumpPromise]);
221
238
 
222
- this.result = {
239
+ const resultData = {
223
240
  code,
224
241
  stdout: this.options.capture ? Buffer.concat(this.outChunks).toString('utf8') : undefined,
225
242
  stderr: this.options.capture ? Buffer.concat(this.errChunks).toString('utf8') : undefined,
226
243
  stdin: this.options.capture && this.inChunks ? Buffer.concat(this.inChunks).toString('utf8') : undefined,
227
244
  child: this.child
228
245
  };
246
+
247
+ this.result = {
248
+ ...resultData,
249
+ // Bun.$ compatibility method
250
+ async text() {
251
+ return resultData.stdout || '';
252
+ }
253
+ };
229
254
 
230
255
  this.finished = true;
231
256
  this.emit('end', this.result);
@@ -481,7 +506,7 @@ class ProcessRunner extends StreamEmitter {
481
506
 
482
507
  async _runPipeline(commands) {
483
508
  if (commands.length === 0) {
484
- return { code: 1, stdout: '', stderr: 'No commands in pipeline', stdin: '' };
509
+ return createResult({ code: 1, stdout: '', stderr: 'No commands in pipeline', stdin: '' });
485
510
  }
486
511
 
487
512
  let currentOutput = '';
@@ -565,14 +590,14 @@ class ProcessRunner extends StreamEmitter {
565
590
  this.emit('data', { type: 'stderr', data: buf });
566
591
  }
567
592
 
568
- // Store final result
569
- const finalResult = {
593
+ // Store final result using createResult helper for .text() method compatibility
594
+ const finalResult = createResult({
570
595
  code: result.code,
571
596
  stdout: currentOutput,
572
597
  stderr: result.stderr,
573
598
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
574
599
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
575
- };
600
+ });
576
601
 
577
602
  this.result = finalResult;
578
603
  this.finished = true;
@@ -605,13 +630,13 @@ class ProcessRunner extends StreamEmitter {
605
630
  }
606
631
  } catch (error) {
607
632
  // Handle errors from virtual commands in pipeline
608
- const result = {
633
+ const result = createResult({
609
634
  code: error.code ?? 1,
610
635
  stdout: currentOutput,
611
636
  stderr: error.stderr ?? error.message,
612
637
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
613
638
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
614
- };
639
+ });
615
640
 
616
641
  this.result = result;
617
642
  this.finished = true;
@@ -637,13 +662,13 @@ class ProcessRunner extends StreamEmitter {
637
662
  } else {
638
663
  // For system commands in pipeline, we would need to spawn processes
639
664
  // For now, return an error indicating this isn't supported
640
- const result = {
665
+ const result = createResult({
641
666
  code: 1,
642
667
  stdout: currentOutput,
643
668
  stderr: `Pipeline with system command '${cmd}' not yet supported`,
644
669
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
645
670
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
646
- };
671
+ });
647
672
 
648
673
  this.result = result;
649
674
  this.finished = true;
@@ -684,21 +709,21 @@ class ProcessRunner extends StreamEmitter {
684
709
  const destResult = await destination;
685
710
 
686
711
  // Return the final result with combined information
687
- return {
712
+ return createResult({
688
713
  code: destResult.code,
689
714
  stdout: destResult.stdout,
690
715
  stderr: sourceResult.stderr + destResult.stderr,
691
716
  stdin: sourceResult.stdin
692
- };
717
+ });
693
718
 
694
719
  } catch (error) {
695
- const result = {
720
+ const result = createResult({
696
721
  code: error.code ?? 1,
697
722
  stdout: '',
698
723
  stderr: error.message || 'Pipeline execution failed',
699
724
  stdin: this.options.stdin && typeof this.options.stdin === 'string' ? this.options.stdin :
700
725
  this.options.stdin && Buffer.isBuffer(this.options.stdin) ? this.options.stdin.toString('utf8') : ''
701
- };
726
+ });
702
727
 
703
728
  this.result = result;
704
729
  this.finished = true;
@@ -846,16 +871,17 @@ class ProcessRunner extends StreamEmitter {
846
871
  stderr: 'pipe'
847
872
  });
848
873
 
849
- result = {
874
+ result = createResult({
850
875
  code: proc.exitCode || 0,
851
876
  stdout: proc.stdout?.toString('utf8') || '',
852
877
  stderr: proc.stderr?.toString('utf8') || '',
853
878
  stdin: typeof stdin === 'string' ? stdin :
854
- Buffer.isBuffer(stdin) ? stdin.toString('utf8') : '',
855
- child: proc
856
- };
879
+ Buffer.isBuffer(stdin) ? stdin.toString('utf8') : ''
880
+ });
881
+ result.child = proc;
857
882
  } else {
858
883
  // Use Node's synchronous spawn
884
+ const require = createRequire(import.meta.url);
859
885
  const cp = require('child_process');
860
886
  const proc = cp.spawnSync(argv[0], argv.slice(1), {
861
887
  cwd,
@@ -866,14 +892,14 @@ class ProcessRunner extends StreamEmitter {
866
892
  stdio: ['pipe', 'pipe', 'pipe']
867
893
  });
868
894
 
869
- result = {
895
+ result = createResult({
870
896
  code: proc.status || 0,
871
897
  stdout: proc.stdout || '',
872
898
  stderr: proc.stderr || '',
873
899
  stdin: typeof stdin === 'string' ? stdin :
874
- Buffer.isBuffer(stdin) ? stdin.toString('utf8') : '',
875
- child: proc
876
- };
900
+ Buffer.isBuffer(stdin) ? stdin.toString('utf8') : ''
901
+ });
902
+ result.child = proc;
877
903
  }
878
904
 
879
905
  // Mirror output if requested (but always capture for result)
package/README.md CHANGED
@@ -14,7 +14,7 @@ A modern $ shell utility library with streaming, async iteration, and EventEmitt
14
14
  - 📡 **Real-time Streaming**: Process command output as it arrives, not after completion
15
15
  - 🔄 **Bun Optimized**: Designed for Bun runtime with Node.js compatibility
16
16
  - ⚡ **Performance**: Memory-efficient streaming prevents large buffer accumulation
17
- - 🎯 **Backward Compatible**: Existing `await $` syntax continues to work
17
+ - 🎯 **Backward Compatible**: Existing `await $` syntax continues to work + Bun.$ `.text()` method
18
18
  - 🛡️ **Type Safe**: Full TypeScript support (coming soon)
19
19
  - 🔧 **Built-in Commands**: 18 essential commands work identically across platforms
20
20
 
@@ -29,6 +29,7 @@ A modern $ shell utility library with streaming, async iteration, and EventEmitt
29
29
  | **Async Iteration** | ✅ `for await (chunk of $.stream())` | ❌ No | ❌ No | ❌ No |
30
30
  | **EventEmitter Pattern** | ✅ `.on('data', ...)` | ❌ No | 🟡 Limited events | ❌ No |
31
31
  | **Mixed Patterns** | ✅ Events + await/sync | ❌ No | ❌ No | ❌ No |
32
+ | **Bun.$ Compatibility** | ✅ `.text()` method support | ✅ Native API | ❌ No | ❌ No |
32
33
  | **Shell Injection Protection** | ✅ Auto-quoting | ✅ Built-in | ✅ Safe by default | ✅ Safe by default |
33
34
  | **Cross-platform** | ✅ macOS/Linux/Windows | ✅ Yes | ✅ Yes | ✅ Yes |
34
35
  | **Performance** | ⚡ Fast (Bun optimized) | ⚡ Very fast | 🐌 Moderate | 🐌 Slow |
@@ -695,10 +696,37 @@ All built-in commands support:
695
696
  stdout: string, // Complete stdout output
696
697
  stderr: string, // Complete stderr output
697
698
  stdin: string, // Input sent to process
698
- child: ChildProcess // Original child process object
699
+ child: ChildProcess, // Original child process object
700
+ async text() // Bun.$ compatibility method - returns stdout as string
699
701
  }
700
702
  ```
701
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
+
702
730
  ## Testing
703
731
 
704
732
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "command-stream",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
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",