@effect/platform 0.1.0 → 0.3.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/Command.d.ts +251 -0
- package/Command.d.ts.map +1 -0
- package/Command.js +164 -0
- package/Command.js.map +1 -0
- package/CommandExecutor.d.ts +140 -0
- package/CommandExecutor.d.ts.map +1 -0
- package/CommandExecutor.js +40 -0
- package/CommandExecutor.js.map +1 -0
- package/Error.d.ts +1 -1
- package/Error.d.ts.map +1 -1
- package/internal/command.d.ts +2 -0
- package/internal/command.d.ts.map +1 -0
- package/internal/command.js +184 -0
- package/internal/command.js.map +1 -0
- package/internal/commandExecutor.d.ts +2 -0
- package/internal/commandExecutor.d.ts.map +1 -0
- package/internal/commandExecutor.js +55 -0
- package/internal/commandExecutor.js.map +1 -0
- package/internal/fileSystem.js +1 -1
- package/internal/fileSystem.js.map +1 -1
- package/mjs/Command.mjs +139 -0
- package/mjs/Command.mjs.map +1 -0
- package/mjs/CommandExecutor.mjs +27 -0
- package/mjs/CommandExecutor.mjs.map +1 -0
- package/mjs/internal/command.mjs +159 -0
- package/mjs/internal/command.mjs.map +1 -0
- package/mjs/internal/commandExecutor.mjs +42 -0
- package/mjs/internal/commandExecutor.mjs.map +1 -0
- package/mjs/internal/fileSystem.mjs +1 -1
- package/mjs/internal/fileSystem.mjs.map +1 -1
- package/package.json +4 -4
- package/src/Command.ts +278 -0
- package/src/CommandExecutor.ts +191 -0
- package/src/Error.ts +1 -1
- package/src/internal/command.ts +211 -0
- package/src/internal/commandExecutor.ts +69 -0
- package/src/internal/fileSystem.ts +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import * as Chunk from "@effect/data/Chunk"
|
|
2
|
+
import { dual } from "@effect/data/Function"
|
|
3
|
+
import * as HashMap from "@effect/data/HashMap"
|
|
4
|
+
import * as Option from "@effect/data/Option"
|
|
5
|
+
import type ReadonlyArray from "@effect/data/ReadonlyArray"
|
|
6
|
+
import * as Effect from "@effect/io/Effect"
|
|
7
|
+
import type * as Command from "@effect/platform/Command"
|
|
8
|
+
import type * as CommandExecutor from "@effect/platform/CommandExecutor"
|
|
9
|
+
import type { PlatformError } from "@effect/platform/Error"
|
|
10
|
+
import * as commandExecutor from "@effect/platform/internal/commandExecutor"
|
|
11
|
+
import * as Stream from "@effect/stream/Stream"
|
|
12
|
+
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const CommandTypeId: Command.CommandTypeId = Symbol.for("@effect/platform/Command") as Command.CommandTypeId
|
|
15
|
+
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const isCommand = (u: unknown): u is Command.Command => typeof u === "object" && u != null && CommandTypeId in u
|
|
18
|
+
|
|
19
|
+
/** @internal */
|
|
20
|
+
export const env: {
|
|
21
|
+
(environment: Record<string, string>): (self: Command.Command) => Command.Command
|
|
22
|
+
(self: Command.Command, environment: Record<string, string>): Command.Command
|
|
23
|
+
} = dual<
|
|
24
|
+
(environment: Record<string, string>) => (self: Command.Command) => Command.Command,
|
|
25
|
+
(self: Command.Command, environment: Record<string, string>) => Command.Command
|
|
26
|
+
>(2, (self, environment) => {
|
|
27
|
+
switch (self._tag) {
|
|
28
|
+
case "StandardCommand": {
|
|
29
|
+
return { ...self, env: HashMap.union(self.env, HashMap.fromIterable(Object.entries(environment))) }
|
|
30
|
+
}
|
|
31
|
+
case "PipedCommand": {
|
|
32
|
+
return pipeTo(env(self.left, environment), env(self.right, environment))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
/** @internal */
|
|
38
|
+
export const exitCode = (
|
|
39
|
+
self: Command.Command
|
|
40
|
+
): Effect.Effect<CommandExecutor.CommandExecutor, PlatformError, CommandExecutor.ExitCode> =>
|
|
41
|
+
Effect.flatMap(commandExecutor.CommandExecutor, (executor) => executor.exitCode(self))
|
|
42
|
+
|
|
43
|
+
/** @internal */
|
|
44
|
+
export const feed = dual<
|
|
45
|
+
(input: string) => (self: Command.Command) => Command.Command,
|
|
46
|
+
(self: Command.Command, input: string) => Command.Command
|
|
47
|
+
>(2, (self, input) => stdin(self, Stream.fromChunk(Chunk.of(new TextEncoder().encode(input)))))
|
|
48
|
+
|
|
49
|
+
/** @internal */
|
|
50
|
+
export const flatten = (self: Command.Command): ReadonlyArray.NonEmptyReadonlyArray<Command.StandardCommand> =>
|
|
51
|
+
Array.from(flattenLoop(self)) as unknown as ReadonlyArray.NonEmptyReadonlyArray<
|
|
52
|
+
Command.StandardCommand
|
|
53
|
+
>
|
|
54
|
+
|
|
55
|
+
/** @internal */
|
|
56
|
+
const flattenLoop = (self: Command.Command): Chunk.NonEmptyChunk<Command.StandardCommand> => {
|
|
57
|
+
switch (self._tag) {
|
|
58
|
+
case "StandardCommand": {
|
|
59
|
+
return Chunk.of(self)
|
|
60
|
+
}
|
|
61
|
+
case "PipedCommand": {
|
|
62
|
+
return Chunk.appendAll(
|
|
63
|
+
flattenLoop(self.left),
|
|
64
|
+
flattenLoop(self.right)
|
|
65
|
+
) as Chunk.NonEmptyChunk<Command.StandardCommand>
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @internal */
|
|
71
|
+
export const lines = (
|
|
72
|
+
command: Command.Command,
|
|
73
|
+
encoding = "utf-8"
|
|
74
|
+
): Effect.Effect<CommandExecutor.CommandExecutor, PlatformError, ReadonlyArray<string>> =>
|
|
75
|
+
Effect.flatMap(commandExecutor.CommandExecutor, (executor) => executor.lines(command, encoding))
|
|
76
|
+
|
|
77
|
+
/** @internal */
|
|
78
|
+
export const make = (command: string, ...args: Array<string>): Command.Command => ({
|
|
79
|
+
[CommandTypeId]: CommandTypeId,
|
|
80
|
+
_tag: "StandardCommand",
|
|
81
|
+
command,
|
|
82
|
+
args,
|
|
83
|
+
env: HashMap.empty(),
|
|
84
|
+
cwd: Option.none(),
|
|
85
|
+
// The initial process input here does not matter, we just want the child
|
|
86
|
+
// process to default to `"pipe"` for the stdin stream.
|
|
87
|
+
stdin: Option.some(Stream.empty),
|
|
88
|
+
stdout: "pipe",
|
|
89
|
+
stderr: "pipe",
|
|
90
|
+
gid: Option.none(),
|
|
91
|
+
uid: Option.none()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
/** @internal */
|
|
95
|
+
export const pipeTo = dual<
|
|
96
|
+
(into: Command.Command) => (self: Command.Command) => Command.Command,
|
|
97
|
+
(self: Command.Command, into: Command.Command) => Command.Command
|
|
98
|
+
>(2, (self, into) => ({
|
|
99
|
+
[CommandTypeId]: CommandTypeId,
|
|
100
|
+
_tag: "PipedCommand",
|
|
101
|
+
left: self,
|
|
102
|
+
right: into
|
|
103
|
+
}))
|
|
104
|
+
|
|
105
|
+
/** @internal */
|
|
106
|
+
export const stderr: {
|
|
107
|
+
(stderr: Command.Command.Output): (self: Command.Command) => Command.Command
|
|
108
|
+
(self: Command.Command, stderr: Command.Command.Output): Command.Command
|
|
109
|
+
} = dual<
|
|
110
|
+
(stderr: Command.Command.Output) => (self: Command.Command) => Command.Command,
|
|
111
|
+
(self: Command.Command, stderr: Command.Command.Output) => Command.Command
|
|
112
|
+
>(2, (self, output) => {
|
|
113
|
+
switch (self._tag) {
|
|
114
|
+
case "StandardCommand": {
|
|
115
|
+
return { ...self, stderr: output }
|
|
116
|
+
}
|
|
117
|
+
// For piped commands it only makes sense to provide `stderr` for the
|
|
118
|
+
// right-most command as the rest will be piped in.
|
|
119
|
+
case "PipedCommand": {
|
|
120
|
+
return { ...self, right: stderr(self.right, output) }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
/** @internal */
|
|
126
|
+
export const stdin: {
|
|
127
|
+
(stdin: Command.Command.Input): (self: Command.Command) => Command.Command
|
|
128
|
+
(self: Command.Command, stdin: Command.Command.Input): Command.Command
|
|
129
|
+
} = dual<
|
|
130
|
+
(stdin: Command.Command.Input) => (self: Command.Command) => Command.Command,
|
|
131
|
+
(self: Command.Command, stdin: Command.Command.Input) => Command.Command
|
|
132
|
+
>(2, (self, input) => {
|
|
133
|
+
switch (self._tag) {
|
|
134
|
+
case "StandardCommand": {
|
|
135
|
+
return { ...self, stdin: Option.some(input) }
|
|
136
|
+
}
|
|
137
|
+
// For piped commands it only makes sense to provide `stdin` for the
|
|
138
|
+
// left-most command as the rest will be piped in.
|
|
139
|
+
case "PipedCommand": {
|
|
140
|
+
return { ...self, left: stdin(self.left, input) }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
/** @internal */
|
|
146
|
+
export const stdout: {
|
|
147
|
+
(stdout: Command.Command.Output): (self: Command.Command) => Command.Command
|
|
148
|
+
(self: Command.Command, stdout: Command.Command.Output): Command.Command
|
|
149
|
+
} = dual<
|
|
150
|
+
(stdout: Command.Command.Output) => (self: Command.Command) => Command.Command,
|
|
151
|
+
(self: Command.Command, stdout: Command.Command.Output) => Command.Command
|
|
152
|
+
>(2, (self, output) => {
|
|
153
|
+
switch (self._tag) {
|
|
154
|
+
case "StandardCommand": {
|
|
155
|
+
return { ...self, stdout: output }
|
|
156
|
+
}
|
|
157
|
+
// For piped commands it only makes sense to provide `stderr` for the
|
|
158
|
+
// right-most command as the rest will be piped in.
|
|
159
|
+
case "PipedCommand": {
|
|
160
|
+
return { ...self, right: stdout(self.right, output) }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
/** @internal */
|
|
166
|
+
export const start = (
|
|
167
|
+
command: Command.Command
|
|
168
|
+
): Effect.Effect<CommandExecutor.CommandExecutor, PlatformError, CommandExecutor.Process> =>
|
|
169
|
+
Effect.flatMap(commandExecutor.CommandExecutor, (executor) => executor.start(command))
|
|
170
|
+
|
|
171
|
+
/** @internal */
|
|
172
|
+
export const stream = (
|
|
173
|
+
command: Command.Command
|
|
174
|
+
): Stream.Stream<CommandExecutor.CommandExecutor, PlatformError, Uint8Array> =>
|
|
175
|
+
Stream.flatMap(commandExecutor.CommandExecutor, (process) => process.stream(command))
|
|
176
|
+
|
|
177
|
+
/** @internal */
|
|
178
|
+
export const streamLines = (
|
|
179
|
+
command: Command.Command
|
|
180
|
+
): Stream.Stream<CommandExecutor.CommandExecutor, PlatformError, string> =>
|
|
181
|
+
Stream.flatMap(commandExecutor.CommandExecutor, (process) => process.streamLines(command))
|
|
182
|
+
|
|
183
|
+
/** @internal */
|
|
184
|
+
export const string = dual<
|
|
185
|
+
(
|
|
186
|
+
encoding?: string
|
|
187
|
+
) => (command: Command.Command) => Effect.Effect<CommandExecutor.CommandExecutor, PlatformError, string>,
|
|
188
|
+
(command: Command.Command, encoding?: string) => Effect.Effect<CommandExecutor.CommandExecutor, PlatformError, string>
|
|
189
|
+
>(
|
|
190
|
+
(args) => isCommand(args[0]),
|
|
191
|
+
(command, encoding) =>
|
|
192
|
+
Effect.flatMap(commandExecutor.CommandExecutor, (executor) => executor.string(command, encoding))
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
/** @internal */
|
|
196
|
+
export const workingDirectory: {
|
|
197
|
+
(cwd: string): (self: Command.Command) => Command.Command
|
|
198
|
+
(self: Command.Command, cwd: string): Command.Command
|
|
199
|
+
} = dual<
|
|
200
|
+
(cwd: string) => (self: Command.Command) => Command.Command,
|
|
201
|
+
(self: Command.Command, cwd: string) => Command.Command
|
|
202
|
+
>(2, (self, cwd) => {
|
|
203
|
+
switch (self._tag) {
|
|
204
|
+
case "StandardCommand": {
|
|
205
|
+
return { ...self, cwd: Option.some(cwd) }
|
|
206
|
+
}
|
|
207
|
+
case "PipedCommand": {
|
|
208
|
+
return pipeTo(workingDirectory(self.left, cwd), workingDirectory(self.right, cwd))
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as Brand from "@effect/data/Brand"
|
|
2
|
+
import * as Chunk from "@effect/data/Chunk"
|
|
3
|
+
import { Tag } from "@effect/data/Context"
|
|
4
|
+
import { pipe } from "@effect/data/Function"
|
|
5
|
+
import * as Effect from "@effect/io/Effect"
|
|
6
|
+
import type * as _CommandExecutor from "@effect/platform/CommandExecutor"
|
|
7
|
+
import * as Sink from "@effect/stream/Sink"
|
|
8
|
+
import * as Stream from "@effect/stream/Stream"
|
|
9
|
+
|
|
10
|
+
/** @internal */
|
|
11
|
+
export const ProcessTypeId: _CommandExecutor.ProcessTypeId = Symbol.for(
|
|
12
|
+
"@effect/platform/Process"
|
|
13
|
+
) as _CommandExecutor.ProcessTypeId
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
export const ExitCode = Brand.nominal<_CommandExecutor.ExitCode>()
|
|
17
|
+
|
|
18
|
+
/** @internal */
|
|
19
|
+
export const ProcessId = Brand.nominal<_CommandExecutor.Process.Id>()
|
|
20
|
+
|
|
21
|
+
/** @internal */
|
|
22
|
+
export const CommandExecutor = Tag<_CommandExecutor.CommandExecutor>()
|
|
23
|
+
|
|
24
|
+
/** @internal */
|
|
25
|
+
export const makeExecutor = (start: _CommandExecutor.CommandExecutor["start"]): _CommandExecutor.CommandExecutor => {
|
|
26
|
+
const stream: _CommandExecutor.CommandExecutor["stream"] = (command) =>
|
|
27
|
+
pipe(
|
|
28
|
+
Stream.fromEffect(start(command)),
|
|
29
|
+
Stream.flatMap((process) => process.stdout)
|
|
30
|
+
)
|
|
31
|
+
const streamLines: _CommandExecutor.CommandExecutor["streamLines"] = (command, encoding) => {
|
|
32
|
+
const decoder = new TextDecoder(encoding)
|
|
33
|
+
return Stream.splitLines(
|
|
34
|
+
Stream.mapChunks(stream(command), Chunk.map((bytes) => decoder.decode(bytes)))
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
start,
|
|
39
|
+
exitCode: (command) => Effect.flatMap(start(command), (process) => process.exitCode),
|
|
40
|
+
stream,
|
|
41
|
+
string: (command, encoding = "utf-8") => {
|
|
42
|
+
const decoder = new TextDecoder(encoding)
|
|
43
|
+
return pipe(
|
|
44
|
+
start(command),
|
|
45
|
+
Effect.flatMap((process) => Stream.run(process.stdout, collectUint8Array)),
|
|
46
|
+
Effect.map((bytes) => decoder.decode(bytes))
|
|
47
|
+
)
|
|
48
|
+
},
|
|
49
|
+
lines: (command, encoding = "utf-8") => {
|
|
50
|
+
return pipe(
|
|
51
|
+
streamLines(command, encoding),
|
|
52
|
+
Stream.runCollect,
|
|
53
|
+
Effect.map(Chunk.toReadonlyArray)
|
|
54
|
+
)
|
|
55
|
+
},
|
|
56
|
+
streamLines
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const collectUint8Array: Sink.Sink<never, never, Uint8Array, never, Uint8Array> = Sink.foldLeftChunks(
|
|
61
|
+
new Uint8Array(),
|
|
62
|
+
(bytes, chunk: Chunk.Chunk<Uint8Array>) =>
|
|
63
|
+
Chunk.reduce(chunk, bytes, (acc, curr) => {
|
|
64
|
+
const newArray = new Uint8Array(acc.length + curr.length)
|
|
65
|
+
newArray.set(acc)
|
|
66
|
+
newArray.set(curr, acc.length)
|
|
67
|
+
return newArray
|
|
68
|
+
})
|
|
69
|
+
)
|
|
@@ -41,7 +41,7 @@ const stream = (file: File, {
|
|
|
41
41
|
Stream.bufferChunks(
|
|
42
42
|
Stream.unfoldEffect(offset, (position) => {
|
|
43
43
|
if (bytesToRead !== undefined && bytesToRead <= position - offset) {
|
|
44
|
-
return Effect.
|
|
44
|
+
return Effect.succeed(Option.none())
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const toRead = bytesToRead !== undefined && bytesToRead - (position - offset) < chunkSize
|