@effect/platform-node-shared 4.0.0-beta.10 → 4.0.0-beta.100
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/README.md +3 -3
- package/dist/NodeChildProcessSpawner.d.ts +19 -5
- package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/dist/NodeChildProcessSpawner.js +99 -29
- package/dist/NodeChildProcessSpawner.js.map +1 -1
- package/dist/NodeClusterSocket.d.ts +10 -4
- package/dist/NodeClusterSocket.d.ts.map +1 -1
- package/dist/NodeClusterSocket.js +22 -8
- package/dist/NodeClusterSocket.js.map +1 -1
- package/dist/NodeCrypto.d.ts +27 -0
- package/dist/NodeCrypto.d.ts.map +1 -0
- package/dist/NodeCrypto.js +55 -0
- package/dist/NodeCrypto.js.map +1 -0
- package/dist/NodeFileSystem.d.ts +5 -2
- package/dist/NodeFileSystem.d.ts.map +1 -1
- package/dist/NodeFileSystem.js +42 -26
- package/dist/NodeFileSystem.js.map +1 -1
- package/dist/NodePath.d.ts +15 -6
- package/dist/NodePath.d.ts.map +1 -1
- package/dist/NodePath.js +23 -7
- package/dist/NodePath.js.map +1 -1
- package/dist/NodeRuntime.d.ts +26 -7
- package/dist/NodeRuntime.d.ts.map +1 -1
- package/dist/NodeRuntime.js +8 -8
- package/dist/NodeRuntime.js.map +1 -1
- package/dist/NodeSink.d.ts +33 -4
- package/dist/NodeSink.d.ts.map +1 -1
- package/dist/NodeSink.js +31 -4
- package/dist/NodeSink.js.map +1 -1
- package/dist/NodeSocket.d.ts +41 -10
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +39 -16
- package/dist/NodeSocket.js.map +1 -1
- package/dist/NodeSocketServer.d.ts +25 -8
- package/dist/NodeSocketServer.d.ts.map +1 -1
- package/dist/NodeSocketServer.js +29 -12
- package/dist/NodeSocketServer.js.map +1 -1
- package/dist/NodeStdio.d.ts +6 -5
- package/dist/NodeStdio.d.ts.map +1 -1
- package/dist/NodeStdio.js +23 -7
- package/dist/NodeStdio.js.map +1 -1
- package/dist/NodeStream.d.ts +76 -20
- package/dist/NodeStream.d.ts.map +1 -1
- package/dist/NodeStream.js +71 -24
- package/dist/NodeStream.js.map +1 -1
- package/dist/NodeTerminal.d.ts +9 -2
- package/dist/NodeTerminal.d.ts.map +1 -1
- package/dist/NodeTerminal.js +13 -3
- package/dist/NodeTerminal.js.map +1 -1
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/utils.js.map +1 -1
- package/package.json +9 -9
- package/src/NodeChildProcessSpawner.ts +143 -46
- package/src/NodeClusterSocket.ts +22 -8
- package/src/NodeCrypto.ts +60 -0
- package/src/NodeFileSystem.ts +56 -28
- package/src/NodePath.ts +23 -7
- package/src/NodeRuntime.ts +28 -13
- package/src/NodeSink.ts +40 -4
- package/src/NodeSocket.ts +47 -17
- package/src/NodeSocketServer.ts +39 -13
- package/src/NodeStdio.ts +41 -23
- package/src/NodeStream.ts +92 -31
- package/src/NodeTerminal.ts +22 -4
- package/src/index.ts +65 -0
- package/src/internal/utils.ts +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 4.0.0
|
|
3
|
+
*/
|
|
4
|
+
// @barrel: Auto-generated exports. Do not edit manually.
|
|
5
|
+
/**
|
|
6
|
+
* @since 4.0.0
|
|
7
|
+
*/
|
|
8
|
+
export * as NodeChildProcessSpawner from "./NodeChildProcessSpawner.js";
|
|
9
|
+
/**
|
|
10
|
+
* @since 4.0.0
|
|
11
|
+
*/
|
|
12
|
+
export * as NodeClusterSocket from "./NodeClusterSocket.js";
|
|
13
|
+
/**
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
export * as NodeCrypto from "./NodeCrypto.js";
|
|
17
|
+
/**
|
|
18
|
+
* @since 4.0.0
|
|
19
|
+
*/
|
|
20
|
+
export * as NodeFileSystem from "./NodeFileSystem.js";
|
|
21
|
+
/**
|
|
22
|
+
* @since 4.0.0
|
|
23
|
+
*/
|
|
24
|
+
export * as NodePath from "./NodePath.js";
|
|
25
|
+
/**
|
|
26
|
+
* @since 4.0.0
|
|
27
|
+
*/
|
|
28
|
+
export * as NodeRuntime from "./NodeRuntime.js";
|
|
29
|
+
/**
|
|
30
|
+
* @since 4.0.0
|
|
31
|
+
*/
|
|
32
|
+
export * as NodeSink from "./NodeSink.js";
|
|
33
|
+
/**
|
|
34
|
+
* @since 4.0.0
|
|
35
|
+
*/
|
|
36
|
+
export * as NodeSocket from "./NodeSocket.js";
|
|
37
|
+
/**
|
|
38
|
+
* @since 4.0.0
|
|
39
|
+
*/
|
|
40
|
+
export * as NodeSocketServer from "./NodeSocketServer.js";
|
|
41
|
+
/**
|
|
42
|
+
* @since 4.0.0
|
|
43
|
+
*/
|
|
44
|
+
export * as NodeStdio from "./NodeStdio.js";
|
|
45
|
+
/**
|
|
46
|
+
* @since 4.0.0
|
|
47
|
+
*/
|
|
48
|
+
export * as NodeStream from "./NodeStream.js";
|
|
49
|
+
/**
|
|
50
|
+
* @since 4.0.0
|
|
51
|
+
*/
|
|
52
|
+
export * as NodeTerminal from "./NodeTerminal.js";
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["NodeChildProcessSpawner","NodeClusterSocket","NodeCrypto","NodeFileSystem","NodePath","NodeRuntime","NodeSink","NodeSocket","NodeSocketServer","NodeStdio","NodeStream","NodeTerminal"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;AAEA;;;AAGA,OAAO,KAAKA,uBAAuB,MAAM,8BAA8B;AAEvE;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AAEzD;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,UAAU,MAAM,iBAAiB;AAE7C;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["PlatformError","handleErrnoException","module","method","err","path","reason","code","systemError","_tag","pathOrDescriptor","syscall","cause"],"sources":["../../src/internal/utils.ts"],"sourcesContent":[null],"mappings":"AACA,OAAO,KAAKA,aAAa,MAAM,sBAAsB;AAGrD;AACA,OAAO,MAAMC,oBAAoB,GAAGA,CAACC,MAA6B,EAAEC,MAAc,KAClF,CACEC,GAA0B,EAC1B,CAACC,IAAI,
|
|
1
|
+
{"version":3,"file":"utils.js","names":["PlatformError","handleErrnoException","module","method","err","path","reason","code","systemError","_tag","pathOrDescriptor","syscall","cause"],"sources":["../../src/internal/utils.ts"],"sourcesContent":[null],"mappings":"AACA,OAAO,KAAKA,aAAa,MAAM,sBAAsB;AAGrD;AACA,OAAO,MAAMC,oBAAoB,GAAGA,CAACC,MAA6B,EAAEC,MAAc,KAClF,CACEC,GAA0B,EAC1B,CAACC,IAAI,CAA8E,KACpD;EAC/B,IAAIC,MAAM,GAAmB,SAAS;EAEtC,QAAQF,GAAG,CAACG,IAAI;IACd,KAAK,QAAQ;MACXD,MAAM,GAAG,UAAU;MACnB;IAEF,KAAK,QAAQ;MACXA,MAAM,GAAG,kBAAkB;MAC3B;IAEF,KAAK,QAAQ;MACXA,MAAM,GAAG,eAAe;MACxB;IAEF,KAAK,QAAQ;MACXA,MAAM,GAAG,aAAa;MACtB;IAEF,KAAK,SAAS;MACZA,MAAM,GAAG,aAAa;MACtB;IAEF,KAAK,OAAO;MACVA,MAAM,GAAG,MAAM;MACf;IAEF,KAAK,OAAO;MACVA,MAAM,GAAG,aAAa;MACtB;EACJ;EAEA,OAAON,aAAa,CAACQ,WAAW,CAAC;IAC/BC,IAAI,EAAEH,MAAM;IACZJ,MAAM;IACNC,MAAM;IACNO,gBAAgB,EAAEL,IAAuB;IACzCM,OAAO,EAAEP,GAAG,CAACO,OAAO;IACpBC,KAAK,EAAER;GACR,CAAC;AACJ,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect/platform-node-shared",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.0.0-beta.
|
|
4
|
+
"version": "4.0.0-beta.100",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Unified interfaces for common platform-specific services",
|
|
7
7
|
"homepage": "https://effect.website",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/Effect-TS/effect
|
|
10
|
+
"url": "https://github.com/Effect-TS/effect.git",
|
|
11
11
|
"directory": "packages/platform-node-shared"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/Effect-TS/effect
|
|
14
|
+
"url": "https://github.com/Effect-TS/effect/issues"
|
|
15
15
|
},
|
|
16
16
|
"tags": [
|
|
17
17
|
"typescript",
|
|
@@ -46,20 +46,20 @@
|
|
|
46
46
|
"provenance": true
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"effect": "^4.0.0-beta.
|
|
49
|
+
"effect": "^4.0.0-beta.100"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/node": "^
|
|
53
|
-
"tar": "^7.5.
|
|
54
|
-
"effect": "^4.0.0-beta.
|
|
52
|
+
"@types/node": "^26.1.1",
|
|
53
|
+
"tar": "^7.5.19",
|
|
54
|
+
"effect": "^4.0.0-beta.100"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@types/ws": "^8.18.1",
|
|
58
|
-
"ws": "^8.
|
|
58
|
+
"ws": "^8.21.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
|
+
"codegen": "effect-utils codegen",
|
|
61
62
|
"build": "tsc -b tsconfig.json && pnpm babel",
|
|
62
|
-
"build:tsgo": "tsgo -b tsconfig.json && pnpm babel",
|
|
63
63
|
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
|
|
64
64
|
"check": "tsc -b tsconfig.json",
|
|
65
65
|
"test": "vitest",
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Node.js implementation of
|
|
2
|
+
* Shared Node.js implementation of the child process spawner service.
|
|
3
|
+
*
|
|
4
|
+
* This module adapts `node:child_process.spawn` to the Effect
|
|
5
|
+
* `ChildProcessSpawner` service. Provide {@link layer} to run `ChildProcess`
|
|
6
|
+
* commands in Node-compatible runtimes: commands get scoped process handles
|
|
7
|
+
* with stdin sinks, stdout and stderr streams, exit-code waiting,
|
|
8
|
+
* interruption-time cleanup, process killing, and custom file-descriptor pipes.
|
|
9
|
+
*
|
|
10
|
+
* The implementation sits below the command-building API. It validates and
|
|
11
|
+
* resolves `cwd` through the Effect `FileSystem` and `Path` services,
|
|
12
|
+
* translates Node errno failures to `PlatformError`, and uses scopes to
|
|
13
|
+
* terminate referenced children when the owning effect is interrupted or
|
|
14
|
+
* finalized. Pipelines are flattened by {@link flattenCommand} and spawned one
|
|
15
|
+
* process at a time, wiring the selected source stream (`stdout`, `stderr`,
|
|
16
|
+
* `all`, or `fdN`) to the destination `stdin` or `fdN`.
|
|
3
17
|
*
|
|
4
18
|
* @since 4.0.0
|
|
5
19
|
*/
|
|
@@ -17,8 +31,15 @@ import * as Sink from "effect/Sink"
|
|
|
17
31
|
import * as Stream from "effect/Stream"
|
|
18
32
|
import * as ChildProcess from "effect/unstable/process/ChildProcess"
|
|
19
33
|
import type { ChildProcessHandle } from "effect/unstable/process/ChildProcessSpawner"
|
|
20
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
ChildProcessSpawner,
|
|
36
|
+
ExitCode,
|
|
37
|
+
make as makeSpawner,
|
|
38
|
+
makeHandle,
|
|
39
|
+
ProcessId
|
|
40
|
+
} from "effect/unstable/process/ChildProcessSpawner"
|
|
21
41
|
import * as NodeChildProcess from "node:child_process"
|
|
42
|
+
import { PassThrough } from "node:stream"
|
|
22
43
|
import { handleErrnoException } from "./internal/utils.ts"
|
|
23
44
|
import * as NodeSink from "./NodeSink.ts"
|
|
24
45
|
import * as NodeStream from "./NodeStream.ts"
|
|
@@ -200,8 +221,11 @@ const make = Effect.gen(function*() {
|
|
|
200
221
|
// Create a stream to read from for output file descriptors
|
|
201
222
|
let stream: Stream.Stream<Uint8Array, PlatformError.PlatformError> = Stream.empty
|
|
202
223
|
if (nodeStream && "read" in nodeStream) {
|
|
224
|
+
const passThrough = new PassThrough()
|
|
225
|
+
nodeStream.on("error", (error) => passThrough.destroy(error))
|
|
226
|
+
nodeStream.pipe(passThrough)
|
|
203
227
|
stream = NodeStream.fromReadable({
|
|
204
|
-
evaluate: () =>
|
|
228
|
+
evaluate: () => passThrough,
|
|
205
229
|
onError: (error) => toPlatformError(`fromReadable(fd${fd})`, toError(error), command)
|
|
206
230
|
})
|
|
207
231
|
}
|
|
@@ -261,16 +285,26 @@ const make = Effect.gen(function*() {
|
|
|
261
285
|
all: Stream.Stream<Uint8Array, PlatformError.PlatformError>
|
|
262
286
|
} => {
|
|
263
287
|
let stdout = childProcess.stdout ?
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
288
|
+
(() => {
|
|
289
|
+
const passThrough = new PassThrough()
|
|
290
|
+
childProcess.stdout!.on("error", (error) => passThrough.destroy(error))
|
|
291
|
+
childProcess.stdout!.pipe(passThrough)
|
|
292
|
+
return NodeStream.fromReadable({
|
|
293
|
+
evaluate: () => passThrough,
|
|
294
|
+
onError: (error) => toPlatformError("fromReadable(stdout)", toError(error), command)
|
|
295
|
+
})
|
|
296
|
+
})() :
|
|
268
297
|
Stream.empty
|
|
269
298
|
let stderr = childProcess.stderr ?
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
299
|
+
(() => {
|
|
300
|
+
const passThrough = new PassThrough()
|
|
301
|
+
childProcess.stderr!.on("error", (error) => passThrough.destroy(error))
|
|
302
|
+
childProcess.stderr!.pipe(passThrough)
|
|
303
|
+
return NodeStream.fromReadable({
|
|
304
|
+
evaluate: () => passThrough,
|
|
305
|
+
onError: (error) => toPlatformError("fromReadable(stderr)", toError(error), command)
|
|
306
|
+
})
|
|
307
|
+
})() :
|
|
274
308
|
Stream.empty
|
|
275
309
|
|
|
276
310
|
if (Sink.isSink(stdoutConfig.stream)) {
|
|
@@ -337,6 +371,23 @@ const make = Effect.gen(function*() {
|
|
|
337
371
|
})
|
|
338
372
|
}
|
|
339
373
|
|
|
374
|
+
const killProcessGroupOnExit = (
|
|
375
|
+
childProcess: NodeChildProcess.ChildProcess,
|
|
376
|
+
signal: NodeJS.Signals
|
|
377
|
+
): void => {
|
|
378
|
+
if (globalThis.process.platform === "win32") {
|
|
379
|
+
NodeChildProcess.exec(`taskkill /pid ${childProcess.pid} /T /F`, () => {
|
|
380
|
+
// ignore errors during best-effort cleanup
|
|
381
|
+
})
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
globalThis.process.kill(-childProcess.pid!, signal)
|
|
386
|
+
} catch {
|
|
387
|
+
// ignore errors during best-effort cleanup
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
340
391
|
const killProcess = (
|
|
341
392
|
command: ChildProcess.StandardCommand,
|
|
342
393
|
childProcess: NodeChildProcess.ChildProcess,
|
|
@@ -368,7 +419,7 @@ const make = Effect.gen(function*() {
|
|
|
368
419
|
? kill(command, childProcess, killSignal)
|
|
369
420
|
: Effect.timeoutOrElse(kill(command, childProcess, killSignal), {
|
|
370
421
|
duration: options.forceKillAfter,
|
|
371
|
-
|
|
422
|
+
orElse: () => kill(command, childProcess, "SIGKILL")
|
|
372
423
|
})
|
|
373
424
|
}
|
|
374
425
|
|
|
@@ -413,6 +464,7 @@ const make = Effect.gen(function*() {
|
|
|
413
464
|
const stdoutConfig = resolveOutputOption(cmd.options, "stdout")
|
|
414
465
|
const stderrConfig = resolveOutputOption(cmd.options, "stderr")
|
|
415
466
|
const resolvedAdditionalFds = resolveAdditionalFds(cmd.options)
|
|
467
|
+
let isReferenced = true
|
|
416
468
|
|
|
417
469
|
const cwd = yield* resolveWorkingDirectory(cmd.options)
|
|
418
470
|
const env = resolveEnvironment(cmd.options)
|
|
@@ -438,20 +490,40 @@ const make = Effect.gen(function*() {
|
|
|
438
490
|
}
|
|
439
491
|
return yield* Effect.void
|
|
440
492
|
}
|
|
493
|
+
if (!isReferenced) {
|
|
494
|
+
return yield* Effect.void
|
|
495
|
+
}
|
|
441
496
|
// Process is still running, kill it
|
|
442
497
|
return yield* killWithTimeout((command, childProcess, signal) =>
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
(
|
|
498
|
+
killProcessGroup(command, childProcess, signal).pipe(
|
|
499
|
+
Effect.catch(() => killProcess(command, childProcess, signal)),
|
|
500
|
+
Effect.andThen(Deferred.await(exitSignal))
|
|
446
501
|
)
|
|
447
502
|
).pipe(
|
|
448
|
-
Effect.andThen(Deferred.await(exitSignal)),
|
|
449
503
|
Effect.ignore
|
|
450
504
|
)
|
|
451
505
|
})
|
|
452
506
|
)
|
|
453
507
|
|
|
454
508
|
const pid = ProcessId(childProcess.pid!)
|
|
509
|
+
childProcess.on("exit", (code) => {
|
|
510
|
+
if (code !== 0 && Predicate.isNotNull(code)) {
|
|
511
|
+
killProcessGroupOnExit(childProcess, cmd.options.killSignal ?? "SIGTERM")
|
|
512
|
+
}
|
|
513
|
+
})
|
|
514
|
+
const reref = Effect.sync(() => {
|
|
515
|
+
if (!isReferenced) {
|
|
516
|
+
childProcess.ref()
|
|
517
|
+
isReferenced = true
|
|
518
|
+
}
|
|
519
|
+
})
|
|
520
|
+
const unref = Effect.sync(() => {
|
|
521
|
+
if (isReferenced) {
|
|
522
|
+
childProcess.unref()
|
|
523
|
+
isReferenced = false
|
|
524
|
+
}
|
|
525
|
+
return reref
|
|
526
|
+
})
|
|
455
527
|
const stdin = yield* setupChildStdin(cmd, childProcess, stdinConfig)
|
|
456
528
|
const { all, stderr, stdout } = setupChildOutputStreams(cmd, childProcess, stdoutConfig, stderrConfig)
|
|
457
529
|
const { getInputFd, getOutputFd } = yield* setupAdditionalFds(cmd, childProcess, resolvedAdditionalFds)
|
|
@@ -469,12 +541,11 @@ const make = Effect.gen(function*() {
|
|
|
469
541
|
const kill = (options?: ChildProcess.KillOptions | undefined) => {
|
|
470
542
|
const killWithTimeout = withTimeout(childProcess, cmd, options)
|
|
471
543
|
return killWithTimeout((command, childProcess, signal) =>
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
(
|
|
544
|
+
killProcessGroup(command, childProcess, signal).pipe(
|
|
545
|
+
Effect.catch(() => killProcess(command, childProcess, signal)),
|
|
546
|
+
Effect.andThen(Deferred.await(exitSignal))
|
|
475
547
|
)
|
|
476
548
|
).pipe(
|
|
477
|
-
Effect.andThen(Deferred.await(exitSignal)),
|
|
478
549
|
Effect.asVoid
|
|
479
550
|
)
|
|
480
551
|
}
|
|
@@ -489,14 +560,15 @@ const make = Effect.gen(function*() {
|
|
|
489
560
|
stderr,
|
|
490
561
|
all,
|
|
491
562
|
getInputFd,
|
|
492
|
-
getOutputFd
|
|
563
|
+
getOutputFd,
|
|
564
|
+
unref
|
|
493
565
|
})
|
|
494
566
|
}
|
|
495
567
|
case "PipedCommand": {
|
|
496
568
|
const { commands, pipeOptions } = flattenCommand(cmd)
|
|
497
569
|
const [root, ...pipeline] = commands
|
|
498
570
|
|
|
499
|
-
|
|
571
|
+
const handles = [yield* spawnCommand(root)]
|
|
500
572
|
|
|
501
573
|
for (let i = 0; i < pipeline.length; i++) {
|
|
502
574
|
const command = pipeline[i]
|
|
@@ -505,7 +577,7 @@ const make = Effect.gen(function*() {
|
|
|
505
577
|
|
|
506
578
|
// Get the appropriate stream from the source based on `from` option
|
|
507
579
|
const sourceStream = Stream.unwrap(
|
|
508
|
-
Effect.
|
|
580
|
+
Effect.succeed(getSourceStream(handles[handles.length - 1], options.from))
|
|
509
581
|
)
|
|
510
582
|
|
|
511
583
|
// Determine where to pipe: stdin or custom fd
|
|
@@ -513,48 +585,73 @@ const make = Effect.gen(function*() {
|
|
|
513
585
|
|
|
514
586
|
if (toOption === "stdin") {
|
|
515
587
|
// Pipe to stdin (default behavior)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
588
|
+
handles.push(
|
|
589
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
590
|
+
...command.options,
|
|
591
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
592
|
+
}))
|
|
593
|
+
)
|
|
520
594
|
} else {
|
|
521
595
|
// Pipe to custom fd (fd3, fd4, etc.)
|
|
522
596
|
const fd = ChildProcess.parseFdName(toOption)
|
|
523
597
|
if (Predicate.isNotUndefined(fd)) {
|
|
524
598
|
const fdName = ChildProcess.fdName(fd) as `fd${number}`
|
|
525
599
|
const existingFds = command.options.additionalFds ?? {}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
600
|
+
handles.push(
|
|
601
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
602
|
+
...command.options,
|
|
603
|
+
additionalFds: {
|
|
604
|
+
...existingFds,
|
|
605
|
+
[fdName]: { type: "input" as const, stream: sourceStream }
|
|
606
|
+
}
|
|
607
|
+
}))
|
|
608
|
+
)
|
|
533
609
|
} else {
|
|
534
610
|
// Invalid fd name, fall back to stdin
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
611
|
+
handles.push(
|
|
612
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
613
|
+
...command.options,
|
|
614
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
615
|
+
}))
|
|
616
|
+
)
|
|
539
617
|
}
|
|
540
618
|
}
|
|
541
619
|
}
|
|
542
620
|
|
|
543
|
-
|
|
621
|
+
const handle = handles[handles.length - 1]
|
|
622
|
+
const unref = Effect.gen(function*() {
|
|
623
|
+
const rerefs: Array<Effect.Effect<void, PlatformError.PlatformError>> = []
|
|
624
|
+
for (const handle of handles) {
|
|
625
|
+
rerefs.push(yield* handle.unref)
|
|
626
|
+
}
|
|
627
|
+
return Effect.forEach([...rerefs].reverse(), (reref) => reref, { discard: true })
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
return makeHandle({
|
|
631
|
+
pid: handle.pid,
|
|
632
|
+
exitCode: handle.exitCode,
|
|
633
|
+
isRunning: handle.isRunning,
|
|
634
|
+
kill: handle.kill,
|
|
635
|
+
stdin: handle.stdin,
|
|
636
|
+
stdout: handle.stdout,
|
|
637
|
+
stderr: handle.stderr,
|
|
638
|
+
all: handle.all,
|
|
639
|
+
getInputFd: handle.getInputFd,
|
|
640
|
+
getOutputFd: handle.getOutputFd,
|
|
641
|
+
unref
|
|
642
|
+
})
|
|
544
643
|
}
|
|
545
644
|
}
|
|
546
645
|
})
|
|
547
646
|
|
|
548
|
-
return
|
|
549
|
-
spawn: spawnCommand
|
|
550
|
-
})
|
|
647
|
+
return makeSpawner(spawnCommand)
|
|
551
648
|
})
|
|
552
649
|
|
|
553
650
|
/**
|
|
554
|
-
* Layer
|
|
651
|
+
* Layer that provides the `NodeChildProcessSpawner` implementation.
|
|
555
652
|
*
|
|
653
|
+
* @category layers
|
|
556
654
|
* @since 4.0.0
|
|
557
|
-
* @category Layers
|
|
558
655
|
*/
|
|
559
656
|
export const layer: Layer.Layer<
|
|
560
657
|
ChildProcessSpawner,
|
|
@@ -569,8 +666,8 @@ export const layer: Layer.Layer<
|
|
|
569
666
|
/**
|
|
570
667
|
* Result of flattening a pipeline of commands.
|
|
571
668
|
*
|
|
669
|
+
* @category models
|
|
572
670
|
* @since 4.0.0
|
|
573
|
-
* @category Models
|
|
574
671
|
*/
|
|
575
672
|
export interface FlattenedPipeline {
|
|
576
673
|
readonly commands: Arr.NonEmptyReadonlyArray<ChildProcess.StandardCommand>
|
|
@@ -581,8 +678,8 @@ export interface FlattenedPipeline {
|
|
|
581
678
|
* Flattens a `Command` into an array of `StandardCommand`s along with pipe
|
|
582
679
|
* options for each connection.
|
|
583
680
|
*
|
|
681
|
+
* @category transforming
|
|
584
682
|
* @since 4.0.0
|
|
585
|
-
* @category Utilities
|
|
586
683
|
*/
|
|
587
684
|
export const flattenCommand = (
|
|
588
685
|
command: ChildProcess.Command
|
package/src/NodeClusterSocket.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node TCP socket transport for Effect Cluster runner-to-runner RPC.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the shared Node layers used by socket-based cluster
|
|
5
|
+
* transports. `layerClientProtocol` opens TCP sockets to peer runner addresses
|
|
6
|
+
* and wraps them in the current RPC serialization protocol. `layerSocketServer`
|
|
7
|
+
* exposes the socket server that receives incoming runner RPC traffic.
|
|
8
|
+
*
|
|
9
|
+
* @since 4.0.0
|
|
3
10
|
*/
|
|
4
11
|
import * as Effect from "effect/Effect"
|
|
5
12
|
import * as Layer from "effect/Layer"
|
|
13
|
+
import * as Option from "effect/Option"
|
|
6
14
|
import * as Runners from "effect/unstable/cluster/Runners"
|
|
7
15
|
import * as ShardingConfig from "effect/unstable/cluster/ShardingConfig"
|
|
8
16
|
import * as RpcClient from "effect/unstable/rpc/RpcClient"
|
|
@@ -13,8 +21,11 @@ import * as NodeSocket from "./NodeSocket.ts"
|
|
|
13
21
|
import * as NodeSocketServer from "./NodeSocketServer.ts"
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
24
|
+
* Provides the cluster `RpcClientProtocol` by opening TCP sockets to runner
|
|
25
|
+
* addresses and using the current RPC serialization service.
|
|
26
|
+
*
|
|
27
|
+
* @category layers
|
|
28
|
+
* @since 4.0.0
|
|
18
29
|
*/
|
|
19
30
|
export const layerClientProtocol: Layer.Layer<
|
|
20
31
|
Runners.RpcClientProtocol,
|
|
@@ -39,8 +50,11 @@ export const layerClientProtocol: Layer.Layer<
|
|
|
39
50
|
)
|
|
40
51
|
|
|
41
52
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
53
|
+
* Provides the socket server used by cluster runners, listening on
|
|
54
|
+
* `ShardingConfig.runnerListenAddress` or `runnerAddress`.
|
|
55
|
+
*
|
|
56
|
+
* @category layers
|
|
57
|
+
* @since 4.0.0
|
|
44
58
|
*/
|
|
45
59
|
export const layerSocketServer: Layer.Layer<
|
|
46
60
|
SocketServer.SocketServer,
|
|
@@ -48,9 +62,9 @@ export const layerSocketServer: Layer.Layer<
|
|
|
48
62
|
ShardingConfig.ShardingConfig
|
|
49
63
|
> = Effect.gen(function*() {
|
|
50
64
|
const config = yield* ShardingConfig.ShardingConfig
|
|
51
|
-
const listenAddress = config.runnerListenAddress
|
|
52
|
-
if (listenAddress
|
|
65
|
+
const listenAddress = Option.orElse(config.runnerListenAddress, () => config.runnerAddress)
|
|
66
|
+
if (Option.isNone(listenAddress)) {
|
|
53
67
|
return yield* Effect.die("layerSocketServer: ShardingConfig.runnerListenAddress is None")
|
|
54
68
|
}
|
|
55
|
-
return NodeSocketServer.layer(listenAddress)
|
|
69
|
+
return NodeSocketServer.layer(listenAddress.value)
|
|
56
70
|
}).pipe(Layer.unwrap)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-compatible implementation of Effect's `Crypto` service.
|
|
3
|
+
*
|
|
4
|
+
* This module builds the service from `node:crypto`, using `randomBytes` for
|
|
5
|
+
* random data and `createHash` for supported digest algorithms. It exports
|
|
6
|
+
* `make` as the concrete service value and `layer` for providing it through
|
|
7
|
+
* Effect context.
|
|
8
|
+
*
|
|
9
|
+
* @since 1.0.0
|
|
10
|
+
*/
|
|
11
|
+
import * as EffectCrypto from "effect/Crypto"
|
|
12
|
+
import * as Effect from "effect/Effect"
|
|
13
|
+
import * as Layer from "effect/Layer"
|
|
14
|
+
import * as PlatformError from "effect/PlatformError"
|
|
15
|
+
import * as NodeCrypto from "node:crypto"
|
|
16
|
+
|
|
17
|
+
const toHashAlgorithm = (algorithm: EffectCrypto.DigestAlgorithm): string => {
|
|
18
|
+
switch (algorithm) {
|
|
19
|
+
case "SHA-1":
|
|
20
|
+
return "sha1"
|
|
21
|
+
case "SHA-256":
|
|
22
|
+
return "sha256"
|
|
23
|
+
case "SHA-384":
|
|
24
|
+
return "sha384"
|
|
25
|
+
case "SHA-512":
|
|
26
|
+
return "sha512"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const digest: EffectCrypto.Crypto["digest"] = (algorithm, data) =>
|
|
31
|
+
Effect.try({
|
|
32
|
+
try: () => Uint8Array.from(NodeCrypto.createHash(toHashAlgorithm(algorithm)).update(data).digest()),
|
|
33
|
+
catch: (cause) =>
|
|
34
|
+
PlatformError.systemError({
|
|
35
|
+
module: "Crypto",
|
|
36
|
+
method: "digest",
|
|
37
|
+
_tag: "Unknown",
|
|
38
|
+
description: "Could not compute digest",
|
|
39
|
+
cause
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The default Node.js Crypto service implementation.
|
|
45
|
+
*
|
|
46
|
+
* @category constructors
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
*/
|
|
49
|
+
export const make: EffectCrypto.Crypto = EffectCrypto.make({
|
|
50
|
+
randomBytes: NodeCrypto.randomBytes,
|
|
51
|
+
digest
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Layer that provides the Node.js Crypto service implementation.
|
|
56
|
+
*
|
|
57
|
+
* @category layers
|
|
58
|
+
* @since 1.0.0
|
|
59
|
+
*/
|
|
60
|
+
export const layer: Layer.Layer<EffectCrypto.Crypto> = Layer.succeed(EffectCrypto.Crypto, make)
|