@onivoro/server-process 22.0.2 → 24.0.1

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 (69) hide show
  1. package/README.md +389 -0
  2. package/jest.config.ts +11 -0
  3. package/package.json +8 -40
  4. package/project.json +23 -0
  5. package/src/lib/docker.ts +14 -0
  6. package/src/lib/exec-promise.spec.ts +17 -0
  7. package/src/lib/exec-promise.ts +14 -0
  8. package/src/lib/exec-rx-as-json.ts +9 -0
  9. package/src/lib/exec-rx-as-lines.ts +10 -0
  10. package/src/lib/exec-rx.spec.ts +22 -0
  11. package/src/lib/exec-rx.ts +16 -0
  12. package/src/lib/exit.ts +1 -0
  13. package/src/lib/listen.ts +12 -0
  14. package/src/lib/psql.ts +16 -0
  15. package/src/lib/spawn-promise.spec.ts +17 -0
  16. package/src/lib/spawn-promise.ts +31 -0
  17. package/tsconfig.json +16 -0
  18. package/tsconfig.lib.json +8 -0
  19. package/tsconfig.spec.json +21 -0
  20. package/dist/cjs/index.js +0 -21
  21. package/dist/cjs/lib/docker.d.ts +0 -8
  22. package/dist/cjs/lib/docker.js +0 -16
  23. package/dist/cjs/lib/exec-promise.d.ts +0 -3
  24. package/dist/cjs/lib/exec-promise.js +0 -16
  25. package/dist/cjs/lib/exec-rx-as-json.d.ts +0 -2
  26. package/dist/cjs/lib/exec-rx-as-json.js +0 -9
  27. package/dist/cjs/lib/exec-rx-as-lines.d.ts +0 -2
  28. package/dist/cjs/lib/exec-rx-as-lines.js +0 -10
  29. package/dist/cjs/lib/exec-rx.d.ts +0 -4
  30. package/dist/cjs/lib/exec-rx.js +0 -18
  31. package/dist/cjs/lib/exit.d.ts +0 -1
  32. package/dist/cjs/lib/exit.js +0 -5
  33. package/dist/cjs/lib/listen.d.ts +0 -5
  34. package/dist/cjs/lib/listen.js +0 -12
  35. package/dist/cjs/lib/psql.d.ts +0 -5
  36. package/dist/cjs/lib/psql.js +0 -20
  37. package/dist/cjs/lib/spawn-promise.d.ts +0 -1
  38. package/dist/cjs/lib/spawn-promise.js +0 -28
  39. package/dist/esm/index.d.ts +0 -9
  40. package/dist/esm/index.js +0 -21
  41. package/dist/esm/lib/docker.d.ts +0 -8
  42. package/dist/esm/lib/docker.js +0 -16
  43. package/dist/esm/lib/exec-promise.d.ts +0 -3
  44. package/dist/esm/lib/exec-promise.js +0 -16
  45. package/dist/esm/lib/exec-rx-as-json.d.ts +0 -2
  46. package/dist/esm/lib/exec-rx-as-json.js +0 -9
  47. package/dist/esm/lib/exec-rx-as-lines.d.ts +0 -2
  48. package/dist/esm/lib/exec-rx-as-lines.js +0 -10
  49. package/dist/esm/lib/exec-rx.d.ts +0 -4
  50. package/dist/esm/lib/exec-rx.js +0 -18
  51. package/dist/esm/lib/exit.d.ts +0 -1
  52. package/dist/esm/lib/exit.js +0 -5
  53. package/dist/esm/lib/listen.d.ts +0 -5
  54. package/dist/esm/lib/listen.js +0 -12
  55. package/dist/esm/lib/psql.d.ts +0 -5
  56. package/dist/esm/lib/psql.js +0 -20
  57. package/dist/esm/lib/spawn-promise.d.ts +0 -1
  58. package/dist/esm/lib/spawn-promise.js +0 -28
  59. package/dist/types/index.d.ts +0 -9
  60. package/dist/types/lib/docker.d.ts +0 -8
  61. package/dist/types/lib/exec-promise.d.ts +0 -3
  62. package/dist/types/lib/exec-rx-as-json.d.ts +0 -2
  63. package/dist/types/lib/exec-rx-as-lines.d.ts +0 -2
  64. package/dist/types/lib/exec-rx.d.ts +0 -4
  65. package/dist/types/lib/exit.d.ts +0 -1
  66. package/dist/types/lib/listen.d.ts +0 -5
  67. package/dist/types/lib/psql.d.ts +0 -5
  68. package/dist/types/lib/spawn-promise.d.ts +0 -1
  69. /package/{dist/cjs/index.d.ts → src/index.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,389 @@
1
+ # @onivoro/server-process
2
+
3
+ A lightweight Node.js process management library providing utilities for command execution, Docker container operations, PostgreSQL commands, and reactive process handling.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @onivoro/server-process
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Promise-based Command Execution**: Simple async/await command execution
14
+ - **Reactive Process Streams**: RxJS-based reactive command execution
15
+ - **Docker Container Support**: Execute commands within Docker containers
16
+ - **PostgreSQL Integration**: Execute psql commands with container support
17
+ - **Process I/O Management**: Handle stdin/stdout streams reactively
18
+ - **JSON Output Processing**: Parse command output as JSON
19
+ - **Line-by-Line Processing**: Stream processing for large outputs
20
+
21
+ ## API Reference
22
+
23
+ ### Command Execution
24
+
25
+ #### `execPromise(cmd, options?)`
26
+
27
+ Execute a command and return a promise that resolves to stdout as a string:
28
+
29
+ ```typescript
30
+ import { execPromise } from '@onivoro/server-process';
31
+
32
+ // Simple command execution
33
+ const output = await execPromise('ls -la');
34
+ console.log(output); // stdout as string
35
+
36
+ // With options
37
+ const result = await execPromise('git status', { cwd: '/path/to/repo' });
38
+ ```
39
+
40
+ **Parameters:**
41
+ - `cmd: string` - Command to execute
42
+ - `options?: EncodingOption & ExecOptions` - Node.js exec options
43
+
44
+ **Returns:** `Promise<string>` - Command stdout
45
+
46
+ #### `spawnPromise(program, args?, options?)`
47
+
48
+ Spawn a process and return a promise that resolves to joined stdout:
49
+
50
+ ```typescript
51
+ import { spawnPromise } from '@onivoro/server-process';
52
+
53
+ // Spawn with arguments
54
+ const output = await spawnPromise('node', ['--version']);
55
+ console.log(output); // Node.js version
56
+
57
+ // With spawn options
58
+ const result = await spawnPromise('npm', ['install'], {
59
+ cwd: '/path/to/project',
60
+ env: { ...process.env, NODE_ENV: 'production' }
61
+ });
62
+ ```
63
+
64
+ **Parameters:**
65
+ - `program: string` - Program to spawn
66
+ - `args?: string[]` - Command arguments
67
+ - `options?: any` - Node.js spawn options
68
+
69
+ **Returns:** `Promise<string>` - Joined stdout (rejects with stderr on error)
70
+
71
+ ### Reactive Command Execution
72
+
73
+ #### `execRx(cmd, options?, emitStdErr?)`
74
+
75
+ Execute a command reactively, emitting stdout (and optionally stderr):
76
+
77
+ ```typescript
78
+ import { execRx } from '@onivoro/server-process';
79
+
80
+ execRx('ping -c 3 google.com').subscribe({
81
+ next: (output) => console.log('Output:', output),
82
+ error: (err) => console.error('Error:', err),
83
+ complete: () => console.log('Command completed')
84
+ });
85
+
86
+ // Without stderr
87
+ execRx('ls -la', undefined, false).subscribe(console.log);
88
+ ```
89
+
90
+ **Parameters:**
91
+ - `cmd: string` - Command to execute
92
+ - `options?: EncodingOption & ExecOptions` - Node.js exec options
93
+ - `emitStdErr?: boolean` - Include stderr in output (default: true)
94
+
95
+ **Returns:** `Observable<string>` - Command output stream
96
+
97
+ #### `execRxAsLines(cmd, options?, emitStdErr?)`
98
+
99
+ Execute a command and emit output line by line:
100
+
101
+ ```typescript
102
+ import { execRxAsLines } from '@onivoro/server-process';
103
+
104
+ execRxAsLines('cat large-file.txt').subscribe({
105
+ next: (line) => console.log('Line:', line),
106
+ complete: () => console.log('File processed')
107
+ });
108
+ ```
109
+
110
+ **Parameters:**
111
+ - `cmd: string` - Command to execute
112
+ - `options?: ExecOptions` - Node.js exec options
113
+ - `emitStdErr?: boolean` - Include stderr in output (default: true)
114
+
115
+ **Returns:** `Observable<string>` - Stream of individual lines
116
+
117
+ #### `execRxAsJson(cmd, options?, emitStdErr?)`
118
+
119
+ Execute a command and parse output as JSON:
120
+
121
+ ```typescript
122
+ import { execRxAsJson } from '@onivoro/server-process';
123
+
124
+ execRxAsJson('docker inspect my-container').subscribe({
125
+ next: (json) => console.log('Container info:', json),
126
+ error: (err) => console.error('Failed to parse JSON:', err)
127
+ });
128
+ ```
129
+
130
+ **Parameters:**
131
+ - `cmd: string` - Command to execute
132
+ - `options?: ExecOptions` - Node.js exec options
133
+ - `emitStdErr?: boolean` - Include stderr in output (default: true)
134
+
135
+ **Returns:** `Observable<any>` - Stream of parsed JSON objects
136
+
137
+ ### Docker Class
138
+
139
+ Execute commands within Docker containers:
140
+
141
+ ```typescript
142
+ import { Docker } from '@onivoro/server-process';
143
+
144
+ const docker = new Docker('my-postgres-container', 'psql');
145
+
146
+ // Execute psql command in container
147
+ docker.execRx('-c "SELECT version();"').subscribe({
148
+ next: (result) => console.log('DB Version:', result),
149
+ error: (err) => console.error('Query failed:', err)
150
+ });
151
+ ```
152
+
153
+ **Constructor:**
154
+ - `containerName: string` - Name of the Docker container
155
+ - `binaryName: string` - Binary to execute within the container
156
+
157
+ **Methods:**
158
+ - `execRx(cmd, options?, emitStdErr?)` - Execute command reactively within container
159
+
160
+ ### PSql Class
161
+
162
+ Execute PostgreSQL commands with optional Docker container support:
163
+
164
+ ```typescript
165
+ import { PSql } from '@onivoro/server-process';
166
+
167
+ // Local psql
168
+ const psql = new PSql();
169
+ psql.execRx('SELECT COUNT(*) FROM users;', 'mydb', 'postgres').subscribe(console.log);
170
+
171
+ // Container-based psql
172
+ const containerPsql = new PSql('postgres-container');
173
+ containerPsql.execRx('SELECT NOW();', 'mydb', 'postgres').subscribe(console.log);
174
+ ```
175
+
176
+ **Constructor:**
177
+ - `containerName?: string` - Optional Docker container name
178
+
179
+ **Methods:**
180
+ - `execRx(cmd, db, username)` - Execute psql command
181
+ - `cmd: string` - SQL command to execute
182
+ - `db: string` - Database name
183
+ - `username: string` - PostgreSQL username
184
+
185
+ ### Process I/O Management
186
+
187
+ #### `listen()`
188
+
189
+ Create reactive streams for process stdin/stdout:
190
+
191
+ ```typescript
192
+ import { listen } from '@onivoro/server-process';
193
+
194
+ const { stdout, stdin } = listen();
195
+
196
+ // Handle stdin data
197
+ stdin.subscribe((data) => {
198
+ console.log('Received input:', data.toString());
199
+ });
200
+
201
+ // stdin automatically completes when process.stdin closes
202
+ ```
203
+
204
+ **Returns:** `{ stdout: Subject, stdin: Subject }` - Reactive streams for process I/O
205
+
206
+ #### `exit(code)`
207
+
208
+ Create a process exit function with specified exit code:
209
+
210
+ ```typescript
211
+ import { exit } from '@onivoro/server-process';
212
+
213
+ const exitWithError = exit(1);
214
+ const exitSuccess = exit(0);
215
+
216
+ // Use in error handling
217
+ if (someErrorCondition) {
218
+ console.error('Fatal error occurred');
219
+ exitWithError();
220
+ }
221
+ ```
222
+
223
+ **Parameters:**
224
+ - `code: number` - Exit code
225
+
226
+ **Returns:** `() => never` - Function that exits the process
227
+
228
+ ## Usage Examples
229
+
230
+ ### Basic Command Execution
231
+
232
+ ```typescript
233
+ import { execPromise, spawnPromise } from '@onivoro/server-process';
234
+
235
+ async function deployApp() {
236
+ try {
237
+ // Check if Docker is running
238
+ await execPromise('docker --version');
239
+
240
+ // Build and deploy
241
+ const buildOutput = await spawnPromise('docker', ['build', '-t', 'myapp', '.']);
242
+ console.log('Build completed:', buildOutput);
243
+
244
+ const runOutput = await spawnPromise('docker', ['run', '-d', '-p', '3000:3000', 'myapp']);
245
+ console.log('Container started:', runOutput);
246
+ } catch (error) {
247
+ console.error('Deployment failed:', error);
248
+ }
249
+ }
250
+ ```
251
+
252
+ ### Reactive Log Monitoring
253
+
254
+ ```typescript
255
+ import { execRxAsLines } from '@onivoro/server-process';
256
+
257
+ function monitorLogs(logFile: string) {
258
+ execRxAsLines(`tail -f ${logFile}`)
259
+ .subscribe({
260
+ next: (line) => {
261
+ if (line.includes('ERROR')) {
262
+ console.error('🚨 Error detected:', line);
263
+ // Trigger alert
264
+ } else if (line.includes('WARN')) {
265
+ console.warn('⚠️ Warning:', line);
266
+ }
267
+ },
268
+ error: (err) => console.error('Log monitoring failed:', err)
269
+ });
270
+ }
271
+
272
+ monitorLogs('/var/log/app.log');
273
+ ```
274
+
275
+ ### Database Operations with Docker
276
+
277
+ ```typescript
278
+ import { PSql } from '@onivoro/server-process';
279
+
280
+ class DatabaseManager {
281
+ private psql = new PSql('postgres-container');
282
+
283
+ async checkHealth() {
284
+ return new Promise((resolve, reject) => {
285
+ this.psql.execRx('SELECT 1;', 'postgres', 'admin').subscribe({
286
+ next: (result) => {
287
+ console.log('Database is healthy');
288
+ resolve(result);
289
+ },
290
+ error: reject
291
+ });
292
+ });
293
+ }
294
+
295
+ async getUserCount(database: string) {
296
+ return new Promise((resolve, reject) => {
297
+ this.psql.execRx('SELECT COUNT(*) FROM users;', database, 'admin').subscribe({
298
+ next: (result) => resolve(parseInt(result.trim())),
299
+ error: reject
300
+ });
301
+ });
302
+ }
303
+ }
304
+ ```
305
+
306
+ ### JSON Processing
307
+
308
+ ```typescript
309
+ import { execRxAsJson } from '@onivoro/server-process';
310
+
311
+ interface ContainerInfo {
312
+ Id: string;
313
+ Name: string;
314
+ State: { Status: string };
315
+ }
316
+
317
+ function getContainerStatus(containerName: string) {
318
+ execRxAsJson<ContainerInfo[]>(`docker inspect ${containerName}`).subscribe({
319
+ next: (containers) => {
320
+ const container = containers[0];
321
+ console.log(`Container ${container.Name} is ${container.State.Status}`);
322
+ },
323
+ error: (err) => console.error('Failed to get container info:', err)
324
+ });
325
+ }
326
+ ```
327
+
328
+ ### Process I/O Handling
329
+
330
+ ```typescript
331
+ import { listen, exit } from '@onivoro/server-process';
332
+
333
+ function setupInteractiveMode() {
334
+ const { stdin } = listen();
335
+
336
+ console.log('Enter commands (type "exit" to quit):');
337
+
338
+ stdin.subscribe({
339
+ next: (data) => {
340
+ const input = data.toString().trim();
341
+
342
+ if (input === 'exit') {
343
+ console.log('Goodbye!');
344
+ exit(0)();
345
+ } else {
346
+ console.log(`You entered: ${input}`);
347
+ }
348
+ },
349
+ complete: () => {
350
+ console.log('Input stream closed');
351
+ exit(0)();
352
+ }
353
+ });
354
+ }
355
+ ```
356
+
357
+ ## Error Handling
358
+
359
+ All functions handle errors appropriately:
360
+
361
+ - **Promise-based functions** reject with error details
362
+ - **Reactive functions** emit errors through the error channel
363
+ - **Process execution errors** include exit codes and stderr information
364
+
365
+ ```typescript
366
+ import { execPromise, execRx } from '@onivoro/server-process';
367
+
368
+ // Promise error handling
369
+ try {
370
+ await execPromise('nonexistent-command');
371
+ } catch (error) {
372
+ console.error('Command failed:', error.message);
373
+ }
374
+
375
+ // Reactive error handling
376
+ execRx('invalid-command').subscribe({
377
+ next: (output) => console.log(output),
378
+ error: (error) => console.error('Command error:', error),
379
+ complete: () => console.log('Done')
380
+ });
381
+ ```
382
+
383
+ ## TypeScript Support
384
+
385
+ All functions are fully typed with TypeScript interfaces. The library uses Node.js built-in types for options and return values.
386
+
387
+ ## License
388
+
389
+ This library is part of the Onivoro monorepo ecosystem.
package/jest.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ displayName: 'lib-server-process',
4
+ preset: '../../../jest.preset.js',
5
+ testEnvironment: 'node',
6
+ transform: {
7
+ '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
8
+ },
9
+ moduleFileExtensions: ['ts', 'js', 'html'],
10
+ coverageDirectory: '../../../coverage/libs/server/process',
11
+ };
package/package.json CHANGED
@@ -1,42 +1,10 @@
1
1
  {
2
- "name": "@onivoro/server-process",
3
- "version": "22.0.2",
4
- "repository": {
5
- "url": "git+https://github.com/onivoro/server-process.git"
6
- },
7
- "main": "dist/cjs/index.js",
8
- "module": "dist/esm/index.js",
9
- "types": "dist/types/index.d.ts",
10
- "files": [
11
- "dist/*"
12
- ],
13
- "scripts": {
14
- "onx": "onx",
15
- "build": "onx Build",
16
- "deploy": "onx Publish",
17
- "test": "onx Test",
18
- "update": "onx Update"
19
- },
20
- "exports": {
21
- ".": {
22
- "types": "./dist/types/index.d.ts",
23
- "require": "./dist/cjs/index.js",
24
- "import": "./dist/esm/index.js",
25
- "default": "./dist/esm/lib.js"
26
- }
27
- },
28
- "onx": {
29
- "platform": "server",
30
- "module": "commonjs"
31
- },
32
- "devDependencies": {
33
- "@onivoro/cli": "^22.0.8",
34
- "@types/jest": "*",
35
- "@types/node": "^22.8.1",
36
- "typescript": "*"
37
- },
38
- "engines": {
39
- "node": "22.10.0",
40
- "npm": "10.9.0"
41
- }
2
+ "name": "@onivoro/server-process",
3
+ "version": "24.0.1",
4
+ "type": "commonjs",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "dependencies": {
8
+ "tslib": "^2.3.0"
9
+ }
42
10
  }
