@livestore/utils-dev 0.4.0 → 0.5.0-dev.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/node/DockerCompose/DockerCompose.d.ts +52 -0
- package/dist/node/DockerCompose/DockerCompose.d.ts.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.js +158 -0
- package/dist/node/DockerCompose/DockerCompose.js.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts +2 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts.map +1 -0
- package/dist/node/{DockerComposeService/DockerComposeService.test.js → DockerCompose/DockerCompose.test.js} +16 -16
- package/dist/node/DockerCompose/DockerCompose.test.js.map +1 -0
- package/dist/node/DockerCompose/mod.d.ts +2 -0
- package/dist/node/DockerCompose/mod.d.ts.map +1 -0
- package/dist/node/DockerCompose/mod.js +2 -0
- package/dist/node/DockerCompose/mod.js.map +1 -0
- package/dist/node/FileLogger.d.ts +15 -8
- package/dist/node/FileLogger.d.ts.map +1 -1
- package/dist/node/FileLogger.js +63 -79
- package/dist/node/FileLogger.js.map +1 -1
- package/dist/node/cmd-log.d.ts.map +1 -1
- package/dist/node/cmd-log.js +19 -8
- package/dist/node/cmd-log.js.map +1 -1
- package/dist/node/cmd.d.ts +10 -12
- package/dist/node/cmd.d.ts.map +1 -1
- package/dist/node/cmd.js +73 -36
- package/dist/node/cmd.js.map +1 -1
- package/dist/node/cmd.test.js +17 -17
- package/dist/node/cmd.test.js.map +1 -1
- package/dist/node/mod.d.ts +4 -5
- package/dist/node/mod.d.ts.map +1 -1
- package/dist/node/mod.js +58 -55
- package/dist/node/mod.js.map +1 -1
- package/dist/node/workspace.d.ts +2 -2
- package/dist/node/workspace.d.ts.map +1 -1
- package/dist/node/workspace.js +7 -7
- package/dist/node/workspace.js.map +1 -1
- package/dist/node-vitest/Vitest.d.ts +17 -8
- package/dist/node-vitest/Vitest.d.ts.map +1 -1
- package/dist/node-vitest/Vitest.js +5 -5
- package/dist/node-vitest/Vitest.js.map +1 -1
- package/dist/node-vitest/Vitest.test.d.ts +3 -5
- package/dist/node-vitest/Vitest.test.d.ts.map +1 -1
- package/dist/node-vitest/Vitest.test.js +14 -3
- package/dist/node-vitest/Vitest.test.js.map +1 -1
- package/dist/wrangler/WranglerDevServer.d.ts +15 -20
- package/dist/wrangler/WranglerDevServer.d.ts.map +1 -1
- package/dist/wrangler/WranglerDevServer.js +77 -71
- package/dist/wrangler/WranglerDevServer.js.map +1 -1
- package/dist/wrangler/WranglerDevServer.test.js +28 -28
- package/dist/wrangler/WranglerDevServer.test.js.map +1 -1
- package/dist/wrangler/mod.d.ts +1 -1
- package/dist/wrangler/mod.d.ts.map +1 -1
- package/dist/wrangler/mod.js +1 -1
- package/dist/wrangler/mod.js.map +1 -1
- package/package.json +19 -37
- package/src/node/{DockerComposeService/DockerComposeService.test.ts → DockerCompose/DockerCompose.test.ts} +18 -18
- package/src/node/DockerCompose/DockerCompose.ts +328 -0
- package/src/node/DockerCompose/mod.ts +1 -0
- package/src/node/FileLogger.ts +89 -114
- package/src/node/cmd-log.ts +21 -10
- package/src/node/cmd.test.ts +19 -17
- package/src/node/cmd.ts +117 -86
- package/src/node/mod.ts +112 -104
- package/src/node/workspace.ts +9 -10
- package/src/node-vitest/Vitest.test.ts +22 -3
- package/src/node-vitest/Vitest.ts +32 -42
- package/src/wrangler/WranglerDevServer.test.ts +33 -37
- package/src/wrangler/WranglerDevServer.ts +145 -129
- package/src/wrangler/mod.ts +1 -6
- package/dist/node/DockerComposeService/DockerComposeService.d.ts +0 -63
- package/dist/node/DockerComposeService/DockerComposeService.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.js +0 -149
- package/dist/node/DockerComposeService/DockerComposeService.js.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts +0 -2
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.js.map +0 -1
- package/src/node/DockerComposeService/DockerComposeService.ts +0 -343
- /package/src/node/{DockerComposeService → DockerCompose}/test-fixtures/docker-compose.yml +0 -0
package/src/node/cmd.test.ts
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
|
|
4
|
-
import { PlatformNode } from '@livestore/utils/node'
|
|
5
|
-
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
3
|
+
|
|
6
4
|
import { expect } from 'vitest'
|
|
7
5
|
|
|
6
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
7
|
+
import { ChildProcessSpawner, Duration, Effect, Layer } from '@livestore/utils/effect'
|
|
8
|
+
import { PlatformNode } from '@livestore/utils/node'
|
|
9
|
+
|
|
8
10
|
import { cmd } from './cmd.ts'
|
|
9
11
|
import { CurrentWorkingDirectory } from './workspace.ts'
|
|
10
12
|
|
|
11
13
|
const withNode = Vitest.makeWithTestCtx({
|
|
12
|
-
makeLayer: () => Layer.mergeAll(PlatformNode.
|
|
14
|
+
makeLayer: () => Layer.mergeAll(PlatformNode.NodeServices.layer, CurrentWorkingDirectory.live),
|
|
13
15
|
timeout: 20_000,
|
|
14
16
|
})
|
|
15
17
|
|
|
16
18
|
Vitest.describe('cmd helper', () => {
|
|
17
19
|
const ansiRegex = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g')
|
|
18
20
|
|
|
19
|
-
Vitest.
|
|
21
|
+
Vitest.live('runs tokenized string without shell', (test) =>
|
|
20
22
|
Effect.gen(function* () {
|
|
21
23
|
const exit = yield* cmd('printf ok')
|
|
22
|
-
expect(exit).toBe(
|
|
24
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
23
25
|
}).pipe(withNode(test)),
|
|
24
26
|
)
|
|
25
27
|
|
|
26
|
-
Vitest.
|
|
28
|
+
Vitest.live('runs array input', (test) =>
|
|
27
29
|
Effect.gen(function* () {
|
|
28
30
|
const exit = yield* cmd(['printf', 'ok'])
|
|
29
|
-
expect(exit).toBe(
|
|
31
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
30
32
|
}).pipe(withNode(test)),
|
|
31
33
|
)
|
|
32
34
|
|
|
33
35
|
/** TODO(#1020): Investigate CI timeout flakiness for cmd tests that spawn many child processes */
|
|
34
|
-
Vitest.
|
|
36
|
+
Vitest.live(
|
|
35
37
|
'supports logging with archive + retention',
|
|
36
38
|
(test) =>
|
|
37
39
|
Effect.gen(function* () {
|
|
@@ -40,7 +42,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
40
42
|
|
|
41
43
|
// first run
|
|
42
44
|
const exit1 = yield* cmd('printf first', { logDir: logsDir })
|
|
43
|
-
expect(exit1).toBe(
|
|
45
|
+
expect(exit1).toBe(ChildProcessSpawner.ExitCode(0))
|
|
44
46
|
const current = path.join(logsDir, 'dev.log')
|
|
45
47
|
expect(fs.existsSync(current)).toBe(true)
|
|
46
48
|
const firstLog = fs.readFileSync(current, 'utf8')
|
|
@@ -48,13 +50,13 @@ Vitest.describe('cmd helper', () => {
|
|
|
48
50
|
expect(firstStdoutLines.length).toBeGreaterThan(0)
|
|
49
51
|
for (const line of firstStdoutLines) {
|
|
50
52
|
expect(line).toContain('[stdout] first')
|
|
51
|
-
expect(line).
|
|
53
|
+
expect(line).toMatch(/Info/i)
|
|
52
54
|
expect(line).toContain('printf first')
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
// second run — archives previous
|
|
56
58
|
const exit2 = yield* cmd('printf second', { logDir: logsDir })
|
|
57
|
-
expect(exit2).toBe(
|
|
59
|
+
expect(exit2).toBe(ChildProcessSpawner.ExitCode(0))
|
|
58
60
|
const archiveDir = path.join(logsDir, 'archive')
|
|
59
61
|
const archives = fs.readdirSync(archiveDir).filter((f) => f.endsWith('.log'))
|
|
60
62
|
expect(archives.length).toBe(1)
|
|
@@ -71,7 +73,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
71
73
|
expect(secondStdoutLines.length).toBeGreaterThan(0)
|
|
72
74
|
for (const line of secondStdoutLines) {
|
|
73
75
|
expect(line).toContain('[stdout] second')
|
|
74
|
-
expect(line).
|
|
76
|
+
expect(line).toMatch(/Info/i)
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// generate many archives to exercise retention (keep 50)
|
|
@@ -85,7 +87,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
85
87
|
{ timeout: 30_000, retry: 3 },
|
|
86
88
|
)
|
|
87
89
|
|
|
88
|
-
Vitest.
|
|
90
|
+
Vitest.live('streams stdout and stderr with logger formatting', (test) =>
|
|
89
91
|
Effect.gen(function* () {
|
|
90
92
|
const workspace = process.env.WORKSPACE_ROOT!
|
|
91
93
|
const logsDir = path.join(workspace, 'tmp', 'cmd-tests', `format-${Date.now()}`)
|
|
@@ -93,7 +95,7 @@ Vitest.describe('cmd helper', () => {
|
|
|
93
95
|
const exit = yield* cmd(['node', '-e', "console.log('out'); console.error('err')"], {
|
|
94
96
|
logDir: logsDir,
|
|
95
97
|
})
|
|
96
|
-
expect(exit).toBe(
|
|
98
|
+
expect(exit).toBe(ChildProcessSpawner.ExitCode(0))
|
|
97
99
|
|
|
98
100
|
const current = path.join(logsDir, 'dev.log')
|
|
99
101
|
const logContent = fs.readFileSync(current, 'utf8')
|
|
@@ -110,13 +112,13 @@ Vitest.describe('cmd helper', () => {
|
|
|
110
112
|
for (const line of relevantLines) {
|
|
111
113
|
const stripped = line.replace(ansiRegex, '')
|
|
112
114
|
expect(stripped.startsWith('[')).toBe(true)
|
|
113
|
-
expect(stripped).toMatch(/(
|
|
115
|
+
expect(stripped).toMatch(/(Info|Warn)/i)
|
|
114
116
|
expect(stripped).toMatch(/\[(stdout|stderr)]/)
|
|
115
117
|
}
|
|
116
118
|
}).pipe(withNode(test)),
|
|
117
119
|
)
|
|
118
120
|
|
|
119
|
-
Vitest.
|
|
121
|
+
Vitest.live('cleans up logged child process when interrupted', (test) =>
|
|
120
122
|
Effect.gen(function* () {
|
|
121
123
|
const workspace = process.env.WORKSPACE_ROOT!
|
|
122
124
|
const logsDir = path.join(workspace, 'tmp', 'cmd-tests', `timeout-${Date.now()}`)
|
package/src/node/cmd.ts
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
|
|
3
|
-
import { isNotUndefined } from '@livestore/utils'
|
|
4
1
|
import {
|
|
5
2
|
Cause,
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
ChildProcess,
|
|
4
|
+
ChildProcessSpawner,
|
|
8
5
|
Effect,
|
|
9
6
|
Fiber,
|
|
10
|
-
|
|
11
|
-
FiberRefs,
|
|
12
|
-
HashMap,
|
|
13
|
-
identity,
|
|
14
|
-
List,
|
|
15
|
-
LogLevel,
|
|
7
|
+
Logger,
|
|
16
8
|
type PlatformError,
|
|
9
|
+
Predicate,
|
|
10
|
+
References,
|
|
17
11
|
Schema,
|
|
18
12
|
Stream,
|
|
19
13
|
} from '@livestore/utils/effect'
|
|
14
|
+
import { NodeFileSystem } from '@livestore/utils/node'
|
|
20
15
|
|
|
21
16
|
import { applyLoggingToCommand } from './cmd-log.ts'
|
|
22
17
|
import * as FileLogger from './FileLogger.ts'
|
|
23
18
|
import { CurrentWorkingDirectory } from './workspace.ts'
|
|
24
19
|
|
|
25
20
|
// Branded zero value so we can compare exit codes without touching internals.
|
|
26
|
-
const SUCCESS_EXIT_CODE:
|
|
21
|
+
const SUCCESS_EXIT_CODE: ChildProcessSpawner.ExitCode = ChildProcessSpawner.ExitCode(0)
|
|
27
22
|
|
|
28
23
|
export const cmd: (
|
|
29
24
|
commandInput: string | (string | undefined)[],
|
|
@@ -43,14 +38,14 @@ export const cmd: (
|
|
|
43
38
|
logRetention?: number
|
|
44
39
|
},
|
|
45
40
|
) => Effect.Effect<
|
|
46
|
-
|
|
41
|
+
ChildProcessSpawner.ExitCode,
|
|
47
42
|
PlatformError.PlatformError | CmdError,
|
|
48
|
-
|
|
43
|
+
ChildProcessSpawner.ChildProcessSpawner | CurrentWorkingDirectory
|
|
49
44
|
> = Effect.fn('cmd')(function* (commandInput, options) {
|
|
50
45
|
const cwd = yield* CurrentWorkingDirectory
|
|
51
46
|
|
|
52
47
|
const asArray = Array.isArray(commandInput)
|
|
53
|
-
const parts = asArray === true ? commandInput.filter(isNotUndefined) : undefined
|
|
48
|
+
const parts = asArray === true ? commandInput.filter(Predicate.isNotUndefined) : undefined
|
|
54
49
|
const [command, ...args] = asArray === true ? (parts as string[]) : commandInput.split(' ')
|
|
55
50
|
|
|
56
51
|
const debugEnvStr = Object.entries(options?.env ?? {})
|
|
@@ -89,7 +84,7 @@ export const cmd: (
|
|
|
89
84
|
useShell,
|
|
90
85
|
} as const
|
|
91
86
|
|
|
92
|
-
const exitCode = yield* isNotUndefined(logPath) === true
|
|
87
|
+
const exitCode = yield* Predicate.isNotUndefined(logPath) === true
|
|
93
88
|
? Effect.gen(function* () {
|
|
94
89
|
yield* Effect.sync(() => console.log(`Logging output to ${logPath}`))
|
|
95
90
|
return yield* runWithLogging({ ...baseArgs, logPath, threadName: commandDebugStr })
|
|
@@ -116,37 +111,42 @@ export const cmdText: (
|
|
|
116
111
|
runInShell?: boolean
|
|
117
112
|
env?: Record<string, string | undefined>
|
|
118
113
|
},
|
|
119
|
-
) => Effect.Effect<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
114
|
+
) => Effect.Effect<
|
|
115
|
+
string,
|
|
116
|
+
PlatformError.PlatformError,
|
|
117
|
+
ChildProcessSpawner.ChildProcessSpawner | CurrentWorkingDirectory
|
|
118
|
+
> = Effect.fn('cmdText')(function* (commandInput, options) {
|
|
119
|
+
const cwd = yield* CurrentWorkingDirectory
|
|
120
|
+
const commandParts =
|
|
121
|
+
Array.isArray(commandInput) === true ? commandInput.filter(Predicate.isNotUndefined) : commandInput.split(' ')
|
|
122
|
+
const [command, ...args] = commandParts
|
|
123
|
+
const debugEnvStr = Object.entries(options?.env ?? {})
|
|
124
|
+
.map(([key, value]) => `${key}='${String(value)}' `)
|
|
125
|
+
.join('')
|
|
126
|
+
|
|
127
|
+
const commandDebugStr = debugEnvStr + [command, ...args].join(' ')
|
|
128
|
+
const subshellStr = options?.runInShell === true ? ' (in subshell)' : ''
|
|
129
|
+
|
|
130
|
+
yield* Effect.logDebug(`Running '${commandDebugStr}' in '${cwd}'${subshellStr}`)
|
|
131
|
+
yield* Effect.annotateCurrentSpan({ 'span.label': commandDebugStr, command, cwd })
|
|
132
|
+
|
|
133
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
134
|
+
return yield* spawner.string(
|
|
135
|
+
buildCommand(commandParts, {
|
|
136
|
+
cwd,
|
|
137
|
+
env: options?.env ?? {},
|
|
138
|
+
stderr: options?.stderr ?? 'inherit',
|
|
139
|
+
shell: options?.runInShell === true,
|
|
140
|
+
}),
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
143
|
|
|
144
144
|
export class CmdError extends Schema.TaggedError<CmdError>('~@livestore/utils-dev/CmdError')('CmdError', {
|
|
145
145
|
command: Schema.String,
|
|
146
146
|
args: Schema.Array(Schema.String),
|
|
147
147
|
cwd: Schema.String,
|
|
148
|
-
env: Schema.Record(
|
|
149
|
-
stderr: Schema.
|
|
148
|
+
env: Schema.Record(Schema.String, Schema.String.pipe(Schema.UndefinedOr)),
|
|
149
|
+
stderr: Schema.Literals(['inherit', 'pipe']),
|
|
150
150
|
}) {}
|
|
151
151
|
|
|
152
152
|
type TRunBaseArgs = {
|
|
@@ -159,15 +159,19 @@ type TRunBaseArgs = {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
const runWithoutLogging = ({ commandInput, cwd, env, stdoutMode, stderrMode, useShell }: TRunBaseArgs) =>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
162
|
+
Effect.gen(function* () {
|
|
163
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
164
|
+
return yield* spawner.exitCode(
|
|
165
|
+
buildCommand(commandInput, {
|
|
166
|
+
cwd,
|
|
167
|
+
env,
|
|
168
|
+
stdin: 'inherit',
|
|
169
|
+
stdout: stdoutMode,
|
|
170
|
+
stderr: stderrMode,
|
|
171
|
+
shell: useShell,
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
})
|
|
171
175
|
|
|
172
176
|
type TRunWithLoggingArgs = TRunBaseArgs & {
|
|
173
177
|
readonly logPath: string
|
|
@@ -192,46 +196,54 @@ const runWithLogging = ({
|
|
|
192
196
|
Effect.gen(function* () {
|
|
193
197
|
const envWithColor = env.FORCE_COLOR === undefined ? { ...env, FORCE_COLOR: '1' } : env
|
|
194
198
|
|
|
195
|
-
const
|
|
196
|
-
Effect.sync(() => fs.openSync(logPath, 'a', 0o666)),
|
|
197
|
-
(fd) => Effect.sync(() => fs.closeSync(fd)),
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
const prettyLogger = FileLogger.prettyLoggerTty({
|
|
199
|
+
const prettyLoggerOptions = {
|
|
201
200
|
colors: true,
|
|
202
201
|
stderr: false,
|
|
203
|
-
formatDate:
|
|
204
|
-
}
|
|
202
|
+
formatDate: FileLogger.formatLogTime,
|
|
203
|
+
} as const
|
|
204
|
+
|
|
205
|
+
const logStringToFile = yield* Logger.make<unknown, string>(({ message }) => {
|
|
206
|
+
if (typeof message === 'string') return message
|
|
207
|
+
return String(message)
|
|
208
|
+
}).pipe(Logger.toFile(logPath, { flag: 'a' }), Effect.provide(NodeFileSystem.layer))
|
|
209
|
+
|
|
210
|
+
const FileLoggerLive = Logger.layer([logStringToFile])
|
|
205
211
|
|
|
206
212
|
const appendLog = ({ channel, content }: { channel: 'stdout' | 'stderr'; content: string }) =>
|
|
207
|
-
Effect.
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
logLevel: channel === 'stdout' ?
|
|
213
|
+
Effect.gen(function* () {
|
|
214
|
+
const date = new Date()
|
|
215
|
+
const formatted = FileLogger.formatPrettyLog({
|
|
216
|
+
logLevel: channel === 'stdout' ? 'Info' : 'Warn',
|
|
211
217
|
message: [`[${channel}]${content.length > 0 ? ` ${content}` : ''}`],
|
|
212
218
|
cause: Cause.empty,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
date,
|
|
220
|
+
fiberId: 0,
|
|
221
|
+
spans: [[threadName, date.getTime()]],
|
|
222
|
+
annotations: { thread: threadName },
|
|
223
|
+
options: prettyLoggerOptions,
|
|
217
224
|
})
|
|
218
|
-
|
|
225
|
+
|
|
226
|
+
yield* Effect.log(formatted.endsWith('\n') === true ? formatted.slice(0, -1) : formatted).pipe(
|
|
227
|
+
Effect.provideService(References.MinimumLogLevel, 'All'),
|
|
228
|
+
Effect.provide(FileLoggerLive),
|
|
229
|
+
)
|
|
219
230
|
})
|
|
220
231
|
|
|
221
|
-
const command = buildCommand(commandInput,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
232
|
+
const command = buildCommand(commandInput, {
|
|
233
|
+
cwd,
|
|
234
|
+
env: envWithColor,
|
|
235
|
+
stdin: 'inherit',
|
|
236
|
+
stdout: 'pipe',
|
|
237
|
+
stderr: 'pipe',
|
|
238
|
+
shell: useShell,
|
|
239
|
+
})
|
|
229
240
|
|
|
230
241
|
// Acquire/start the command and make sure we kill it on interruption.
|
|
231
|
-
const
|
|
242
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
|
|
243
|
+
const runningProcess = yield* Effect.acquireRelease(spawner.spawn(command), (proc) =>
|
|
232
244
|
proc.isRunning.pipe(
|
|
233
245
|
Effect.flatMap((running) =>
|
|
234
|
-
running === true ? proc.kill().pipe(Effect.
|
|
246
|
+
running === true ? proc.kill().pipe(Effect.catch(() => Effect.void)) : Effect.void,
|
|
235
247
|
),
|
|
236
248
|
Effect.ignore,
|
|
237
249
|
),
|
|
@@ -249,20 +261,20 @@ const runWithLogging = ({
|
|
|
249
261
|
})
|
|
250
262
|
|
|
251
263
|
const stdoutFiber = yield* runningProcess.stdout.pipe(
|
|
252
|
-
Stream.decodeText('utf8'),
|
|
264
|
+
Stream.decodeText({ encoding: 'utf8' }),
|
|
253
265
|
Stream.runForEach((chunk) => stdoutHandler.onChunk(chunk)),
|
|
254
266
|
Effect.forkScoped,
|
|
255
267
|
)
|
|
256
268
|
|
|
257
269
|
const stderrFiber = yield* runningProcess.stderr.pipe(
|
|
258
|
-
Stream.decodeText('utf8'),
|
|
270
|
+
Stream.decodeText({ encoding: 'utf8' }),
|
|
259
271
|
Stream.runForEach((chunk) => stderrHandler.onChunk(chunk)),
|
|
260
272
|
Effect.forkScoped,
|
|
261
273
|
)
|
|
262
274
|
|
|
263
275
|
// Dump any buffered data and finish both stream fibers before we return.
|
|
264
276
|
const flushOutputs = Effect.gen(function* () {
|
|
265
|
-
const stillRunning = yield* runningProcess.isRunning.pipe(Effect.
|
|
277
|
+
const stillRunning = yield* runningProcess.isRunning.pipe(Effect.orElseSucceed(() => false))
|
|
266
278
|
if (stillRunning === true) {
|
|
267
279
|
yield* Effect.ignore(runningProcess.kill())
|
|
268
280
|
}
|
|
@@ -278,18 +290,37 @@ const runWithLogging = ({
|
|
|
278
290
|
}),
|
|
279
291
|
)
|
|
280
292
|
|
|
281
|
-
|
|
293
|
+
type TBuildCommandOptions = {
|
|
294
|
+
readonly cwd: string
|
|
295
|
+
readonly env: Record<string, string | undefined>
|
|
296
|
+
readonly stdin?: ChildProcess.CommandInput
|
|
297
|
+
readonly stdout?: ChildProcess.CommandOutput
|
|
298
|
+
readonly stderr?: ChildProcess.CommandOutput
|
|
299
|
+
readonly shell: boolean
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const buildCommand = (input: string | string[], options: TBuildCommandOptions): ChildProcess.Command => {
|
|
303
|
+
const commandOptions = {
|
|
304
|
+
cwd: options.cwd,
|
|
305
|
+
env: options.env,
|
|
306
|
+
extendEnv: true,
|
|
307
|
+
shell: options.shell,
|
|
308
|
+
...(options.stdin !== undefined ? { stdin: options.stdin } : {}),
|
|
309
|
+
...(options.stdout !== undefined ? { stdout: options.stdout } : {}),
|
|
310
|
+
...(options.stderr !== undefined ? { stderr: options.stderr } : {}),
|
|
311
|
+
} as const
|
|
312
|
+
|
|
282
313
|
if (Array.isArray(input) === true) {
|
|
283
314
|
const [c, ...a] = input
|
|
284
|
-
return
|
|
315
|
+
return ChildProcess.make(c ?? '', a, commandOptions)
|
|
285
316
|
}
|
|
286
317
|
|
|
287
|
-
if (
|
|
288
|
-
return
|
|
318
|
+
if (options.shell === true) {
|
|
319
|
+
return ChildProcess.make(input, commandOptions)
|
|
289
320
|
}
|
|
290
321
|
|
|
291
322
|
const [c, ...a] = input.split(' ')
|
|
292
|
-
return
|
|
323
|
+
return ChildProcess.make(c ?? '', a, commandOptions)
|
|
293
324
|
}
|
|
294
325
|
|
|
295
326
|
type TLineTerminator = 'newline' | 'carriage-return' | 'none'
|