@effect/platform-node-shared 4.0.0-beta.7 → 4.0.0-beta.71
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/NodeChildProcessSpawner.d.ts +18 -4
- package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/dist/NodeChildProcessSpawner.js +76 -17
- 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 +46 -8
- package/dist/NodeClusterSocket.js.map +1 -1
- package/dist/NodeCrypto.d.ts +42 -0
- package/dist/NodeCrypto.d.ts.map +1 -0
- package/dist/NodeCrypto.js +70 -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 +56 -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 +39 -7
- package/dist/NodePath.js.map +1 -1
- package/dist/NodeRuntime.d.ts +48 -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 +49 -4
- package/dist/NodeSink.d.ts.map +1 -1
- package/dist/NodeSink.js +23 -4
- package/dist/NodeSink.js.map +1 -1
- package/dist/NodeSocket.d.ts +50 -9
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +31 -15
- package/dist/NodeSocket.js.map +1 -1
- package/dist/NodeSocketServer.d.ts +24 -7
- package/dist/NodeSocketServer.d.ts.map +1 -1
- package/dist/NodeSocketServer.js +28 -11
- 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 +46 -7
- package/dist/NodeStdio.js.map +1 -1
- package/dist/NodeStream.d.ts +93 -20
- package/dist/NodeStream.d.ts.map +1 -1
- package/dist/NodeStream.js +85 -23
- 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/package.json +7 -6
- package/src/NodeChildProcessSpawner.ts +116 -28
- package/src/NodeClusterSocket.ts +46 -8
- package/src/NodeCrypto.ts +75 -0
- package/src/NodeFileSystem.ts +63 -28
- package/src/NodePath.ts +39 -7
- package/src/NodeRuntime.ts +50 -13
- package/src/NodeSink.ts +56 -4
- package/src/NodeSocket.ts +56 -16
- package/src/NodeSocketServer.ts +64 -12
- package/src/NodeStdio.ts +64 -23
- package/src/NodeStream.ts +106 -30
- package/src/NodeTerminal.ts +44 -4
- package/src/index.ts +65 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 4.0.0
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* @since 4.0.0
|
|
6
|
+
*/
|
|
7
|
+
export * as NodeChildProcessSpawner from "./NodeChildProcessSpawner.ts";
|
|
8
|
+
/**
|
|
9
|
+
* @since 4.0.0
|
|
10
|
+
*/
|
|
11
|
+
export * as NodeClusterSocket from "./NodeClusterSocket.ts";
|
|
12
|
+
/**
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
export * as NodeCrypto from "./NodeCrypto.ts";
|
|
16
|
+
/**
|
|
17
|
+
* @since 4.0.0
|
|
18
|
+
*/
|
|
19
|
+
export * as NodeFileSystem from "./NodeFileSystem.ts";
|
|
20
|
+
/**
|
|
21
|
+
* @since 4.0.0
|
|
22
|
+
*/
|
|
23
|
+
export * as NodePath from "./NodePath.ts";
|
|
24
|
+
/**
|
|
25
|
+
* @since 4.0.0
|
|
26
|
+
*/
|
|
27
|
+
export * as NodeRuntime from "./NodeRuntime.ts";
|
|
28
|
+
/**
|
|
29
|
+
* @since 4.0.0
|
|
30
|
+
*/
|
|
31
|
+
export * as NodeSink from "./NodeSink.ts";
|
|
32
|
+
/**
|
|
33
|
+
* @since 4.0.0
|
|
34
|
+
*/
|
|
35
|
+
export * as NodeSocket from "./NodeSocket.ts";
|
|
36
|
+
/**
|
|
37
|
+
* @since 4.0.0
|
|
38
|
+
*/
|
|
39
|
+
export * as NodeSocketServer from "./NodeSocketServer.ts";
|
|
40
|
+
/**
|
|
41
|
+
* @since 4.0.0
|
|
42
|
+
*/
|
|
43
|
+
export * as NodeStdio from "./NodeStdio.ts";
|
|
44
|
+
/**
|
|
45
|
+
* @since 4.0.0
|
|
46
|
+
*/
|
|
47
|
+
export * as NodeStream from "./NodeStream.ts";
|
|
48
|
+
/**
|
|
49
|
+
* @since 4.0.0
|
|
50
|
+
*/
|
|
51
|
+
export * as NodeTerminal from "./NodeTerminal.ts";
|
|
52
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,OAAO,KAAK,uBAAuB,MAAM,8BAA8B,CAAA;AAEvE;;GAEG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;GAEG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;GAEG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;GAEG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA"}
|
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":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.71",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Unified interfaces for common platform-specific services",
|
|
7
7
|
"homepage": "https://effect.website",
|
|
@@ -46,18 +46,19 @@
|
|
|
46
46
|
"provenance": true
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"effect": "^4.0.0-beta.
|
|
49
|
+
"effect": "^4.0.0-beta.71"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/node": "^25.
|
|
53
|
-
"tar": "^7.5.
|
|
54
|
-
"effect": "^4.0.0-beta.
|
|
52
|
+
"@types/node": "^25.7.0",
|
|
53
|
+
"tar": "^7.5.15",
|
|
54
|
+
"effect": "^4.0.0-beta.71"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@types/ws": "^8.18.1",
|
|
58
|
-
"ws": "^8.
|
|
58
|
+
"ws": "^8.20.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
|
+
"codegen": "effect-utils codegen",
|
|
61
62
|
"build": "tsc -b tsconfig.json && pnpm babel",
|
|
62
63
|
"build:tsgo": "tsgo -b tsconfig.json && pnpm babel",
|
|
63
64
|
"babel": "babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
|
|
@@ -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,7 +31,13 @@ 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"
|
|
22
42
|
import { handleErrnoException } from "./internal/utils.ts"
|
|
23
43
|
import * as NodeSink from "./NodeSink.ts"
|
|
@@ -337,6 +357,23 @@ const make = Effect.gen(function*() {
|
|
|
337
357
|
})
|
|
338
358
|
}
|
|
339
359
|
|
|
360
|
+
const killProcessGroupOnExit = (
|
|
361
|
+
childProcess: NodeChildProcess.ChildProcess,
|
|
362
|
+
signal: NodeJS.Signals
|
|
363
|
+
): void => {
|
|
364
|
+
if (globalThis.process.platform === "win32") {
|
|
365
|
+
NodeChildProcess.exec(`taskkill /pid ${childProcess.pid} /T /F`, () => {
|
|
366
|
+
// ignore errors during best-effort cleanup
|
|
367
|
+
})
|
|
368
|
+
return
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
globalThis.process.kill(-childProcess.pid!, signal)
|
|
372
|
+
} catch {
|
|
373
|
+
// ignore errors during best-effort cleanup
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
340
377
|
const killProcess = (
|
|
341
378
|
command: ChildProcess.StandardCommand,
|
|
342
379
|
childProcess: NodeChildProcess.ChildProcess,
|
|
@@ -368,7 +405,7 @@ const make = Effect.gen(function*() {
|
|
|
368
405
|
? kill(command, childProcess, killSignal)
|
|
369
406
|
: Effect.timeoutOrElse(kill(command, childProcess, killSignal), {
|
|
370
407
|
duration: options.forceKillAfter,
|
|
371
|
-
|
|
408
|
+
orElse: () => kill(command, childProcess, "SIGKILL")
|
|
372
409
|
})
|
|
373
410
|
}
|
|
374
411
|
|
|
@@ -413,6 +450,8 @@ const make = Effect.gen(function*() {
|
|
|
413
450
|
const stdoutConfig = resolveOutputOption(cmd.options, "stdout")
|
|
414
451
|
const stderrConfig = resolveOutputOption(cmd.options, "stderr")
|
|
415
452
|
const resolvedAdditionalFds = resolveAdditionalFds(cmd.options)
|
|
453
|
+
let isReferenced = true
|
|
454
|
+
let cleanupOnNonZeroExit = false
|
|
416
455
|
|
|
417
456
|
const cwd = yield* resolveWorkingDirectory(cmd.options)
|
|
418
457
|
const env = resolveEnvironment(cmd.options)
|
|
@@ -438,6 +477,9 @@ const make = Effect.gen(function*() {
|
|
|
438
477
|
}
|
|
439
478
|
return yield* Effect.void
|
|
440
479
|
}
|
|
480
|
+
if (!isReferenced) {
|
|
481
|
+
return yield* Effect.void
|
|
482
|
+
}
|
|
441
483
|
// Process is still running, kill it
|
|
442
484
|
return yield* killWithTimeout((command, childProcess, signal) =>
|
|
443
485
|
Effect.catch(
|
|
@@ -452,6 +494,26 @@ const make = Effect.gen(function*() {
|
|
|
452
494
|
)
|
|
453
495
|
|
|
454
496
|
const pid = ProcessId(childProcess.pid!)
|
|
497
|
+
childProcess.on("exit", (code) => {
|
|
498
|
+
if (cleanupOnNonZeroExit && code !== 0 && Predicate.isNotNull(code)) {
|
|
499
|
+
killProcessGroupOnExit(childProcess, cmd.options.killSignal ?? "SIGTERM")
|
|
500
|
+
}
|
|
501
|
+
})
|
|
502
|
+
const reref = Effect.sync(() => {
|
|
503
|
+
if (!isReferenced) {
|
|
504
|
+
childProcess.ref()
|
|
505
|
+
isReferenced = true
|
|
506
|
+
cleanupOnNonZeroExit = false
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
const unref = Effect.sync(() => {
|
|
510
|
+
if (isReferenced) {
|
|
511
|
+
childProcess.unref()
|
|
512
|
+
isReferenced = false
|
|
513
|
+
cleanupOnNonZeroExit = true
|
|
514
|
+
}
|
|
515
|
+
return reref
|
|
516
|
+
})
|
|
455
517
|
const stdin = yield* setupChildStdin(cmd, childProcess, stdinConfig)
|
|
456
518
|
const { all, stderr, stdout } = setupChildOutputStreams(cmd, childProcess, stdoutConfig, stderrConfig)
|
|
457
519
|
const { getInputFd, getOutputFd } = yield* setupAdditionalFds(cmd, childProcess, resolvedAdditionalFds)
|
|
@@ -489,14 +551,15 @@ const make = Effect.gen(function*() {
|
|
|
489
551
|
stderr,
|
|
490
552
|
all,
|
|
491
553
|
getInputFd,
|
|
492
|
-
getOutputFd
|
|
554
|
+
getOutputFd,
|
|
555
|
+
unref
|
|
493
556
|
})
|
|
494
557
|
}
|
|
495
558
|
case "PipedCommand": {
|
|
496
559
|
const { commands, pipeOptions } = flattenCommand(cmd)
|
|
497
560
|
const [root, ...pipeline] = commands
|
|
498
561
|
|
|
499
|
-
|
|
562
|
+
const handles = [yield* spawnCommand(root)]
|
|
500
563
|
|
|
501
564
|
for (let i = 0; i < pipeline.length; i++) {
|
|
502
565
|
const command = pipeline[i]
|
|
@@ -505,7 +568,7 @@ const make = Effect.gen(function*() {
|
|
|
505
568
|
|
|
506
569
|
// Get the appropriate stream from the source based on `from` option
|
|
507
570
|
const sourceStream = Stream.unwrap(
|
|
508
|
-
Effect.
|
|
571
|
+
Effect.succeed(getSourceStream(handles[handles.length - 1], options.from))
|
|
509
572
|
)
|
|
510
573
|
|
|
511
574
|
// Determine where to pipe: stdin or custom fd
|
|
@@ -513,48 +576,73 @@ const make = Effect.gen(function*() {
|
|
|
513
576
|
|
|
514
577
|
if (toOption === "stdin") {
|
|
515
578
|
// Pipe to stdin (default behavior)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
579
|
+
handles.push(
|
|
580
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
581
|
+
...command.options,
|
|
582
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
583
|
+
}))
|
|
584
|
+
)
|
|
520
585
|
} else {
|
|
521
586
|
// Pipe to custom fd (fd3, fd4, etc.)
|
|
522
587
|
const fd = ChildProcess.parseFdName(toOption)
|
|
523
588
|
if (Predicate.isNotUndefined(fd)) {
|
|
524
589
|
const fdName = ChildProcess.fdName(fd) as `fd${number}`
|
|
525
590
|
const existingFds = command.options.additionalFds ?? {}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
591
|
+
handles.push(
|
|
592
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
593
|
+
...command.options,
|
|
594
|
+
additionalFds: {
|
|
595
|
+
...existingFds,
|
|
596
|
+
[fdName]: { type: "input" as const, stream: sourceStream }
|
|
597
|
+
}
|
|
598
|
+
}))
|
|
599
|
+
)
|
|
533
600
|
} else {
|
|
534
601
|
// Invalid fd name, fall back to stdin
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
602
|
+
handles.push(
|
|
603
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
604
|
+
...command.options,
|
|
605
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
606
|
+
}))
|
|
607
|
+
)
|
|
539
608
|
}
|
|
540
609
|
}
|
|
541
610
|
}
|
|
542
611
|
|
|
543
|
-
|
|
612
|
+
const handle = handles[handles.length - 1]
|
|
613
|
+
const unref = Effect.gen(function*() {
|
|
614
|
+
const rerefs: Array<Effect.Effect<void, PlatformError.PlatformError>> = []
|
|
615
|
+
for (const handle of handles) {
|
|
616
|
+
rerefs.push(yield* handle.unref)
|
|
617
|
+
}
|
|
618
|
+
return Effect.forEach([...rerefs].reverse(), (reref) => reref, { discard: true })
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
return makeHandle({
|
|
622
|
+
pid: handle.pid,
|
|
623
|
+
exitCode: handle.exitCode,
|
|
624
|
+
isRunning: handle.isRunning,
|
|
625
|
+
kill: handle.kill,
|
|
626
|
+
stdin: handle.stdin,
|
|
627
|
+
stdout: handle.stdout,
|
|
628
|
+
stderr: handle.stderr,
|
|
629
|
+
all: handle.all,
|
|
630
|
+
getInputFd: handle.getInputFd,
|
|
631
|
+
getOutputFd: handle.getOutputFd,
|
|
632
|
+
unref
|
|
633
|
+
})
|
|
544
634
|
}
|
|
545
635
|
}
|
|
546
636
|
})
|
|
547
637
|
|
|
548
|
-
return
|
|
549
|
-
spawn: spawnCommand
|
|
550
|
-
})
|
|
638
|
+
return makeSpawner(spawnCommand)
|
|
551
639
|
})
|
|
552
640
|
|
|
553
641
|
/**
|
|
554
642
|
* Layer providing the `NodeChildProcessSpawner` implementation.
|
|
555
643
|
*
|
|
644
|
+
* @category layers
|
|
556
645
|
* @since 4.0.0
|
|
557
|
-
* @category Layers
|
|
558
646
|
*/
|
|
559
647
|
export const layer: Layer.Layer<
|
|
560
648
|
ChildProcessSpawner,
|
|
@@ -569,8 +657,8 @@ export const layer: Layer.Layer<
|
|
|
569
657
|
/**
|
|
570
658
|
* Result of flattening a pipeline of commands.
|
|
571
659
|
*
|
|
660
|
+
* @category models
|
|
572
661
|
* @since 4.0.0
|
|
573
|
-
* @category Models
|
|
574
662
|
*/
|
|
575
663
|
export interface FlattenedPipeline {
|
|
576
664
|
readonly commands: Arr.NonEmptyReadonlyArray<ChildProcess.StandardCommand>
|
|
@@ -581,8 +669,8 @@ export interface FlattenedPipeline {
|
|
|
581
669
|
* Flattens a `Command` into an array of `StandardCommand`s along with pipe
|
|
582
670
|
* options for each connection.
|
|
583
671
|
*
|
|
672
|
+
* @category utils
|
|
584
673
|
* @since 4.0.0
|
|
585
|
-
* @category Utilities
|
|
586
674
|
*/
|
|
587
675
|
export const flattenCommand = (
|
|
588
676
|
command: ChildProcess.Command
|
package/src/NodeClusterSocket.ts
CHANGED
|
@@ -1,8 +1,40 @@
|
|
|
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
|
+
* **Mental model**
|
|
10
|
+
*
|
|
11
|
+
* The cluster runtime decides which runners exist, which shards they own, and
|
|
12
|
+
* which messages must be delivered. This module only provides the TCP transport
|
|
13
|
+
* those services use after a runner address has been selected. The client side
|
|
14
|
+
* dials an advertised runner address; the server side listens on the address
|
|
15
|
+
* configured for the local runner.
|
|
16
|
+
*
|
|
17
|
+
* **Common tasks**
|
|
18
|
+
*
|
|
19
|
+
* Add these layers when a Node or Node-compatible cluster deployment should use
|
|
20
|
+
* direct socket RPC instead of an HTTP transport. Configure `runnerAddress` as
|
|
21
|
+
* the address other runners can reach, and configure `runnerListenAddress` when
|
|
22
|
+
* the local process must bind a different host or port.
|
|
23
|
+
*
|
|
24
|
+
* **Gotchas**
|
|
25
|
+
*
|
|
26
|
+
* Containers, port mappings, and Kubernetes services often require different
|
|
27
|
+
* advertised and listening addresses. Serialization is provided by the
|
|
28
|
+
* surrounding layer, not by this module. A reachable socket confirms only that
|
|
29
|
+
* TCP transport is available; gossip, shard ownership, health checks, and
|
|
30
|
+
* persisted message notification are coordinated by the cluster services that
|
|
31
|
+
* use this transport.
|
|
32
|
+
*
|
|
33
|
+
* @since 4.0.0
|
|
3
34
|
*/
|
|
4
35
|
import * as Effect from "effect/Effect"
|
|
5
36
|
import * as Layer from "effect/Layer"
|
|
37
|
+
import * as Option from "effect/Option"
|
|
6
38
|
import * as Runners from "effect/unstable/cluster/Runners"
|
|
7
39
|
import * as ShardingConfig from "effect/unstable/cluster/ShardingConfig"
|
|
8
40
|
import * as RpcClient from "effect/unstable/rpc/RpcClient"
|
|
@@ -13,8 +45,11 @@ import * as NodeSocket from "./NodeSocket.ts"
|
|
|
13
45
|
import * as NodeSocketServer from "./NodeSocketServer.ts"
|
|
14
46
|
|
|
15
47
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
48
|
+
* Provides the cluster `RpcClientProtocol` by opening TCP sockets to runner
|
|
49
|
+
* addresses and using the current RPC serialization service.
|
|
50
|
+
*
|
|
51
|
+
* @category layers
|
|
52
|
+
* @since 4.0.0
|
|
18
53
|
*/
|
|
19
54
|
export const layerClientProtocol: Layer.Layer<
|
|
20
55
|
Runners.RpcClientProtocol,
|
|
@@ -39,8 +74,11 @@ export const layerClientProtocol: Layer.Layer<
|
|
|
39
74
|
)
|
|
40
75
|
|
|
41
76
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
77
|
+
* Provides the socket server used by cluster runners, listening on
|
|
78
|
+
* `ShardingConfig.runnerListenAddress` or `runnerAddress`.
|
|
79
|
+
*
|
|
80
|
+
* @category layers
|
|
81
|
+
* @since 4.0.0
|
|
44
82
|
*/
|
|
45
83
|
export const layerSocketServer: Layer.Layer<
|
|
46
84
|
SocketServer.SocketServer,
|
|
@@ -48,9 +86,9 @@ export const layerSocketServer: Layer.Layer<
|
|
|
48
86
|
ShardingConfig.ShardingConfig
|
|
49
87
|
> = Effect.gen(function*() {
|
|
50
88
|
const config = yield* ShardingConfig.ShardingConfig
|
|
51
|
-
const listenAddress = config.runnerListenAddress
|
|
52
|
-
if (listenAddress
|
|
89
|
+
const listenAddress = Option.orElse(config.runnerListenAddress, () => config.runnerAddress)
|
|
90
|
+
if (Option.isNone(listenAddress)) {
|
|
53
91
|
return yield* Effect.die("layerSocketServer: ShardingConfig.runnerListenAddress is None")
|
|
54
92
|
}
|
|
55
|
-
return NodeSocketServer.layer(listenAddress)
|
|
93
|
+
return NodeSocketServer.layer(listenAddress.value)
|
|
56
94
|
}).pipe(Layer.unwrap)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `NodeCrypto` module implements Effect's `Crypto` service with
|
|
3
|
+
* Node-compatible `node:crypto` APIs. It exports {@link make} as the concrete
|
|
4
|
+
* service value and {@link layer} for providing that service to programs that
|
|
5
|
+
* need cryptographic random bytes, UUID generation, random values, or SHA
|
|
6
|
+
* digests over `Uint8Array` input.
|
|
7
|
+
*
|
|
8
|
+
* **Common tasks**
|
|
9
|
+
*
|
|
10
|
+
* - Provide {@link layer} in Node-compatible platform packages and tests
|
|
11
|
+
* - Reuse {@link make} when a surrounding layer already manages service
|
|
12
|
+
* construction
|
|
13
|
+
* - Compute SHA-1, SHA-256, SHA-384, or SHA-512 digests through
|
|
14
|
+
* `effect/Crypto` after the layer is provided
|
|
15
|
+
*
|
|
16
|
+
* **Gotchas**
|
|
17
|
+
*
|
|
18
|
+
* - Random bytes come from `node:crypto.randomBytes`
|
|
19
|
+
* - Digests use `node:crypto.createHash`; hash failures become platform
|
|
20
|
+
* errors
|
|
21
|
+
* - SHA-1 is included for interoperability with existing protocols, not for
|
|
22
|
+
* new security-sensitive designs
|
|
23
|
+
*
|
|
24
|
+
* @since 1.0.0
|
|
25
|
+
*/
|
|
26
|
+
import * as EffectCrypto from "effect/Crypto"
|
|
27
|
+
import * as Effect from "effect/Effect"
|
|
28
|
+
import * as Layer from "effect/Layer"
|
|
29
|
+
import * as PlatformError from "effect/PlatformError"
|
|
30
|
+
import * as NodeCrypto from "node:crypto"
|
|
31
|
+
|
|
32
|
+
const toHashAlgorithm = (algorithm: EffectCrypto.DigestAlgorithm): string => {
|
|
33
|
+
switch (algorithm) {
|
|
34
|
+
case "SHA-1":
|
|
35
|
+
return "sha1"
|
|
36
|
+
case "SHA-256":
|
|
37
|
+
return "sha256"
|
|
38
|
+
case "SHA-384":
|
|
39
|
+
return "sha384"
|
|
40
|
+
case "SHA-512":
|
|
41
|
+
return "sha512"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const digest: EffectCrypto.Crypto["digest"] = (algorithm, data) =>
|
|
46
|
+
Effect.try({
|
|
47
|
+
try: () => Uint8Array.from(NodeCrypto.createHash(toHashAlgorithm(algorithm)).update(data).digest()),
|
|
48
|
+
catch: (cause) =>
|
|
49
|
+
PlatformError.systemError({
|
|
50
|
+
module: "Crypto",
|
|
51
|
+
method: "digest",
|
|
52
|
+
_tag: "Unknown",
|
|
53
|
+
description: "Could not compute digest",
|
|
54
|
+
cause
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The default Node.js Crypto service implementation.
|
|
60
|
+
*
|
|
61
|
+
* @category constructors
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
*/
|
|
64
|
+
export const make: EffectCrypto.Crypto = EffectCrypto.make({
|
|
65
|
+
randomBytes: NodeCrypto.randomBytes,
|
|
66
|
+
digest
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A layer that provides the Node.js Crypto service implementation.
|
|
71
|
+
*
|
|
72
|
+
* @category layers
|
|
73
|
+
* @since 1.0.0
|
|
74
|
+
*/
|
|
75
|
+
export const layer: Layer.Layer<EffectCrypto.Crypto> = Layer.succeed(EffectCrypto.Crypto, make)
|