package/project.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "lib-server-process",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/server/process/src",
5
+ "projectType": "library",
6
+ "targets": {
7
+ "build": {
8
+ "executor": "@nx/js:tsc",
9
+ "outputs": ["{options.outputPath}"],
10
+ "options": {
11
+ "outputPath": "dist/libs/server/process",
12
+ "main": "libs/server/process/src/index.ts",
13
+ "tsConfig": "libs/server/process/tsconfig.lib.json",
14
+ "assets": [
15
+ "libs/server/process/README.md",
16
+ "libs/server/process/package.json"
17
+ ],
18
+ "declaration": true
19
+ }
20
+ }
21
+ },
22
+ "tags": []
23
+ }
@@ -0,0 +1,14 @@
1
+ import { ExecOptions } from "child_process";
2
+ import { EncodingOption } from "fs";
3
+ import { execRx } from "./exec-rx";
4
+
5
+ export class Docker {
6
+ constructor(
7
+ public readonly containerName: string,
8
+ public readonly binaryName: string,
9
+ ) { }
10
+
11
+ execRx(cmd: string, options?: EncodingOption & ExecOptions, emitStdErr=true) {
12
+ return execRx(`docker exec ${this.containerName} ${this.binaryName} ${cmd}`, options, emitStdErr);
13
+ }
14
+ }
@@ -0,0 +1,17 @@
1
+ import { parse } from 'path';
2
+ import { execPromise } from './exec-promise';
3
+
4
+ describe('execPromise', () => {
5
+ it('resolves with stdout', async () => {
6
+ const result = await execPromise(`ls ${__dirname}`);
7
+ expect(result).toContain(parse(__filename).base);
8
+ });
9
+
10
+ it('rejects with stderr', async () => {
11
+ try {
12
+ await execPromise(`ls 'no way jose'`);
13
+ } catch (e) {
14
+ expect(e.message.replace(/\n/g, ' ')).toContain('No such file or directory');
15
+ }
16
+ });
17
+ });
@@ -0,0 +1,14 @@
1
+ import { exec, ExecOptions } from "child_process";
2
+ import { EncodingOption } from "fs";
3
+
4
+ export function execPromise(cmd: string, options?: EncodingOption & ExecOptions): Promise<any> {
5
+ return new Promise((resolve, reject) => {
6
+ exec(cmd, options, (err, stdout) => {
7
+ if (err) {
8
+ reject(err);
9
+ } else {
10
+ resolve(stdout.toString());
11
+ }
12
+ });
13
+ });
14
+ }
@@ -0,0 +1,9 @@
1
+ import { ExecOptions } from 'child_process';
2
+ import { map } from 'rxjs/operators';
3
+ import { execRx } from './exec-rx';
4
+
5
+ export const execRxAsJson = (cmd: string, options?: ExecOptions, emitStdErr = true) => {
6
+ return execRx(cmd, options, emitStdErr).pipe(
7
+ map((s: string) => JSON.parse(s)),
8
+ )
9
+ };
@@ -0,0 +1,10 @@
1
+ import { ExecOptions } from 'child_process';
2
+ import { from } from 'rxjs';
3
+ import { concatMap } from 'rxjs/operators';
4
+ import { execRx } from './exec-rx';
5
+
6
+ export const execRxAsLines = (cmd: string, options?: ExecOptions, emitStdErr = true) => {
7
+ return execRx(cmd, options, emitStdErr).pipe(
8
+ concatMap((s: string) => from(s.split('\n'))),
9
+ )
10
+ };
@@ -0,0 +1,22 @@
1
+ import { execRx } from './exec-rx';
2
+ import { of } from 'rxjs';
3
+ import { catchError } from 'rxjs/operators';
4
+
5
+ describe(execRx.name, () => {
6
+ describe('GIVEN command succeeds', () => {
7
+ it('returns the stdout', (done) => {
8
+ execRx(`cat ${__filename}`).subscribe((d) => {
9
+ expect(d).toEqual(expect.stringContaining('execRx worx!'));
10
+ done();
11
+ }, () => { throw new Error("fail") }
12
+ );
13
+ });
14
+ });
15
+
16
+ describe('GIVEN command fails', () => {
17
+ it('emits error', (done) => {
18
+ execRx(`cat ${__filename + 'blah'}`).pipe(catchError(() => of(done())))
19
+ .subscribe();
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,16 @@
1
+ import { exec, ExecOptions } from "child_process";
2
+ import { EncodingOption } from "fs";
3
+ import { Observable } from "rxjs";
4
+
5
+ export function execRx(cmd: string, options?: EncodingOption & ExecOptions, emitStdErr=true): Observable<any> {
6
+ return new Observable((observer) => {
7
+ exec(cmd, options, (err, stdout, stderr) => {
8
+ if (err) {
9
+ observer.error(err);
10
+ } else {
11
+ observer.next(`${stdout}${emitStdErr && ` ${stderr}`}`);
12
+ observer.complete();
13
+ }
14
+ });
15
+ });
16
+ }
@@ -0,0 +1 @@
1
+ export const exit = (code: number) => process.exit.bind(process, code);
@@ -0,0 +1,12 @@
1
+ import { Subject } from 'rxjs';
2
+
3
+ const stdin = new Subject();
4
+ const stdout = new Subject();
5
+
6
+ export const listen = () => {
7
+
8
+ process.stdin.on('data', d => stdin.next(d));
9
+ process.stdin.on('close', () => stdin.complete());
10
+
11
+ return { stdout, stdin };
12
+ }
@@ -0,0 +1,16 @@
1
+ import { Docker } from "./docker";
2
+ import { execRx } from "./exec-rx";
3
+
4
+ const binaryName = 'psql';
5
+
6
+ export class PSql {
7
+ constructor(public readonly containerName: string = '') { }
8
+
9
+ execRx(cmd: string, db: string, username: string) {
10
+ const dbOptions = db ? ['-d', db] : [];
11
+ const commonArgs = ['-qtAX', '-U', username, ...dbOptions, '-c'].join(' ') + cmd;
12
+ return this.containerName
13
+ ? (new Docker(this.containerName, binaryName)).execRx(commonArgs)
14
+ : execRx([binaryName, commonArgs].join(' '));
15
+ }
16
+ }
@@ -0,0 +1,17 @@
1
+ import { parse } from 'path';
2
+ import { spawnPromise } from './spawn-promise';
3
+
4
+ describe('spawnPromise', () => {
5
+ it('resolves with stdout', async () => {
6
+ const result = await spawnPromise(`ls`, [__dirname]);
7
+ expect(result).toContain(parse(__filename).base);
8
+ });
9
+
10
+ it('rejects with stderr', async () => {
11
+ try {
12
+ await spawnPromise(`ls`, ['no way jose']);
13
+ } catch (e) {
14
+ expect(e).toContain('No such file or directory');
15
+ }
16
+ });
17
+ });
@@ -0,0 +1,31 @@
1
+ import { spawn } from "child_process";
2
+
3
+ const data = 'data';
4
+
5
+ export function spawnPromise(program: string, args?: string[], options?: any) {
6
+ return new Promise((res, rej) => {
7
+ let stdout: string[] = [];
8
+ let stderr: string[] = [];
9
+
10
+ const proc = spawn(program, args, options);
11
+
12
+ proc.stdout.on(data, (data) => {
13
+ stdout.push(`${data}`);
14
+ });
15
+
16
+ proc.stderr.on(data, (data) => {
17
+ stderr.push(`${data}`);
18
+ });
19
+
20
+ proc.on('close', (code) => {
21
+ if (code) {
22
+ rej(stderr.join(' '));
23
+ } else {
24
+ res(stdout.join(' '));
25
+ }
26
+
27
+ stdout = undefined as any;
28
+ stderr = undefined as any;
29
+ });
30
+ });
31
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../../tsconfig.server.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../../dist/out-tsc"
5
+ },
6
+ "files": [],
7
+ "include": [],
8
+ "references": [
9
+ {
10
+ "path": "./tsconfig.lib.json"
11
+ },
12
+ {
13
+ "path": "./tsconfig.spec.json"
14
+ }
15
+ ]
16
+ }