@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
@@ -1,5 +1,36 @@
1
1
  /**
2
- * @since 1.0.0
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
+ * **Mental model**
11
+ *
12
+ * {@link layer} installs a process-backed `FileSystem` service. Each operation
13
+ * delegates to the corresponding Node filesystem API, then maps Node failures
14
+ * into `PlatformError` values and invalid arguments into `BadArgument` failures.
15
+ * Paths keep Node's normal behavior: relative paths resolve from the current
16
+ * working directory and platform path rules still apply.
17
+ *
18
+ * **Common tasks**
19
+ *
20
+ * Provide {@link layer} at the Node runtime boundary, then depend on the
21
+ * `FileSystem` service from application code. Use the service for ordinary
22
+ * reads and writes, directory management, metadata inspection, links, temporary
23
+ * resources, and file watching without importing Node's `fs` APIs directly.
24
+ *
25
+ * **Gotchas**
26
+ *
27
+ * Open files are scoped resources with tracked read and write positions; append
28
+ * mode lets the operating system choose the write offset. File watching follows
29
+ * `node:fs.watch` semantics unless a custom watch backend is supplied, so
30
+ * recursive support, event coalescing, and reported paths vary by runtime and
31
+ * platform.
32
+ *
33
+ * @since 4.0.0
3
34
  */
4
35
  import * as Cause from "effect/Cause"
5
36
  import * as Effect from "effect/Effect"
