@effect/platform-node-shared 4.0.0-beta.9 → 4.0.0-beta.91
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 +19 -5
- package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/dist/NodeChildProcessSpawner.js +77 -18
- 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 +33 -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 +68 -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 +117 -29
- package/src/NodeClusterSocket.ts +22 -8
- package/src/NodeCrypto.ts +60 -0
- package/src/NodeFileSystem.ts +40 -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 +89 -30
- package/src/NodeTerminal.ts +22 -4
- package/src/index.ts +65 -0
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.91",
|
|
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.91"
|
|
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.91"
|
|
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
|
-
* Layer
|
|
642
|
+
* Layer that provides 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 transforming
|
|
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,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)
|
package/src/NodeFileSystem.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shared Node-compatible implementation of Effect's `FileSystem` service.
|
|
3
|
+
*
|
|
4
|
+
* This module adapts Node's `node:fs`, `node:os`, and `node:path` APIs into a
|
|
5
|
+
* `FileSystem` layer for Effect programs running on Node-compatible runtimes.
|
|
6
|
+
* Platform packages use it to provide file and directory I/O, permissions,
|
|
7
|
+
* links, metadata, temporary files and directories, and file watching through
|
|
8
|
+
* the shared `FileSystem` service.
|
|
9
|
+
*
|
|
10
|
+
* @since 4.0.0
|
|
3
11
|
*/
|
|
4
12
|
import * as Cause from "effect/Cause"
|
|
5
13
|
import * as Effect from "effect/Effect"
|
|
@@ -232,7 +240,7 @@ const makeFile = (() => {
|
|
|
232
240
|
readonly fd: FileSystem.File.Descriptor
|
|
233
241
|
private readonly append: boolean
|
|
234
242
|
|
|
235
|
-
private position: bigint =
|
|
243
|
+
private position: bigint = BigInt(0)
|
|
236
244
|
|
|
237
245
|
constructor(
|
|
238
246
|
fd: FileSystem.File.Descriptor,
|
|
@@ -285,19 +293,19 @@ const makeFile = (() => {
|
|
|
285
293
|
const position = this.position
|
|
286
294
|
return Effect.map(
|
|
287
295
|
nodeReadAlloc(this.fd, { buffer, position }),
|
|
288
|
-
(bytesRead): Buffer
|
|
296
|
+
(bytesRead): Option.Option<Buffer> => {
|
|
289
297
|
if (bytesRead === 0) {
|
|
290
|
-
return
|
|
298
|
+
return Option.none()
|
|
291
299
|
}
|
|
292
300
|
|
|
293
301
|
this.position = position + BigInt(bytesRead)
|
|
294
302
|
if (bytesRead === sizeNumber) {
|
|
295
|
-
return buffer
|
|
303
|
+
return Option.some(buffer)
|
|
296
304
|
}
|
|
297
305
|
|
|
298
306
|
const dst = Buffer.allocUnsafeSlow(bytesRead)
|
|
299
307
|
buffer.copy(dst, 0, 0, bytesRead)
|
|
300
|
-
return dst
|
|
308
|
+
return Option.some(dst)
|
|
301
309
|
}
|
|
302
310
|
)
|
|
303
311
|
})
|
|
@@ -468,19 +476,19 @@ const makeFileInfo = (stat: NFS.Stats): FileSystem.File.Info => ({
|
|
|
468
476
|
stat.isSocket() ?
|
|
469
477
|
"Socket" :
|
|
470
478
|
"Unknown",
|
|
471
|
-
mtime: stat.mtime,
|
|
472
|
-
atime: stat.atime,
|
|
473
|
-
birthtime: stat.birthtime,
|
|
479
|
+
mtime: Option.fromNullishOr(stat.mtime),
|
|
480
|
+
atime: Option.fromNullishOr(stat.atime),
|
|
481
|
+
birthtime: Option.fromNullishOr(stat.birthtime),
|
|
474
482
|
dev: stat.dev,
|
|
475
|
-
rdev: stat.rdev,
|
|
476
|
-
ino: stat.ino,
|
|
483
|
+
rdev: Option.fromNullishOr(stat.rdev),
|
|
484
|
+
ino: Option.fromNullishOr(stat.ino),
|
|
477
485
|
mode: stat.mode,
|
|
478
|
-
nlink: stat.nlink,
|
|
479
|
-
uid: stat.uid,
|
|
480
|
-
gid: stat.gid,
|
|
486
|
+
nlink: Option.fromNullishOr(stat.nlink),
|
|
487
|
+
uid: Option.fromNullishOr(stat.uid),
|
|
488
|
+
gid: Option.fromNullishOr(stat.gid),
|
|
481
489
|
size: FileSystem.Size(stat.size),
|
|
482
|
-
blksize: FileSystem.Size(stat.blksize),
|
|
483
|
-
blocks: stat.blocks
|
|
490
|
+
blksize: stat.blksize !== undefined ? Option.some(FileSystem.Size(stat.blksize)) : Option.none(),
|
|
491
|
+
blocks: Option.fromNullishOr(stat.blocks)
|
|
484
492
|
})
|
|
485
493
|
const stat = (() => {
|
|
486
494
|
const nodeStat = effectify(
|
|
@@ -531,7 +539,9 @@ const watchNode = (path: string) =>
|
|
|
531
539
|
Stream.callback<FileSystem.WatchEvent, Error.PlatformError>((queue) =>
|
|
532
540
|
Effect.acquireRelease(
|
|
533
541
|
Effect.sync(() => {
|
|
534
|
-
const watcher = NFS.watch(path, {
|
|
542
|
+
const watcher = NFS.watch(path, {
|
|
543
|
+
recursive: true
|
|
544
|
+
}, (event, path) => {
|
|
535
545
|
if (!path) return
|
|
536
546
|
switch (event) {
|
|
537
547
|
case "rename": {
|
|
@@ -570,15 +580,14 @@ const watchNode = (path: string) =>
|
|
|
570
580
|
)
|
|
571
581
|
)
|
|
572
582
|
|
|
573
|
-
const watch = (backend: FileSystem.WatchBackend["Service"]
|
|
583
|
+
const watch = (backend: Option.Option<FileSystem.WatchBackend["Service"]>, path: string) =>
|
|
574
584
|
stat(path).pipe(
|
|
575
|
-
Effect.map((stat) =>
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}),
|
|
585
|
+
Effect.map((stat) =>
|
|
586
|
+
backend.pipe(
|
|
587
|
+
Option.flatMap((_) => _.register(path, stat)),
|
|
588
|
+
Option.getOrElse(() => watchNode(path))
|
|
589
|
+
)
|
|
590
|
+
),
|
|
582
591
|
Stream.unwrap
|
|
583
592
|
)
|
|
584
593
|
|
|
@@ -628,13 +637,16 @@ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend),
|
|
|
628
637
|
truncate,
|
|
629
638
|
utimes,
|
|
630
639
|
watch(path) {
|
|
631
|
-
return watch(
|
|
640
|
+
return watch(backend, path)
|
|
632
641
|
},
|
|
633
642
|
writeFile
|
|
634
643
|
}))
|
|
635
644
|
|
|
636
645
|
/**
|
|
637
|
-
*
|
|
638
|
-
*
|
|
646
|
+
* Provides the `FileSystem` service backed by Node filesystem APIs, including
|
|
647
|
+
* file operations, directory operations, links, metadata, and file watching.
|
|
648
|
+
*
|
|
649
|
+
* @category layers
|
|
650
|
+
* @since 4.0.0
|
|
639
651
|
*/
|
|
640
652
|
export const layer: Layer.Layer<FileSystem.FileSystem> = Layer.effect(FileSystem.FileSystem)(makeFileSystem)
|
package/src/NodePath.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node-backed provider for Effect's `Path` service.
|
|
3
|
+
*
|
|
4
|
+
* This module turns Node's `node:path` and `node:url` APIs into `Path` layers.
|
|
5
|
+
* `layer` uses the host platform path implementation, while `layerPosix` and
|
|
6
|
+
* `layerWin32` provide fixed POSIX and Windows variants. All three layers also
|
|
7
|
+
* include helpers for converting between file paths and file URLs.
|
|
8
|
+
*
|
|
9
|
+
* @since 4.0.0
|
|
3
10
|
*/
|
|
4
11
|
import * as Effect from "effect/Effect"
|
|
5
12
|
import * as Layer from "effect/Layer"
|
|
@@ -31,8 +38,11 @@ const toFileUrl = (path: string): Effect.Effect<URL, BadArgument> =>
|
|
|
31
38
|
})
|
|
32
39
|
|
|
33
40
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
41
|
+
* Provides the `Path` service using Node's POSIX path implementation plus
|
|
42
|
+
* file URL conversion helpers.
|
|
43
|
+
*
|
|
44
|
+
* @category layers
|
|
45
|
+
* @since 4.0.0
|
|
36
46
|
*/
|
|
37
47
|
export const layerPosix: Layer.Layer<Path> = Layer.succeed(Path)({
|
|
38
48
|
[TypeId]: TypeId,
|
|
@@ -42,8 +52,11 @@ export const layerPosix: Layer.Layer<Path> = Layer.succeed(Path)({
|
|
|
42
52
|
})
|
|
43
53
|
|
|
44
54
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
55
|
+
* Provides the `Path` service using Node's Windows path implementation plus
|
|
56
|
+
* file URL conversion helpers.
|
|
57
|
+
*
|
|
58
|
+
* @category layers
|
|
59
|
+
* @since 4.0.0
|
|
47
60
|
*/
|
|
48
61
|
export const layerWin32: Layer.Layer<Path> = Layer.succeed(Path)({
|
|
49
62
|
[TypeId]: TypeId,
|
|
@@ -53,8 +66,11 @@ export const layerWin32: Layer.Layer<Path> = Layer.succeed(Path)({
|
|
|
53
66
|
})
|
|
54
67
|
|
|
55
68
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
69
|
+
* Provides the default `Path` service using the host platform's Node path
|
|
70
|
+
* implementation plus file URL conversion helpers.
|
|
71
|
+
*
|
|
72
|
+
* @category layers
|
|
73
|
+
* @since 4.0.0
|
|
58
74
|
*/
|
|
59
75
|
export const layer: Layer.Layer<Path> = Layer.succeed(Path)({
|
|
60
76
|
[TypeId]: TypeId,
|