@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.
Files changed (66) hide show
  1. package/dist/NodeChildProcessSpawner.d.ts +18 -4
  2. package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
  3. package/dist/NodeChildProcessSpawner.js +76 -17
  4. package/dist/NodeChildProcessSpawner.js.map +1 -1
  5. package/dist/NodeClusterSocket.d.ts +10 -4
  6. package/dist/NodeClusterSocket.d.ts.map +1 -1
  7. package/dist/NodeClusterSocket.js +46 -8
  8. package/dist/NodeClusterSocket.js.map +1 -1
  9. package/dist/NodeCrypto.d.ts +42 -0
  10. package/dist/NodeCrypto.d.ts.map +1 -0
  11. package/dist/NodeCrypto.js +70 -0
  12. package/dist/NodeCrypto.js.map +1 -0
  13. package/dist/NodeFileSystem.d.ts +5 -2
  14. package/dist/NodeFileSystem.d.ts.map +1 -1
  15. package/dist/NodeFileSystem.js +56 -26
  16. package/dist/NodeFileSystem.js.map +1 -1
  17. package/dist/NodePath.d.ts +15 -6
  18. package/dist/NodePath.d.ts.map +1 -1
  19. package/dist/NodePath.js +39 -7
  20. package/dist/NodePath.js.map +1 -1
  21. package/dist/NodeRuntime.d.ts +48 -7
  22. package/dist/NodeRuntime.d.ts.map +1 -1
  23. package/dist/NodeRuntime.js +8 -8
  24. package/dist/NodeRuntime.js.map +1 -1
  25. package/dist/NodeSink.d.ts +49 -4
  26. package/dist/NodeSink.d.ts.map +1 -1
  27. package/dist/NodeSink.js +23 -4
  28. package/dist/NodeSink.js.map +1 -1
  29. package/dist/NodeSocket.d.ts +50 -9
  30. package/dist/NodeSocket.d.ts.map +1 -1
  31. package/dist/NodeSocket.js +31 -15
  32. package/dist/NodeSocket.js.map +1 -1
  33. package/dist/NodeSocketServer.d.ts +24 -7
  34. package/dist/NodeSocketServer.d.ts.map +1 -1
  35. package/dist/NodeSocketServer.js +28 -11
  36. package/dist/NodeSocketServer.js.map +1 -1
  37. package/dist/NodeStdio.d.ts +6 -5
  38. package/dist/NodeStdio.d.ts.map +1 -1
  39. package/dist/NodeStdio.js +46 -7
  40. package/dist/NodeStdio.js.map +1 -1
  41. package/dist/NodeStream.d.ts +93 -20
  42. package/dist/NodeStream.d.ts.map +1 -1
  43. package/dist/NodeStream.js +85 -23
  44. package/dist/NodeStream.js.map +1 -1
  45. package/dist/NodeTerminal.d.ts +9 -2
  46. package/dist/NodeTerminal.d.ts.map +1 -1
  47. package/dist/NodeTerminal.js +13 -3
  48. package/dist/NodeTerminal.js.map +1 -1
  49. package/dist/index.d.ts +52 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +53 -0
  52. package/dist/index.js.map +1 -0
  53. package/package.json +7 -6
  54. package/src/NodeChildProcessSpawner.ts +116 -28
  55. package/src/NodeClusterSocket.ts +46 -8
  56. package/src/NodeCrypto.ts +75 -0
  57. package/src/NodeFileSystem.ts +63 -28
  58. package/src/NodePath.ts +39 -7
  59. package/src/NodeRuntime.ts +50 -13
  60. package/src/NodeSink.ts +56 -4
  61. package/src/NodeSocket.ts +56 -16
  62. package/src/NodeSocketServer.ts +64 -12
  63. package/src/NodeStdio.ts +64 -23
  64. package/src/NodeStream.ts +106 -30
  65. package/src/NodeTerminal.ts +44 -4
  66. package/src/index.ts +65 -0
@@ -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.7",
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.7"
49
+ "effect": "^4.0.0-beta.71"
50
50
  },
51
51
  "devDependencies": {
52
- "@types/node": "^25.2.0",
53
- "tar": "^7.5.7",
54
- "effect": "^4.0.0-beta.7"
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.19.0"
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 `ChildProcessSpawner`.
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 { ChildProcessSpawner, ExitCode, makeHandle, ProcessId } from "effect/unstable/process/ChildProcessSpawner"
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
- onTimeout: () => kill(command, childProcess, "SIGKILL")
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
- let handle = spawnCommand(root)
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.map(handle, (h) => getSourceStream(h, options.from))
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
- handle = spawnCommand(ChildProcess.make(command.command, command.args, {
517
- ...command.options,
518
- stdin: { ...stdinConfig, stream: sourceStream }
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
- handle = spawnCommand(ChildProcess.make(command.command, command.args, {
527
- ...command.options,
528
- additionalFds: {
529
- ...existingFds,
530
- [fdName]: { type: "input" as const, stream: sourceStream }
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
- handle = spawnCommand(ChildProcess.make(command.command, command.args, {
536
- ...command.options,
537
- stdin: { ...stdinConfig, stream: sourceStream }
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
- return yield* handle
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 ChildProcessSpawner.of({
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
@@ -1,8 +1,40 @@
1
1
  /**
2
- * @since 1.0.0
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
- * @since 1.0.0
17
- * @category Layers
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
- * @since 1.0.0
43
- * @category Layers
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 ?? config.runnerAddress
52
- if (listenAddress === undefined) {
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)