@@ -232,7 +263,7 @@ const makeFile = (() => {
232
263
  readonly fd: FileSystem.File.Descriptor
233
264
  private readonly append: boolean
234
265
 
235
- private position: bigint = 0n
266
+ private position: bigint = BigInt(0)
236
267
 
237
268
  constructor(
238
269
  fd: FileSystem.File.Descriptor,
@@ -285,19 +316,19 @@ const makeFile = (() => {
285
316
  const position = this.position
286
317
  return Effect.map(
287
318
  nodeReadAlloc(this.fd, { buffer, position }),
288
- (bytesRead): Buffer | undefined => {
319
+ (bytesRead): Option.Option<Buffer> => {
289
320
  if (bytesRead === 0) {
290
- return undefined
321
+ return Option.none()
291
322
  }
292
323
 
293
324
  this.position = position + BigInt(bytesRead)
294
325
  if (bytesRead === sizeNumber) {
295
- return buffer
326
+ return Option.some(buffer)
296
327
  }
297
328
 
298
329
  const dst = Buffer.allocUnsafeSlow(bytesRead)
299
330
  buffer.copy(dst, 0, 0, bytesRead)
300
- return dst
331
+ return Option.some(dst)
301
332
  }
302
333
  )
303
334
  })
@@ -468,19 +499,19 @@ const makeFileInfo = (stat: NFS.Stats): FileSystem.File.Info => ({
468
499
  stat.isSocket() ?
469
500
  "Socket" :
470
501
  "Unknown",
471
- mtime: stat.mtime,
472
- atime: stat.atime,
473
- birthtime: stat.birthtime,
502
+ mtime: Option.fromNullishOr(stat.mtime),
503
+ atime: Option.fromNullishOr(stat.atime),
504
+ birthtime: Option.fromNullishOr(stat.birthtime),
474
505
  dev: stat.dev,
475
- rdev: stat.rdev,
476
- ino: stat.ino,
506
+ rdev: Option.fromNullishOr(stat.rdev),
507
+ ino: Option.fromNullishOr(stat.ino),
477
508
  mode: stat.mode,
478
- nlink: stat.nlink,
479
- uid: stat.uid,
480
- gid: stat.gid,
509
+ nlink: Option.fromNullishOr(stat.nlink),
510
+ uid: Option.fromNullishOr(stat.uid),
511
+ gid: Option.fromNullishOr(stat.gid),
481
512
  size: FileSystem.Size(stat.size),
482
- blksize: FileSystem.Size(stat.blksize),
483
- blocks: stat.blocks
513
+ blksize: stat.blksize !== undefined ? Option.some(FileSystem.Size(stat.blksize)) : Option.none(),
514
+ blocks: Option.fromNullishOr(stat.blocks)
484
515
  })
485
516
  const stat = (() => {
486
517
  const nodeStat = effectify(
@@ -531,7 +562,9 @@ const watchNode = (path: string) =>
531
562
  Stream.callback<FileSystem.WatchEvent, Error.PlatformError>((queue) =>
532
563
  Effect.acquireRelease(
533
564
  Effect.sync(() => {
534
- const watcher = NFS.watch(path, {}, (event, path) => {
565
+ const watcher = NFS.watch(path, {
566
+ recursive: true
567
+ }, (event, path) => {
535
568
  if (!path) return
536
569
  switch (event) {
537
570
  case "rename": {
@@ -570,15 +603,14 @@ const watchNode = (path: string) =>
570
603
  )
571
604
  )
572
605
 
573
- const watch = (backend: FileSystem.WatchBackend["Service"] | undefined, path: string) =>
606
+ const watch = (backend: Option.Option<FileSystem.WatchBackend["Service"]>, path: string) =>
574
607
  stat(path).pipe(
575
- Effect.map((stat) => {
576
- if (backend) {
577
- const stream = backend.register(path, stat)
578
- if (stream) return stream
579
- }
580
- return watchNode(path)
581
- }),
608
+ Effect.map((stat) =>
609
+ backend.pipe(
610
+ Option.flatMap((_) => _.register(path, stat)),
611
+ Option.getOrElse(() => watchNode(path))
612
+ )
613
+ ),
582
614
  Stream.unwrap
583
615
  )
584
616
 
@@ -628,13 +660,16 @@ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend),
628
660
  truncate,
629
661
  utimes,
630
662
  watch(path) {
631
- return watch(Option.getOrUndefined(backend), path)
663
+ return watch(backend, path)
632
664
  },
633
665
  writeFile
634
666
  }))
635
667
 
636
668
  /**
637
- * @since 1.0.0
638
- * @category Layers
669
+ * Provides the `FileSystem` service backed by Node filesystem APIs, including
670
+ * file operations, directory operations, links, metadata, and file watching.
671
+ *
672
+ * @category layers
673
+ * @since 4.0.0
639
674
  */
640
675
  export const layer: Layer.Layer<FileSystem.FileSystem> = Layer.effect(FileSystem.FileSystem)(makeFileSystem)
package/src/NodePath.ts CHANGED
@@ -1,5 +1,28 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node-backed provider for Effect's `Path` service.
3
+ *
4
+ * This module turns Node's `node:path` and `node:url` APIs into `Layer`s for
5
+ * programs that depend on `Path`. Use it when code should receive path
6
+ * operations from the Effect environment instead of importing `node:path`
7
+ * directly, including configuration loading, filesystem composition, and file
8
+ * URL conversion.
9
+ *
10
+ * **Mental model**
11
+ *
12
+ * `layer` follows the platform semantics of the current Node runtime. The
13
+ * `layerPosix` and `layerWin32` variants pin the syntax rules to POSIX or
14
+ * Windows, which is useful for deterministic parsing, formatting, and tests.
15
+ * All three layers include `fromFileUrl` and `toFileUrl` behavior backed by
16
+ * Node's URL conversion functions.
17
+ *
18
+ * **Gotchas**
19
+ *
20
+ * Path operations are syntactic: they normalize separators, roots, drive
21
+ * letters, UNC segments, extensions, and relative segments without checking the
22
+ * filesystem. File URL conversion follows Node's validation and encoding
23
+ * rules, and invalid conversions fail with `BadArgument`.
24
+ *
25
+ * @since 4.0.0
3
26
  */
4
27
  import * as Effect from "effect/Effect"
5
28
  import * as Layer from "effect/Layer"
@@ -31,8 +54,11 @@ const toFileUrl = (path: string): Effect.Effect<URL, BadArgument> =>
31
54
  })
32
55
 
33
56
  /**
34
- * @since 1.0.0
35
- * @category Layers
57
+ * Provides the `Path` service using Node's POSIX path implementation plus
58
+ * file URL conversion helpers.
59
+ *
60
+ * @category layers
61
+ * @since 4.0.0
36
62
  */
37
63
  export const layerPosix: Layer.Layer<Path> = Layer.succeed(Path)({
38
64
  [TypeId]: TypeId,
@@ -42,8 +68,11 @@ export const layerPosix: Layer.Layer<Path> = Layer.succeed(Path)({
42
68
  })
43
69
 
44
70
  /**
45
- * @since 1.0.0
46
- * @category Layers
71
+ * Provides the `Path` service using Node's Windows path implementation plus
72
+ * file URL conversion helpers.
73
+ *
74
+ * @category layers
75
+ * @since 4.0.0
47
76
  */
48
77
  export const layerWin32: Layer.Layer<Path> = Layer.succeed(Path)({
49
78
  [TypeId]: TypeId,
@@ -53,8 +82,11 @@ export const layerWin32: Layer.Layer<Path> = Layer.succeed(Path)({
53
82
  })
54
83
 
55
84
  /**
56
- * @since 1.0.0
57
- * @category Layers
85
+ * Provides the default `Path` service using the host platform's Node path
86
+ * implementation plus file URL conversion helpers.
87
+ *
88
+ * @category layers
89
+ * @since 4.0.0
58
90
  */
59
91
  export const layer: Layer.Layer<Path> = Layer.succeed(Path)({
60
92
  [TypeId]: TypeId,
@@ -1,17 +1,54 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node-compatible process runner for Effect programs.
3
+ *
4
+ * This module provides the shared `runMain` implementation used by
5
+ * Node-compatible platform packages. Use it at the outer edge of a CLI, script,
6
+ * worker, server, or test harness when a single Effect should become the main
7
+ * fiber for the process while still following Node signal and exit-code
8
+ * conventions.
9
+ *
10
+ * **Mental model**
11
+ *
12
+ * `runMain` starts the supplied Effect as the process root. While that fiber is
13
+ * running, `SIGINT` and `SIGTERM` are translated into fiber interruption so
14
+ * scoped resources and finalizers get a chance to run. After the fiber exits,
15
+ * the signal listeners are removed and the configured teardown decides the
16
+ * process exit code.
17
+ *
18
+ * **Common tasks**
19
+ *
20
+ * - Launch a command-line program or long-running service from an Effect.
21
+ * - Share the same main-runner behavior across Node-compatible packages.
22
+ * - Customize teardown or runtime error reporting with the `runMain` options.
23
+ *
24
+ * **Gotchas**
25
+ *
26
+ * Clean success lets the Node event loop drain naturally instead of forcing
27
+ * `process.exit(0)`. Signal-triggered interruption or a non-zero teardown code
28
+ * calls `process.exit`, so long-lived handles should be acquired in Effect
29
+ * scopes and released by finalizers.
30
+ *
31
+ * @since 4.0.0
3
32
  */
4
33
  import type { Effect } from "effect/Effect"
5
34
  import * as Runtime from "effect/Runtime"
6
35
 
7
36
  /**
8
- * @since 1.0.0
9
- * @category Run main
37
+ * Runs an Effect as the Node process main program, interrupting the fiber on
38
+ * `SIGINT` or `SIGTERM` and invoking the configured teardown to determine the
39
+ * process exit code.
40
+ *
41
+ * @category running
42
+ * @since 4.0.0
10
43
  */
11
44
  export const runMain: {
12
45
  /**
13
- * @since 1.0.0
14
- * @category Run main
46
+ * Runs an Effect as the Node process main program, interrupting the fiber on
47
+ * `SIGINT` or `SIGTERM` and invoking the configured teardown to determine the
48
+ * process exit code.
49
+ *
50
+ * @category running
51
+ * @since 4.0.0
15
52
  */
16
53
  (
17
54
  options?: {
@@ -20,8 +57,12 @@ export const runMain: {
20
57
  }
21
58
  ): <E, A>(effect: Effect<A, E>) => void
22
59
  /**
23
- * @since 1.0.0
24
- * @category Run main
60
+ * Runs an Effect as the Node process main program, interrupting the fiber on
61
+ * `SIGINT` or `SIGTERM` and invoking the configured teardown to determine the
62
+ * process exit code.
63
+ *
64
+ * @category running
65
+ * @since 4.0.0
25
66
  */
26
67
  <E, A>(
27
68
  effect: Effect<A, E>,
@@ -37,10 +78,8 @@ export const runMain: {
37
78
  let receivedSignal = false
38
79
 
39
80
  fiber.addObserver((exit) => {
40
- if (!receivedSignal) {
41
- process.removeListener("SIGINT", onSigint)
42
- process.removeListener("SIGTERM", onSigint)
43
- }
81
+ process.removeListener("SIGINT", onSigint)
82
+ process.removeListener("SIGTERM", onSigint)
44
83
  teardown(exit, (code) => {
45
84
  if (receivedSignal || code !== 0) {
46
85
  process.exit(code)
@@ -50,8 +89,6 @@ export const runMain: {
50
89
 
51
90
  function onSigint() {
52
91
  receivedSignal = true
53
- process.removeListener("SIGINT", onSigint)
54
- process.removeListener("SIGTERM", onSigint)
55
92
  fiber.interruptUnsafe(fiber.id)
56
93
  }
57
94
 
package/src/NodeSink.ts CHANGED
@@ -1,5 +1,37 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Sink adapters for writing Effect stream chunks into Node writable streams.
3
+ *
4
+ * This module is the writable-stream boundary for Node APIs: push Effect
5
+ * `Stream` or `Channel` chunks into file streams, HTTP request or response
6
+ * bodies, process stdio, sockets, and transform inputs such as compression or
7
+ * encryption streams. It exposes a `Sink` constructor for ordinary stream
8
+ * pipelines plus lower-level `Channel` and pull helpers used by other Node
9
+ * stream adapters.
10
+ *
11
+ * **Mental model**
12
+ *
13
+ * {@link fromWritable} builds a `Sink` for normal stream pipelines.
14
+ * {@link fromWritableChannel} exposes the same bridge as a `Channel`, and
15
+ * {@link pullIntoWritable} is the lower-level loop for adapters that already
16
+ * have a pull and a Node writable. All three write chunks in order and follow
17
+ * Node writable backpressure.
18
+ *
19
+ * **Common tasks**
20
+ *
21
+ * Use {@link fromWritable} to pipe an Effect stream into a Node destination.
22
+ * Use {@link fromWritableChannel} when composing at the `Channel` level. Use
23
+ * {@link pullIntoWritable} inside custom Node stream adapters that need direct
24
+ * control over the upstream pull.
25
+ *
26
+ * **Gotchas**
27
+ *
28
+ * When `write` returns `false`, pulling pauses until `drain` so upstream
29
+ * producers do not overrun the writable buffer. Writable `error` events are
30
+ * mapped through `onError`, and the writable is ended and awaited via `finish`
31
+ * when upstream completes unless `endOnDone` is `false`. Use `endOnDone: false`
32
+ * for externally owned or long-lived writables.
33
+ *
34
+ * @since 4.0.0
3
35
  */
4
36
  import type { NonEmptyReadonlyArray } from "effect/Array"
5
37
  import * as Cause from "effect/Cause"
@@ -11,8 +43,12 @@ import * as Sink from "effect/Sink"
11
43
  import type { Writable } from "node:stream"
12
44
 
13
45
  /**
46
+ * Creates a `Sink` that writes chunks to a Node writable stream, respecting
47
+ * backpressure, mapping writable errors with `onError`, and ending the stream
48
+ * on completion unless `endOnDone` is `false`.
49
+ *
14
50
  * @category constructors
15
- * @since 1.0.0
51
+ * @since 4.0.0
16
52
  */
17
53
  export const fromWritable = <E, A = Uint8Array | string>(
18
54
  options: {
@@ -25,8 +61,12 @@ export const fromWritable = <E, A = Uint8Array | string>(
25
61
  Sink.fromChannel(Channel.mapDone(fromWritableChannel<never, E, A>(options), (_) => [_]))
26
62
 
27
63
  /**
64
+ * Creates a `Channel` that pulls chunks from upstream and writes them to a
65
+ * Node writable stream, respecting backpressure and optionally ending the
66
+ * writable when upstream is done.
67
+ *
28
68
  * @category constructors
29
- * @since 1.0.0
69
+ * @since 4.0.0
30
70
  */
31
71
  export const fromWritableChannel = <IE, E, A = Uint8Array | string>(
32
72
  options: {
@@ -42,7 +82,12 @@ export const fromWritableChannel = <IE, E, A = Uint8Array | string>(
42
82
  })
43
83
 
44
84
  /**
45
- * @since 1.0.0
85
+ * Repeatedly pulls non-empty chunks and writes them to a Node writable stream,
86
+ * waiting for `drain` when needed, failing on writable errors, and ending the
87
+ * writable on upstream completion unless disabled.
88
+ *
89
+ * @category converting
90
+ * @since 4.0.0
46
91
  */
47
92
  export const pullIntoWritable = <A, IE, E>(options: {
48
93
  readonly pull: Pull.Pull<NonEmptyReadonlyArray<A>, IE, unknown>
@@ -66,6 +111,13 @@ export const pullIntoWritable = <A, IE, E>(options: {
66
111
  })
67
112
  }),
68
113
  Effect.forever({ disableYield: true }),
114
+ Effect.raceFirst(Effect.callback<never, E>((resume) => {
115
+ const onError = (error: unknown) => resume(Effect.fail(options.onError(error)))
116
+ options.writable.once("error", onError)
117
+ return Effect.sync(() => {
118
+ options.writable.off("error", onError)
119
+ })
120
+ })),
69
121
  options.endOnDone !== false ?
70
122
  Pull.catchDone((_) => {
71
123
  if ("closed" in options.writable && options.writable.closed) {
package/src/NodeSocket.ts CHANGED
@@ -1,8 +1,33 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node socket adapters for Effect sockets.
3
+ *
4
+ * This module opens `node:net` connections or wraps existing Node `Duplex`
5
+ * streams and presents them as `Socket.Socket` values, socket channels, or
6
+ * layers. It is the low-level bridge for TCP clients, Unix domain socket
7
+ * clients, and protocols that already expose a Node duplex stream.
8
+ *
9
+ * **Mental model**
10
+ *
11
+ * A socket acquired here is scoped. `makeNet` dials with
12
+ * `net.createConnection`, `fromDuplex` adapts any duplex returned by an Effect,
13
+ * `makeNetChannel` exposes the socket as a `Channel`, and `layerNet` provides
14
+ * it through the Effect environment. While a socket handler is running,
15
+ * `NetSocket` gives access to the underlying Node `net.Socket` for cases that
16
+ * need Node-specific operations.
17
+ *
18
+ * **Gotchas**
19
+ *
20
+ * `openTimeout` only limits opening the connection. Writes complete when Node
21
+ * accepts or flushes a chunk, close events are translated to `SocketError`
22
+ * values, and finalizers close or destroy the stream when the surrounding scope
23
+ * ends. Unix socket paths are supplied through `NetConnectOpts.path`, so use
24
+ * the same platform path rules as Node.
25
+ *
26
+ * @since 4.0.0
3
27
  */
4
28
  import type { Array } from "effect"
5
29
  import * as Channel from "effect/Channel"
30
+ import * as Context from "effect/Context"
6
31
  import * as Deferred from "effect/Deferred"
7
32
  import type * as Duration from "effect/Duration"
8
33
  import * as Effect from "effect/Effect"
@@ -12,28 +37,34 @@ import { identity } from "effect/Function"
12
37
  import * as Latch from "effect/Latch"
13
38
  import * as Layer from "effect/Layer"
14
39
  import * as Scope from "effect/Scope"
15
- import * as ServiceMap from "effect/ServiceMap"
16
40
  import * as Socket from "effect/unstable/socket/Socket"
17
41
  import * as Net from "node:net"
18
42
  import type { Duplex } from "node:stream"
19
43
 
20
44
  /**
21
- * @since 1.0.0
22
45
  * @category re-exports
46
+ * @since 4.0.0
23
47
  */
24
48
  export * as NodeWS from "ws"
25
49
 
26
50
  /**
27
- * @since 1.0.0
51
+ * Service tag for the underlying Node `net.Socket` associated with the current
52
+ * socket connection.
53
+ *
28
54
  * @category tags
55
+ * @since 4.0.0
29
56
  */
30
- export class NetSocket extends ServiceMap.Service<NetSocket, Net.Socket>()(
57
+ export class NetSocket extends Context.Service<NetSocket, Net.Socket>()(
31
58
  "@effect/platform-node/NodeSocket/NetSocket"
32
59
  ) {}
33
60
 
34
61
  /**
35
- * @since 1.0.0
62
+ * Opens a TCP connection with Node `net.createConnection` and exposes it as a
63
+ * `Socket.Socket`, supporting `openTimeout` and closing or destroying the
64
+ * socket when the enclosing scope is finalized.
65
+ *
36
66
  * @category constructors
67
+ * @since 4.0.0
37
68
  */
38
69
  export const makeNet = (
39
70
  options: Net.NetConnectOpts & {
@@ -41,11 +72,11 @@ export const makeNet = (
41
72
  }
42
73
  ): Effect.Effect<Socket.Socket> =>
43
74
  fromDuplex(
44
- Effect.servicesWith((services: ServiceMap.ServiceMap<Scope.Scope>) => {
75
+ Effect.contextWith((context: Context.Context<Scope.Scope>) => {
45
76
  let conn: Net.Socket | undefined
46
77
  return Effect.flatMap(
47
78
  Scope.addFinalizer(
48
- ServiceMap.get(services, Scope.Scope),
79
+ Context.get(context, Scope.Scope),
49
80
  Effect.sync(() => {
50
81
  if (!conn) return
51
82
  if (conn.closed === false) {
@@ -77,8 +108,12 @@ export const makeNet = (
77
108
  )
78
109
 
79
110
  /**
80
- * @since 1.0.0
111
+ * Adapts a Node `Duplex` into a `Socket.Socket`, wiring data events to socket
112
+ * handlers, providing a scoped writer, and mapping open, read, write, and close
113
+ * failures to `SocketError`.
114
+ *
81
115
  * @category constructors
116
+ * @since 4.0.0
82
117
  */
83
118
  export const fromDuplex = <RO>(
84
119
  open: Effect.Effect<Duplex, Socket.SocketError, RO>,
@@ -89,7 +124,7 @@ export const fromDuplex = <RO>(
89
124
  Effect.withFiber<Socket.Socket, never, Exclude<RO, Scope.Scope>>((fiber) => {
90
125
  let currentSocket: Duplex | undefined
91
126
  const latch = Latch.makeUnsafe(false)
92
- const openServices = fiber.services as ServiceMap.ServiceMap<RO>
127
+ const openServices = fiber.context as Context.Context<RO>
93
128
 
94
129
  const run = <R, E, _>(handler: (_: Uint8Array) => Effect.Effect<_, E, R> | void, opts?: {
95
130
  readonly onOpen?: Effect.Effect<void> | undefined
@@ -113,7 +148,7 @@ export const fromDuplex = <RO>(
113
148
  options?.openTimeout ?
114
149
  Effect.timeoutOrElse({
115
150
  duration: options.openTimeout,
116
- onTimeout: () =>
151
+ orElse: () =>
117
152
  Effect.fail(
118
153
  new Socket.SocketError({
119
154
  reason: new Socket.SocketOpenError({ kind: "Timeout", cause: new Error("Connection timed out") })
@@ -166,7 +201,7 @@ export const fromDuplex = <RO>(
166
201
  )
167
202
  }
168
203
  })).pipe(
169
- Effect.updateServices((input: ServiceMap.ServiceMap<R>) => ServiceMap.merge(openServices, input)),
204
+ Effect.updateContext((input: Context.Context<R>) => Context.merge(openServices, input)),
170
205
  Effect.onExit(() =>
171
206
  Effect.sync(() => {
172
207
  latch.closeUnsafe()
@@ -204,8 +239,7 @@ export const fromDuplex = <RO>(
204
239
  })
205
240
  )
206
241
 
207
- return Effect.succeed(Socket.Socket.of({
208
- [Socket.TypeId]: Socket.TypeId,
242
+ return Effect.succeed(Socket.make({
209
243
  run,
210
244
  runRaw: run,
211
245
  writer
@@ -213,8 +247,11 @@ export const fromDuplex = <RO>(
213
247
  })
214
248
 
215
249
  /**
216
- * @since 1.0.0
250
+ * Creates a `Channel` over a TCP socket, reading arrays of `Uint8Array`
251
+ * chunks and writing arrays of bytes, strings, or socket close events.
252
+ *
217
253
  * @category constructors
254
+ * @since 4.0.0
218
255
  */
219
256
  export const makeNetChannel = <IE = never>(
220
257
  options: Net.NetConnectOpts
@@ -230,8 +267,11 @@ export const makeNetChannel = <IE = never>(
230
267
  )
231
268
 
232
269
  /**
233
- * @since 1.0.0
270
+ * Provides a `Socket.Socket` by opening a TCP connection with the supplied
271
+ * Node `net` connection options.
272
+ *
234
273
  * @category layers
274
+ * @since 4.0.0
235
275
  */
236
276
  export const layerNet: (options: Net.NetConnectOpts) => Layer.Layer<
237
277
  Socket.Socket,