@effect/platform-node 0.1.0 → 0.2.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 +111 -0
- package/Command.d.ts.map +1 -0
- package/Command.js +103 -0
- package/Command.js.map +1 -0
- package/CommandExecutor.d.ts +44 -0
- package/CommandExecutor.d.ts.map +1 -0
- package/CommandExecutor.js +23 -0
- package/CommandExecutor.js.map +1 -0
- package/NodeContext.d.ts +16 -0
- package/NodeContext.d.ts.map +1 -0
- package/NodeContext.js +24 -0
- package/NodeContext.js.map +1 -0
- package/Stream.d.ts +3 -2
- package/Stream.d.ts.map +1 -1
- package/Stream.js.map +1 -1
- package/internal/commandExecutor.d.ts +2 -0
- package/internal/commandExecutor.d.ts.map +1 -0
- package/internal/commandExecutor.js +145 -0
- package/internal/commandExecutor.js.map +1 -0
- package/internal/error.d.ts +2 -0
- package/internal/error.d.ts.map +1 -0
- package/internal/error.js +44 -0
- package/internal/error.js.map +1 -0
- package/internal/fileSystem.js +28 -62
- package/internal/fileSystem.js.map +1 -1
- package/internal/path.js +16 -10
- package/internal/path.js.map +1 -1
- package/internal/runtime.js +2 -2
- package/internal/runtime.js.map +1 -1
- package/internal/sink.js +3 -3
- package/internal/sink.js.map +1 -1
- package/internal/stream.js +8 -5
- package/internal/stream.js.map +1 -1
- package/mjs/Command.mjs +85 -0
- package/mjs/Command.mjs.map +1 -0
- package/mjs/CommandExecutor.mjs +13 -0
- package/mjs/CommandExecutor.mjs.map +1 -0
- package/mjs/NodeContext.mjs +15 -0
- package/mjs/NodeContext.mjs.map +1 -0
- package/mjs/Stream.mjs.map +1 -1
- package/mjs/internal/commandExecutor.mjs +136 -0
- package/mjs/internal/commandExecutor.mjs.map +1 -0
- package/mjs/internal/error.mjs +37 -0
- package/mjs/internal/error.mjs.map +1 -0
- package/mjs/internal/fileSystem.mjs +28 -62
- package/mjs/internal/fileSystem.mjs.map +1 -1
- package/mjs/internal/path.mjs +16 -10
- package/mjs/internal/path.mjs.map +1 -1
- package/mjs/internal/runtime.mjs +2 -2
- package/mjs/internal/runtime.mjs.map +1 -1
- package/mjs/internal/sink.mjs +3 -3
- package/mjs/internal/sink.mjs.map +1 -1
- package/mjs/internal/stream.mjs +8 -5
- package/mjs/internal/stream.mjs.map +1 -1
- package/package.json +5 -5
- package/src/Command.ts +114 -0
- package/src/CommandExecutor.ts +49 -0
- package/src/NodeContext.ts +26 -0
- package/src/Stream.ts +3 -2
- package/src/internal/commandExecutor.ts +202 -0
- package/src/internal/error.ts +51 -0
- package/src/internal/fileSystem.ts +28 -76
- package/src/internal/path.ts +8 -8
- package/src/internal/runtime.ts +2 -2
- package/src/internal/sink.ts +3 -3
- package/src/internal/stream.ts +11 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sink.mjs","names":["Effect","Sink","fromWritable","evaluate","onError","encoding","endOnClose","makeSinkWithRelease","makeSink","stream","unwrap","map","forEach","write","sync","unwrapScoped","acquireRelease","endWritable","async","resume","closed","unit","end","_","cb","err","fail"],"sources":["../../src/internal/sink.ts"],"sourcesContent":[null],"mappings":"AAEA,OAAO,KAAKA,MAAM,MAAM,mBAAmB;AAE3C,OAAO,KAAKC,IAAI,MAAM,qBAAqB;AAG3C;AACA,OAAO,MAAMC,YAAY,GAAGA,CAC1BC,QAA2B,EAC3BC,OAA8B,EAC9B;EAAEC,QAAQ;EAAEC,UAAU,GAAG;AAAI,IAA0B,EAAE,KAEzDA,UAAU,GACRC,mBAAmB,CAAOJ,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,CAAC,GACtDG,QAAQ,CAAOL,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,CAAC;AAE/C,MAAMG,QAAQ,GAAGA,CAAOC,MAAyB,EAAEL,OAA8B,EAAEC,QAAyB,KAIxGJ,IAAI,CAACS,MAAM,CADXV,MAAM,CAACW,GAAG,CAAEF,MAAM,IAAKR,IAAI,CAACW,OAAO,CAACC,KAAK,CAAOJ,MAAM,EAAEL,OAAO,EAAEC,QAAQ,CAAC,CAAC,CAAC,CAD5EL,MAAM,CAACc,IAAI,CAACL,MAAM,CAAC,EAGpB;AAEH,MAAMF,mBAAmB,GAAGA,CAC1BE,MAAyB,EACzBL,OAA8B,EAC9BC,QAAyB,KAKvBJ,IAAI,CAACc,YAAY,CADjBf,MAAM,CAACW,GAAG,CAAEF,MAAM,IAAKR,IAAI,CAACW,OAAO,CAACC,KAAK,CAAOJ,MAAM,EAAEL,OAAO,EAAEC,QAAQ,CAAC,CAAC,CAAC,CAD5EL,MAAM,CAACgB,cAAc,CAAChB,MAAM,CAACc,IAAI,CAACL,MAAM,CAAC,EAAEQ,WAAW,CAAC,EAGxD;AAEH,MAAMA,WAAW,GAAIR,MAAgB,IACnCT,MAAM,CAACkB,KAAK,CAAsBC,MAAM,IAAI;EAC1C,IAAIV,MAAM,CAACW,MAAM,EAAE;IACjBD,MAAM,CAACnB,MAAM,CAACqB,IAAI,
|
|
1
|
+
{"version":3,"file":"sink.mjs","names":["Effect","Sink","fromWritable","evaluate","onError","encoding","endOnClose","makeSinkWithRelease","makeSink","stream","unwrap","map","forEach","write","sync","unwrapScoped","acquireRelease","endWritable","async","resume","closed","unit","end","_","cb","err","fail"],"sources":["../../src/internal/sink.ts"],"sourcesContent":[null],"mappings":"AAEA,OAAO,KAAKA,MAAM,MAAM,mBAAmB;AAE3C,OAAO,KAAKC,IAAI,MAAM,qBAAqB;AAG3C;AACA,OAAO,MAAMC,YAAY,GAAGA,CAC1BC,QAA2B,EAC3BC,OAA8B,EAC9B;EAAEC,QAAQ;EAAEC,UAAU,GAAG;AAAI,IAA0B,EAAE,KAEzDA,UAAU,GACRC,mBAAmB,CAAOJ,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,CAAC,GACtDG,QAAQ,CAAOL,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,CAAC;AAE/C,MAAMG,QAAQ,GAAGA,CAAOC,MAAyB,EAAEL,OAA8B,EAAEC,QAAyB,KAIxGJ,IAAI,CAACS,MAAM,CADXV,MAAM,CAACW,GAAG,CAAEF,MAAM,IAAKR,IAAI,CAACW,OAAO,CAACC,KAAK,CAAOJ,MAAM,EAAEL,OAAO,EAAEC,QAAQ,CAAC,CAAC,CAAC,CAD5EL,MAAM,CAACc,IAAI,CAACL,MAAM,CAAC,EAGpB;AAEH,MAAMF,mBAAmB,GAAGA,CAC1BE,MAAyB,EACzBL,OAA8B,EAC9BC,QAAyB,KAKvBJ,IAAI,CAACc,YAAY,CADjBf,MAAM,CAACW,GAAG,CAAEF,MAAM,IAAKR,IAAI,CAACW,OAAO,CAACC,KAAK,CAAOJ,MAAM,EAAEL,OAAO,EAAEC,QAAQ,CAAC,CAAC,CAAC,CAD5EL,MAAM,CAACgB,cAAc,CAAChB,MAAM,CAACc,IAAI,CAACL,MAAM,CAAC,EAAEQ,WAAW,CAAC,EAGxD;AAEH,MAAMA,WAAW,GAAIR,MAAgB,IACnCT,MAAM,CAACkB,KAAK,CAAsBC,MAAM,IAAI;EAC1C,IAAIV,MAAM,CAACW,MAAM,EAAE;IACjBD,MAAM,CAACnB,MAAM,CAACqB,IAAI,CAAC;IACnB;;EAGFZ,MAAM,CAACa,GAAG,CAAC,MAAMH,MAAM,CAACnB,MAAM,CAACqB,IAAI,CAAC,CAAC;AACvC,CAAC,CAAC;AAEJ,MAAMR,KAAK,GAAGA,CAAOJ,MAAgB,EAAEL,OAA8B,EAAEC,QAAyB,KAC7FkB,CAAI,IACHvB,MAAM,CAACkB,KAAK,CAAkBC,MAAM,IAAI;EACtC,MAAMK,EAAE,GAAIC,GAAkB,IAAI;IAChC,IAAIA,GAAG,EAAE;MACPN,MAAM,CAACnB,MAAM,CAAC0B,IAAI,CAACtB,OAAO,CAACqB,GAAG,CAAC,CAAC,CAAC;KAClC,MAAM;MACLN,MAAM,CAACnB,MAAM,CAACqB,IAAI,CAAC;;EAEvB,CAAC;EAED,IAAIhB,QAAQ,EAAE;IACZI,MAAM,CAACI,KAAK,CAACU,CAAC,EAAElB,QAAQ,EAAEmB,EAAE,CAAC;GAC9B,MAAM;IACLf,MAAM,CAACI,KAAK,CAACU,CAAC,EAAEC,EAAE,CAAC;;AAEvB,CAAC,CAAC"}
|
package/mjs/internal/stream.mjs
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import * as Option from "@effect/data/Option";
|
|
2
2
|
import * as Effect from "@effect/io/Effect";
|
|
3
|
-
import { Size } from "@effect/platform/FileSystem";
|
|
4
3
|
import * as Stream from "@effect/stream/Stream";
|
|
5
|
-
const DEFAULT_CHUNK_SIZE = /*#__PURE__*/Size(64 * 1024);
|
|
6
4
|
/** @internal */
|
|
7
5
|
export const fromReadable = (evaluate, onError, {
|
|
8
|
-
chunkSize =
|
|
6
|
+
chunkSize = Option.none()
|
|
9
7
|
} = {}) => Stream.flatMap(_ => Stream.repeatEffectOption(readChunk(_, chunkSize)))(Stream.unwrapScoped(Effect.map(stream => Stream.async(emit => {
|
|
10
8
|
stream.once("error", err => {
|
|
11
9
|
emit.fail(onError(err));
|
|
12
10
|
});
|
|
13
|
-
|
|
11
|
+
// The 'close' event is emitted after a process has ended and the stdio
|
|
12
|
+
// streams of a child process have been closed. This is distinct from
|
|
13
|
+
// the 'exit' event, since multiple processes might share the same
|
|
14
|
+
// stdio streams. The 'close' event will always emit after 'exit' was
|
|
15
|
+
// already emitted, or 'error' if the child failed to spawn.
|
|
16
|
+
stream.once("close", () => {
|
|
14
17
|
emit.end();
|
|
15
18
|
});
|
|
16
19
|
stream.on("readable", () => {
|
|
@@ -25,5 +28,5 @@ export const fromReadable = (evaluate, onError, {
|
|
|
25
28
|
stream.destroy();
|
|
26
29
|
}
|
|
27
30
|
})))));
|
|
28
|
-
const readChunk = (stream, size) => Effect.flatMap(
|
|
31
|
+
const readChunk = (stream, size) => Effect.flatMap(_ => _ ? Effect.succeed(_) : Effect.fail(Option.none()))(Effect.sync(() => size._tag === "Some" ? stream.read(Number(size)) : stream.read()));
|
|
29
32
|
//# sourceMappingURL=stream.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.mjs","names":["Option","Effect","
|
|
1
|
+
{"version":3,"file":"stream.mjs","names":["Option","Effect","Stream","fromReadable","evaluate","onError","chunkSize","none","flatMap","_","repeatEffectOption","readChunk","unwrapScoped","map","stream","async","emit","once","err","fail","end","on","single","readable","acquireRelease","sync","removeAllListeners","closed","destroy","size","succeed","_tag","read","Number"],"sources":["../../src/internal/stream.ts"],"sourcesContent":[null],"mappings":"AAEA,OAAO,KAAKA,MAAM,MAAM,qBAAqB;AAC7C,OAAO,KAAKC,MAAM,MAAM,mBAAmB;AAG3C,OAAO,KAAKC,MAAM,MAAM,uBAAuB;AAG/C;AACA,OAAO,MAAMC,YAAY,GAAGA,CAC1BC,QAA2B,EAC3BC,OAA8B,EAC9B;EAAEC,SAAS,GAAGN,MAAM,CAACO,IAAI;AAAE,IAA0B,EAAE,KAoCrDL,MAAM,CAACM,OAAO,CAAEC,CAAC,IAAKP,MAAM,CAACQ,kBAAkB,CAACC,SAAS,CAAIF,CAAC,EAAEH,SAAS,CAAC,CAAC,CAAC,CAD5EJ,MAAM,CAACU,YAAY,CAxBnBX,MAAM,CAACY,GAAG,CAAEC,MAAM,IAChBZ,MAAM,CAACa,KAAK,CAAsBC,IAAI,IAAI;EACxCF,MAAM,CAACG,IAAI,CAAC,OAAO,EAAGC,GAAG,IAAI;IAC3BF,IAAI,CAACG,IAAI,CAACd,OAAO,CAACa,GAAG,CAAC,CAAC;EACzB,CAAC,CAAC;EAEF;EACA;EACA;EACA;EACA;EACAJ,MAAM,CAACG,IAAI,CAAC,OAAO,EAAE,MAAK;IACxBD,IAAI,CAACI,GAAG,EAAE;EACZ,CAAC,CAAC;EAEFN,MAAM,CAACO,EAAE,CAAC,UAAU,EAAE,MAAK;IACzBL,IAAI,CAACM,MAAM,CAACR,MAAM,CAAC;EACrB,CAAC,CAAC;EAEF,IAAIA,MAAM,CAACS,QAAQ,EAAE;IACnBP,IAAI,CAACM,MAAM,CAACR,MAAM,CAAC;;AAEvB,CAAC,EAAE,CAAC,CAAC,CACN,CA/BDb,MAAM,CAACuB,cAAc,CAACvB,MAAM,CAACwB,IAAI,CAACrB,QAAQ,CAAC,EAAGU,MAAM,IAClDb,MAAM,CAACwB,IAAI,CAAC,MAAK;EACfX,MAAM,CAACY,kBAAkB,EAAE;EAE3B,IAAI,CAACZ,MAAM,CAACa,MAAM,EAAE;IAClBb,MAAM,CAACc,OAAO,EAAE;;AAEpB,CAAC,CAAC,CAAC,GA2BN;AAEH,MAAMjB,SAAS,GAAGA,CAChBG,MAAgB,EAChBe,IAAyB,KAIvB5B,MAAM,CAACO,OAAO,CAAEC,CAAC,IAAMA,CAAC,GAAGR,MAAM,CAAC6B,OAAO,CAACrB,CAAC,CAAC,GAAGR,MAAM,CAACkB,IAAI,CAACnB,MAAM,CAACO,IAAI,EAAE,CAAE,CAAC,CAD3EN,MAAM,CAACwB,IAAI,CAAC,MAAOI,IAAI,CAACE,IAAI,KAAK,MAAM,GAAGjB,MAAM,CAACkB,IAAI,CAACC,MAAM,CAACJ,IAAI,CAAC,CAAC,GAAGf,MAAM,CAACkB,IAAI,EAAe,CAAC,CAElG"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect/platform-node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/effect-ts/platform.git"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@effect/data": "^0.
|
|
11
|
-
"@effect/io": "^0.
|
|
12
|
-
"@effect/stream": "^0.
|
|
13
|
-
"@effect/platform": "^0.
|
|
10
|
+
"@effect/data": "^0.13.5",
|
|
11
|
+
"@effect/io": "^0.31.3",
|
|
12
|
+
"@effect/stream": "^0.25.1",
|
|
13
|
+
"@effect/platform": "^0.2.0"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
package/src/Command.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
/**
|
|
7
|
+
* @since 1.0.0
|
|
8
|
+
* @category model
|
|
9
|
+
*/
|
|
10
|
+
Command,
|
|
11
|
+
/**
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
* @category model
|
|
14
|
+
*/
|
|
15
|
+
CommandInput,
|
|
16
|
+
/**
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category model
|
|
19
|
+
*/
|
|
20
|
+
CommandOutput,
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category model
|
|
24
|
+
*/
|
|
25
|
+
PipedCommand,
|
|
26
|
+
/**
|
|
27
|
+
* @since 1.0.0
|
|
28
|
+
* @category model
|
|
29
|
+
*/
|
|
30
|
+
StandardCommand
|
|
31
|
+
} from "@effect/platform/Command"
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
/**
|
|
35
|
+
* @since 1.0.0
|
|
36
|
+
* @category combinators
|
|
37
|
+
*/
|
|
38
|
+
env,
|
|
39
|
+
/**
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
* @category execution
|
|
42
|
+
*/
|
|
43
|
+
exitCode,
|
|
44
|
+
/**
|
|
45
|
+
* @since 1.0.0
|
|
46
|
+
* @category combinators
|
|
47
|
+
*/
|
|
48
|
+
feed,
|
|
49
|
+
/**
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
* @category combinators
|
|
52
|
+
*/
|
|
53
|
+
flatten,
|
|
54
|
+
/**
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
* @category refinements
|
|
57
|
+
*/
|
|
58
|
+
isCommand,
|
|
59
|
+
/**
|
|
60
|
+
* @since 1.0.0
|
|
61
|
+
* @category execution
|
|
62
|
+
*/
|
|
63
|
+
lines,
|
|
64
|
+
/**
|
|
65
|
+
* @since 1.0.0
|
|
66
|
+
* @category constructors
|
|
67
|
+
*/
|
|
68
|
+
make,
|
|
69
|
+
/**
|
|
70
|
+
* @since 1.0.0
|
|
71
|
+
* @category combinators
|
|
72
|
+
*/
|
|
73
|
+
pipeTo,
|
|
74
|
+
/**
|
|
75
|
+
* @since 1.0.0
|
|
76
|
+
* @category execution
|
|
77
|
+
*/
|
|
78
|
+
start,
|
|
79
|
+
/**
|
|
80
|
+
* @since 1.0.0
|
|
81
|
+
* @category combinators
|
|
82
|
+
*/
|
|
83
|
+
stderr,
|
|
84
|
+
/**
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category combinators
|
|
87
|
+
*/
|
|
88
|
+
stdin,
|
|
89
|
+
/**
|
|
90
|
+
* @since 1.0.0
|
|
91
|
+
* @category combinators
|
|
92
|
+
*/
|
|
93
|
+
stdout,
|
|
94
|
+
/**
|
|
95
|
+
* @since 1.0.0
|
|
96
|
+
* @category execution
|
|
97
|
+
*/
|
|
98
|
+
stream,
|
|
99
|
+
/**
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category execution
|
|
102
|
+
*/
|
|
103
|
+
streamLines,
|
|
104
|
+
/**
|
|
105
|
+
* @since 1.0.0
|
|
106
|
+
* @category execution
|
|
107
|
+
*/
|
|
108
|
+
string,
|
|
109
|
+
/**
|
|
110
|
+
* @since 1.0.0
|
|
111
|
+
* @category combinators
|
|
112
|
+
*/
|
|
113
|
+
workingDirectory
|
|
114
|
+
} from "@effect/platform/Command"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Layer } from "@effect/io/Layer"
|
|
5
|
+
import * as internal from "@effect/platform-node/internal/commandExecutor"
|
|
6
|
+
import type { CommandExecutor } from "@effect/platform/CommandExecutor"
|
|
7
|
+
import type { FileSystem } from "@effect/platform/FileSystem"
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category models
|
|
13
|
+
*/
|
|
14
|
+
ExitCode,
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category models
|
|
18
|
+
*/
|
|
19
|
+
Process,
|
|
20
|
+
/**
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
* @category models
|
|
23
|
+
*/
|
|
24
|
+
ProcessId,
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
* @category symbols
|
|
28
|
+
*/
|
|
29
|
+
ProcessTypeId,
|
|
30
|
+
/**
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @category models
|
|
33
|
+
*/
|
|
34
|
+
Signal
|
|
35
|
+
} from "@effect/platform/CommandExecutor"
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
/**
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @category tag
|
|
41
|
+
*/
|
|
42
|
+
CommandExecutor
|
|
43
|
+
} from "@effect/platform/CommandExecutor"
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @since 1.0.0
|
|
47
|
+
* @category layer
|
|
48
|
+
*/
|
|
49
|
+
export const layer: Layer<FileSystem, never, CommandExecutor> = internal.layer
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import { pipe } from "@effect/data/Function"
|
|
5
|
+
import * as Layer from "@effect/io/Layer"
|
|
6
|
+
import * as CommandExecutor from "@effect/platform-node/CommandExecutor"
|
|
7
|
+
import * as Console from "@effect/platform-node/Console"
|
|
8
|
+
import * as FileSystem from "@effect/platform-node/FileSystem"
|
|
9
|
+
import * as Path from "@effect/platform-node/Path"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
* @category models
|
|
14
|
+
*/
|
|
15
|
+
export type NodeContext = Console.Console | CommandExecutor.CommandExecutor | FileSystem.FileSystem | Path.Path
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
* @category layer
|
|
20
|
+
*/
|
|
21
|
+
export const layer: Layer.Layer<never, never, NodeContext> = pipe(
|
|
22
|
+
Console.layer,
|
|
23
|
+
Layer.merge(FileSystem.layer),
|
|
24
|
+
Layer.merge(Path.layer),
|
|
25
|
+
Layer.merge(Layer.provideMerge(FileSystem.layer, CommandExecutor.layer))
|
|
26
|
+
)
|
package/src/Stream.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
import type { LazyArg } from "@effect/data/Function"
|
|
5
|
+
import type { Option } from "@effect/data/Option"
|
|
5
6
|
import * as internal from "@effect/platform-node/internal/stream"
|
|
6
7
|
import type { Size } from "@effect/platform/FileSystem"
|
|
7
8
|
import type { Stream } from "@effect/stream/Stream"
|
|
@@ -12,8 +13,8 @@ import type { Readable } from "stream"
|
|
|
12
13
|
* @since 1.0.0
|
|
13
14
|
*/
|
|
14
15
|
export interface FromReadableOptions {
|
|
15
|
-
/** Defaults to
|
|
16
|
-
readonly chunkSize?: Size
|
|
16
|
+
/** Defaults to None, which lets Node.js decide the chunk size */
|
|
17
|
+
readonly chunkSize?: Option<Size>
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { constUndefined, pipe } from "@effect/data/Function"
|
|
2
|
+
import * as Option from "@effect/data/Option"
|
|
3
|
+
import * as Effect from "@effect/io/Effect"
|
|
4
|
+
import * as Layer from "@effect/io/Layer"
|
|
5
|
+
import { handleErrnoException } from "@effect/platform-node/internal/error"
|
|
6
|
+
import { fromWritable } from "@effect/platform-node/internal/sink"
|
|
7
|
+
import { fromReadable } from "@effect/platform-node/internal/stream"
|
|
8
|
+
import * as Command from "@effect/platform/Command"
|
|
9
|
+
import * as CommandExecutor from "@effect/platform/CommandExecutor"
|
|
10
|
+
import type * as Error from "@effect/platform/Error"
|
|
11
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
12
|
+
import * as Sink from "@effect/stream/Sink"
|
|
13
|
+
import * as Stream from "@effect/stream/Stream"
|
|
14
|
+
import * as ChildProcess from "node:child_process"
|
|
15
|
+
|
|
16
|
+
const inputToStdioOption = (stdin: Option.Option<Command.Command.Input>): "pipe" | "inherit" =>
|
|
17
|
+
Option.match(stdin, { onNone: () => "inherit", onSome: () => "pipe" })
|
|
18
|
+
|
|
19
|
+
const outputToStdioOption = (output: Command.Command.Output): "pipe" | "inherit" =>
|
|
20
|
+
typeof output === "string" ? output : "pipe"
|
|
21
|
+
|
|
22
|
+
const toError = (err: unknown): Error => err instanceof globalThis.Error ? err : new globalThis.Error(String(err))
|
|
23
|
+
|
|
24
|
+
const toPlatformError = (
|
|
25
|
+
method: string,
|
|
26
|
+
error: NodeJS.ErrnoException,
|
|
27
|
+
command: Command.Command
|
|
28
|
+
): Error.PlatformError => {
|
|
29
|
+
const flattened = Command.flatten(command).reduce((acc, curr) => {
|
|
30
|
+
const command = `${curr.command} ${curr.args.join(" ")}`
|
|
31
|
+
return acc.length === 0 ? command : `${acc} | ${command}`
|
|
32
|
+
}, "")
|
|
33
|
+
return handleErrnoException("Command", method)(error, [flattened])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const runCommand = (fileSystem: FileSystem.FileSystem) =>
|
|
37
|
+
(command: Command.Command): Effect.Effect<never, Error.PlatformError, CommandExecutor.Process> => {
|
|
38
|
+
switch (command._tag) {
|
|
39
|
+
case "StandardCommand": {
|
|
40
|
+
return pipe(
|
|
41
|
+
// Validate that the directory is accessible
|
|
42
|
+
Option.match(command.cwd, {
|
|
43
|
+
onNone: () => Effect.unit,
|
|
44
|
+
onSome: (dir) => fileSystem.access(dir)
|
|
45
|
+
}),
|
|
46
|
+
Effect.zipRight(Effect.sync(() => globalThis.process.env)),
|
|
47
|
+
Effect.flatMap((env) =>
|
|
48
|
+
Effect.asyncInterrupt<never, Error.PlatformError, CommandExecutor.Process>((resume) => {
|
|
49
|
+
const handle = ChildProcess.spawn(command.command, command.args, {
|
|
50
|
+
stdio: [
|
|
51
|
+
inputToStdioOption(command.stdin),
|
|
52
|
+
outputToStdioOption(command.stdout),
|
|
53
|
+
outputToStdioOption(command.stderr)
|
|
54
|
+
],
|
|
55
|
+
cwd: Option.getOrElse(command.cwd, constUndefined),
|
|
56
|
+
env: { ...env, ...Object.fromEntries(command.env) }
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// If starting the process throws an error, make sure to capture it
|
|
60
|
+
handle.on("error", (err) => {
|
|
61
|
+
handle.kill("SIGKILL")
|
|
62
|
+
resume(Effect.fail(toPlatformError("spawn", err, command)))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// If the process is assigned a process identifier, then we know it
|
|
66
|
+
// was spawned successfully
|
|
67
|
+
if (handle.pid) {
|
|
68
|
+
let stdin: Sink.Sink<never, Error.PlatformError, unknown, never, void> = Sink.drain()
|
|
69
|
+
|
|
70
|
+
if (handle.stdin !== null) {
|
|
71
|
+
stdin = fromWritable(
|
|
72
|
+
() => handle.stdin!,
|
|
73
|
+
(err) => toPlatformError("toWritable", toError(err), command)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const exitCode: CommandExecutor.Process["exitCode"] = Effect.asyncInterrupt((resume) => {
|
|
78
|
+
handle.on("exit", (code, signal) => {
|
|
79
|
+
if (code !== null) {
|
|
80
|
+
resume(Effect.succeed(CommandExecutor.ExitCode(code)))
|
|
81
|
+
} else {
|
|
82
|
+
// If code is `null`, then `signal` must be defined. See the NodeJS
|
|
83
|
+
// documentation for the `"exit"` event on a `child_process`.
|
|
84
|
+
// https://nodejs.org/api/child_process.html#child_process_event_exit
|
|
85
|
+
resume(
|
|
86
|
+
Effect.fail(
|
|
87
|
+
toPlatformError(
|
|
88
|
+
"exitCode",
|
|
89
|
+
new globalThis.Error(`Process interrupted due to receipt of signal: ${signal}`),
|
|
90
|
+
command
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
// Make sure to terminate the running process if the fiber is
|
|
97
|
+
// terminated
|
|
98
|
+
return Effect.sync(() => {
|
|
99
|
+
handle.kill("SIGKILL")
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const isRunning = Effect.sync(() =>
|
|
104
|
+
handle.exitCode === null &&
|
|
105
|
+
handle.signalCode === null &&
|
|
106
|
+
!handle.killed
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
const kill: CommandExecutor.Process["kill"] = (signal = "SIGTERM") =>
|
|
110
|
+
Effect.asyncInterrupt((resume) => {
|
|
111
|
+
handle.kill(signal)
|
|
112
|
+
handle.on("exit", () => {
|
|
113
|
+
resume(Effect.unit)
|
|
114
|
+
})
|
|
115
|
+
// Make sure to terminate the running process if the fiber
|
|
116
|
+
// is terminated
|
|
117
|
+
return Effect.sync(() => {
|
|
118
|
+
handle.kill("SIGKILL")
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
resume(Effect.sync<CommandExecutor.Process>(() => {
|
|
123
|
+
const pid = CommandExecutor.ProcessId(handle.pid!)
|
|
124
|
+
const stderr = fromReadable<Error.PlatformError, Uint8Array>(
|
|
125
|
+
() => handle.stderr!,
|
|
126
|
+
(err) => toPlatformError("fromReadable(stderr)", toError(err), command)
|
|
127
|
+
)
|
|
128
|
+
let stdout: Stream.Stream<never, Error.PlatformError, Uint8Array> = fromReadable<
|
|
129
|
+
Error.PlatformError,
|
|
130
|
+
Uint8Array
|
|
131
|
+
>(
|
|
132
|
+
() => handle.stdout!,
|
|
133
|
+
(err) => toPlatformError("fromReadable(stdout)", toError(err), command)
|
|
134
|
+
)
|
|
135
|
+
// TODO: add Sink.isSink
|
|
136
|
+
if (typeof command.stdout !== "string") {
|
|
137
|
+
stdout = Stream.transduce(stdout, command.stdout)
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
[CommandExecutor.ProcessTypeId]: CommandExecutor.ProcessTypeId,
|
|
141
|
+
pid,
|
|
142
|
+
exitCode,
|
|
143
|
+
isRunning,
|
|
144
|
+
kill,
|
|
145
|
+
stdin,
|
|
146
|
+
stderr,
|
|
147
|
+
stdout
|
|
148
|
+
}
|
|
149
|
+
}))
|
|
150
|
+
}
|
|
151
|
+
return Effect.async<never, never, void>((resume) => {
|
|
152
|
+
if (handle.pid) {
|
|
153
|
+
handle.kill("SIGTERM")
|
|
154
|
+
}
|
|
155
|
+
handle.on("exit", () => {
|
|
156
|
+
resume(Effect.unit)
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
),
|
|
161
|
+
Effect.tap((process) =>
|
|
162
|
+
Option.match(command.stdin, {
|
|
163
|
+
onNone: () => Effect.unit,
|
|
164
|
+
onSome: (stdin) => Effect.forkDaemon(Stream.run(stdin, process.stdin))
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
case "PipedCommand": {
|
|
170
|
+
const flattened = Command.flatten(command)
|
|
171
|
+
if (flattened.length === 1) {
|
|
172
|
+
return pipe(flattened[0], runCommand(fileSystem))
|
|
173
|
+
}
|
|
174
|
+
const head = flattened[0]
|
|
175
|
+
const tail = flattened.slice(1)
|
|
176
|
+
const initial = tail.slice(0, tail.length - 1)
|
|
177
|
+
const last = tail[tail.length - 1]
|
|
178
|
+
const stream = initial.reduce(
|
|
179
|
+
(stdin, command) =>
|
|
180
|
+
pipe(
|
|
181
|
+
Command.stdin(command, stdin),
|
|
182
|
+
runCommand(fileSystem),
|
|
183
|
+
Stream.flatMap((process) => process.stdout)
|
|
184
|
+
),
|
|
185
|
+
pipe(
|
|
186
|
+
runCommand(fileSystem)(head),
|
|
187
|
+
Stream.flatMap((process) => process.stdout)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
return pipe(Command.stdin(last, stream), runCommand(fileSystem))
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** @internal */
|
|
196
|
+
export const layer: Layer.Layer<FileSystem.FileSystem, never, CommandExecutor.CommandExecutor> = Layer.effect(
|
|
197
|
+
CommandExecutor.CommandExecutor,
|
|
198
|
+
pipe(
|
|
199
|
+
FileSystem.FileSystem,
|
|
200
|
+
Effect.map((fileSystem) => CommandExecutor.makeExecutor(runCommand(fileSystem)))
|
|
201
|
+
)
|
|
202
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PlatformError, SystemErrorReason } from "@effect/platform/Error"
|
|
2
|
+
import { SystemError } from "@effect/platform/Error"
|
|
3
|
+
import type { PathLike } from "node:fs"
|
|
4
|
+
|
|
5
|
+
/** @internal */
|
|
6
|
+
export const handleErrnoException = (module: SystemError["module"], method: string) =>
|
|
7
|
+
(
|
|
8
|
+
err: NodeJS.ErrnoException,
|
|
9
|
+
[path]: [path: PathLike | number, ...args: Array<any>]
|
|
10
|
+
): PlatformError => {
|
|
11
|
+
let reason: SystemErrorReason = "Unknown"
|
|
12
|
+
|
|
13
|
+
switch (err.code) {
|
|
14
|
+
case "ENOENT":
|
|
15
|
+
reason = "NotFound"
|
|
16
|
+
break
|
|
17
|
+
|
|
18
|
+
case "EACCES":
|
|
19
|
+
reason = "PermissionDenied"
|
|
20
|
+
break
|
|
21
|
+
|
|
22
|
+
case "EEXIST":
|
|
23
|
+
reason = "AlreadyExists"
|
|
24
|
+
break
|
|
25
|
+
|
|
26
|
+
case "EISDIR":
|
|
27
|
+
reason = "BadResource"
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
case "ENOTDIR":
|
|
31
|
+
reason = "BadResource"
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
case "EBUSY":
|
|
35
|
+
reason = "Busy"
|
|
36
|
+
break
|
|
37
|
+
|
|
38
|
+
case "ELOOP":
|
|
39
|
+
reason = "BadResource"
|
|
40
|
+
break
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return SystemError({
|
|
44
|
+
reason,
|
|
45
|
+
module,
|
|
46
|
+
method,
|
|
47
|
+
pathOrDescriptor: path as string | number,
|
|
48
|
+
syscall: err.syscall,
|
|
49
|
+
message: err.message
|
|
50
|
+
})
|
|
51
|
+
}
|