@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,7 +1,43 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node socket server adapters for Effect's unstable socket server API.
3
+ *
4
+ * This module turns `node:net` TCP or Unix-domain servers and `ws` WebSocket
5
+ * servers into scoped `SocketServer.SocketServer` services. Use the TCP
6
+ * constructors when handlers should receive a `Socket.Socket` backed by a Node
7
+ * `net.Socket`; use the WebSocket constructors when handlers should receive a
8
+ * `Socket.Socket` backed by `ws` and have access to the per-connection
9
+ * WebSocket and `IncomingMessage` services.
10
+ *
11
+ * **Mental model**
12
+ *
13
+ * Calling {@link make} or {@link makeWebSocket} starts the underlying server in
14
+ * the current scope and returns the bound address. `run` installs the
15
+ * connection handler for that server, forks each accepted connection into a
16
+ * child scope, and closes those fibers when `run` finalizes. Connections
17
+ * accepted before `run` is installed are queued and then handed to the handler.
18
+ *
19
+ * **Common tasks**
20
+ *
21
+ * - Bind TCP ports or Unix socket paths with {@link make} or {@link layer}.
22
+ * - Expose `ws` WebSocket endpoints with {@link makeWebSocket} or
23
+ * {@link layerWebSocket}.
24
+ * - Read the returned `address` after binding port `0` or a wildcard host.
25
+ * - Inspect `NodeSocket.NetSocket`, `Socket.WebSocket`, or
26
+ * {@link IncomingMessage} from handler context when lower-level details are
27
+ * needed.
28
+ *
29
+ * **Gotchas**
30
+ *
31
+ * A constructor listens before it returns, so open errors fail construction
32
+ * rather than the first `run`. The server lifetime belongs to the scope that
33
+ * created it, while each `run` call owns its connection fibers. WebSocket
34
+ * handlers run with the `ws` connection and Node request in context, but TCP
35
+ * handlers expose only the underlying Node socket context.
36
+ *
37
+ * @since 4.0.0
3
38
  */
4
39
  import type { Cause } from "effect/Cause"
40
+ import * as Context from "effect/Context"
5
41
  import * as Deferred from "effect/Deferred"
6
42
  import * as Effect from "effect/Effect"
7
43
  import * as Exit from "effect/Exit"
@@ -11,7 +47,6 @@ import * as Function from "effect/Function"
11
47
  import * as Layer from "effect/Layer"
12
48
  import * as References from "effect/References"
13
49
  import * as Scope from "effect/Scope"
14
- import * as ServiceMap from "effect/ServiceMap"
15
50
  import * as Socket from "effect/unstable/socket/Socket"
16
51
  import * as SocketServer from "effect/unstable/socket/SocketServer"
17
52
  import type * as Http from "node:http"
@@ -20,17 +55,24 @@ import * as NodeSocket from "./NodeSocket.ts"
20
55
  import { NodeWS } from "./NodeSocket.ts"
21
56
 
22
57
  /**
23
- * @since 1.0.0
58
+ * Service tag for the Node `IncomingMessage` associated with the current
59
+ * WebSocket server connection.
60
+ *
24
61
  * @category tags
62
+ * @since 4.0.0
25
63
  */
26
- export class IncomingMessage extends ServiceMap.Service<
64
+ export class IncomingMessage extends Context.Service<
27
65
  IncomingMessage,
28
66
  Http.IncomingMessage
29
67
  >()("@effect/platform-node-shared/NodeSocketServer/IncomingMessage") {}
30
68
 
31
69
  /**
32
- * @since 1.0.0
70
+ * Creates a scoped TCP `SocketServer` from a Node `net.Server`, starts
71
+ * listening with the supplied options, queues pending connections until `run`
72
+ * is called, and closes the server when the scope ends.
73
+ *
33
74
  * @category constructors
75
+ * @since 4.0.0
34
76
  */
35
77
  export const make = Effect.fnUntraced(function*(
36
78
  options: Net.ServerOpts & Net.ListenOptions
@@ -69,7 +111,7 @@ export const make = Effect.fnUntraced(function*(
69
111
 
70
112
  const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
71
113
  const scope = yield* Scope.make()
72
- const services = ServiceMap.omit(Scope.Scope)(yield* Effect.services<R>()) as ServiceMap.ServiceMap<R>
114
+ const services = Context.omit(Scope.Scope)(yield* Effect.context<R>()) as Context.Context<R>
73
115
  const trackFiber = Fiber.runIn(scope)
74
116
  const prevOnConnection = onConnection
75
117
  onConnection = function(conn: Net.Socket) {
@@ -109,7 +151,7 @@ export const make = Effect.fnUntraced(function*(
109
151
  ),
110
152
  Effect.flatMap(handler),
111
153
  Effect.catchCause(reportUnhandledError),
112
- Effect.runForkWith(ServiceMap.add(services, NodeSocket.NetSocket, conn)),
154
+ Effect.runForkWith(Context.add(services, NodeSocket.NetSocket, conn)),
113
155
  trackFiber
114
156
  )
115
157
  }
@@ -144,8 +186,11 @@ export const make = Effect.fnUntraced(function*(
144
186
  })
145
187
 
146
188
  /**
147
- * @since 1.0.0
189
+ * Provides a TCP `SocketServer` by creating and managing a scoped Node
190
+ * `net.Server` with the supplied server and listen options.
191
+ *
148
192
  * @category layers
193
+ * @since 4.0.0
149
194
  */
150
195
  export const layer: (
151
196
  options: Net.ServerOpts & Net.ListenOptions
@@ -155,8 +200,12 @@ export const layer: (
155
200
  > = Function.flow(make, Layer.effect(SocketServer.SocketServer))
156
201
 
157
202
  /**
158
- * @since 1.0.0
203
+ * Creates a scoped WebSocket `SocketServer` backed by the `ws` package,
204
+ * providing the WebSocket and its Node `IncomingMessage` to connection
205
+ * handlers and closing the server when the scope ends.
206
+ *
159
207
  * @category constructors
208
+ * @since 4.0.0
160
209
  */
161
210
  export const makeWebSocket: (
162
211
  options: NodeWS.ServerOptions<typeof NodeWS.WebSocket, typeof Http.IncomingMessage>
@@ -202,7 +251,7 @@ export const makeWebSocket: (
202
251
 
203
252
  const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
204
253
  const scope = yield* Scope.make()
205
- const services = ServiceMap.omit(Scope.Scope)(yield* Effect.services<R>()) as ServiceMap.ServiceMap<R>
254
+ const services = Context.omit(Scope.Scope)(yield* Effect.context<R>()) as Context.Context<R>
206
255
  const trackFiber = Fiber.runIn(scope)
207
256
  const prevOnConnection = onConnection
208
257
  onConnection = function(conn: globalThis.WebSocket, req: Http.IncomingMessage) {
@@ -221,7 +270,7 @@ export const makeWebSocket: (
221
270
  ),
222
271
  Effect.flatMap(handler),
223
272
  Effect.catchCause(reportUnhandledError),
224
- Effect.runForkWith(ServiceMap.makeUnsafe(map)),
273
+ Effect.runForkWith(Context.makeUnsafe(map)),
225
274
  trackFiber
226
275
  )
227
276
  }
@@ -255,8 +304,11 @@ export const makeWebSocket: (
255
304
  })
256
305
 
257
306
  /**
258
- * @since 1.0.0
307
+ * Provides a WebSocket `SocketServer` backed by the `ws` package and managed
308
+ * with the supplied server options.
309
+ *
259
310
  * @category layers
311
+ * @since 4.0.0
260
312
  */
261
313
  export const layerWebSocket: (
262
314
  options: NodeSocket.NodeWS.ServerOptions<typeof NodeSocket.NodeWS.WebSocket, typeof Http.IncomingMessage>
package/src/NodeStdio.ts CHANGED
@@ -1,6 +1,38 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Shared Node.js implementation of the Effect `Stdio` service.
3
+ *
4
+ * `NodeStdio` provides {@link Stdio.Stdio} from the current Node process. The
5
+ * exported {@link layer} reads command-line arguments from `process.argv`,
6
+ * consumes input from `process.stdin`, and writes normal and error output to
7
+ * `process.stdout` and `process.stderr`. It is the shared implementation used
8
+ * by Node platform packages for CLIs, scripts, command runners, test harnesses,
9
+ * and other process-oriented programs.
10
+ *
11
+ * **Mental model**
12
+ *
13
+ * Application code should depend on `Stdio`; this module decides that the
14
+ * backing resources are the global Node process handles. The layer does not
15
+ * create new streams and does not claim exclusive ownership of the existing
16
+ * ones.
17
+ *
18
+ * **Common tasks**
19
+ *
20
+ * Use {@link layer} when an Effect program needs process arguments, stdin,
21
+ * stdout, or stderr through the service environment. Pair it with terminal
22
+ * services when the same program also needs line editing, raw key input, or
23
+ * terminal dimensions.
24
+ *
25
+ * **Gotchas**
26
+ *
27
+ * Process stdio streams are shared with the rest of the Node process. This
28
+ * layer leaves stdin open and does not end stdout or stderr by default, avoiding
29
+ * accidental closure of handles that other code may still use. The streams may
30
+ * be pipes, files, or TTYs; terminal-specific behavior such as raw mode, echo,
31
+ * color detection, and cursor movement belongs with terminal APIs.
32
+ *
33
+ * @since 4.0.0
3
34
  */
35
+ import * as Effect from "effect/Effect"
4
36
  import * as Layer from "effect/Layer"
5
37
  import { systemError } from "effect/PlatformError"
6
38
  import * as Stdio from "effect/Stdio"
@@ -8,32 +40,41 @@ import { fromWritable } from "./NodeSink.ts"
8
40
  import { fromReadable } from "./NodeStream.ts"
9
41
 
10
42
  /**
11
- * @category Layers
12
- * @since 1.0.0
43
+ * Provides `Stdio` from `process.argv`, `process.stdin`, `process.stdout`,
44
+ * and `process.stderr`; stdin remains open and stdout/stderr are not ended by
45
+ * default.
46
+ *
47
+ * @category layers
48
+ * @since 4.0.0
13
49
  */
14
50
  export const layer: Layer.Layer<Stdio.Stdio> = Layer.succeed(
15
51
  Stdio.Stdio,
16
52
  Stdio.make({
17
- stdout: fromWritable({
18
- evaluate: () => process.stdout,
19
- onError: (cause) =>
20
- systemError({
21
- module: "Stdio",
22
- method: "stdout",
23
- _tag: "Unknown",
24
- cause
25
- })
26
- }),
27
- stderr: fromWritable({
28
- evaluate: () => process.stderr,
29
- onError: (cause) =>
30
- systemError({
31
- module: "Stdio",
32
- method: "stderr",
33
- _tag: "Unknown",
34
- cause
35
- })
36
- }),
53
+ args: Effect.sync(() => process.argv.slice(2)),
54
+ stdout: (options) =>
55
+ fromWritable({
56
+ evaluate: () => process.stdout,
57
+ onError: (cause) =>
58
+ systemError({
59
+ module: "Stdio",
60
+ method: "stdout",
61
+ _tag: "Unknown",
62
+ cause
63
+ }),
64
+ endOnDone: options?.endOnDone ?? false
65
+ }),
66
+ stderr: (options) =>
67
+ fromWritable({
68
+ evaluate: () => process.stderr,
69
+ onError: (cause) =>
70
+ systemError({
71
+ module: "Stdio",
72
+ method: "stderr",
73
+ _tag: "Unknown",
74
+ cause
75
+ }),
76
+ endOnDone: options?.endOnDone ?? false
77
+ }),
37
78
  stdin: fromReadable({
38
79
  evaluate: () => process.stdin,
39
80
  onError: (cause) =>
package/src/NodeStream.ts CHANGED
@@ -1,9 +1,35 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Adapters between Node streams and Effect streams, channels, and readables.
3
+ *
4
+ * This module is the stream boundary for Node APIs: wrap `Readable` or
5
+ * `Duplex` values as Effect `Stream`s and `Channel`s, pipe an Effect stream
6
+ * through a Node duplex transform, expose an Effect `Stream` back to Node as a
7
+ * `Readable`, or collect bounded readable payloads into strings, array
8
+ * buffers, and `Uint8Array`s. Common sources include files, HTTP bodies, child
9
+ * process stdio, sockets, and compression or crypto transforms.
10
+ *
11
+ * **Mental model**
12
+ *
13
+ * Read adapters pull from Node's readable side into Effect. Duplex adapters
14
+ * write upstream Effect chunks to Node while reading transformed chunks back.
15
+ * `toReadable` runs an Effect stream from the caller's context, while
16
+ * `toReadableNever` is for streams that need no services.
17
+ *
18
+ * **Gotchas**
19
+ *
20
+ * Node backpressure is preserved: writes pause until `drain` before more input
21
+ * is pulled. Readables are destroyed on scope finalization by default, and
22
+ * duplex writable sides are ended when upstream completes unless configured
23
+ * otherwise. For externally owned or long-lived streams, choose `closeOnDone`
24
+ * and `endOnDone` deliberately; for collection helpers, set `maxBytes` when
25
+ * input size is not already bounded.
26
+ *
27
+ * @since 4.0.0
3
28
  */
4
29
  import * as Arr from "effect/Array"
5
30
  import * as Cause from "effect/Cause"
6
31
  import * as Channel from "effect/Channel"
32
+ import * as Context from "effect/Context"
7
33
  import * as Effect from "effect/Effect"
8
34
  import * as Exit from "effect/Exit"
9
35
  import * as Fiber from "effect/Fiber"
@@ -13,15 +39,18 @@ import * as Latch from "effect/Latch"
13
39
  import * as MutableRef from "effect/MutableRef"
14
40
  import * as Pull from "effect/Pull"
15
41
  import * as Scope from "effect/Scope"
16
- import * as ServiceMap from "effect/ServiceMap"
17
42
  import * as Stream from "effect/Stream"
18
43
  import type { Duplex } from "node:stream"
19
44
  import { Readable } from "node:stream"
20
45
  import { pullIntoWritable } from "./NodeSink.ts"
21
46
 
22
47
  /**
48
+ * Converts a Node readable stream into an Effect `Stream`, reading chunks with
49
+ * an optional chunk size, mapping stream errors with `onError`, and destroying
50
+ * the readable on completion unless `closeOnDone` is `false`.
51
+ *
23
52
  * @category constructors
24
- * @since 1.0.0
53
+ * @since 4.0.0
25
54
  */
26
55
  export const fromReadable = <A = Uint8Array, E = Cause.UnknownError>(options: {
27
56
  readonly evaluate: LazyArg<Readable | NodeJS.ReadableStream>
@@ -32,8 +61,12 @@ export const fromReadable = <A = Uint8Array, E = Cause.UnknownError>(options: {
32
61
  }): Stream.Stream<A, E> => Stream.fromChannel(fromReadableChannel<A, E>(options))
33
62
 
34
63
  /**
64
+ * Creates a `Channel` that pulls chunks from a Node readable stream, mapping
65
+ * errors with `onError` and destroying the readable on completion unless
66
+ * `closeOnDone` is `false`.
67
+ *
35
68
  * @category constructors
36
- * @since 1.0.0
69
+ * @since 4.0.0
37
70
  */
38
71
  export const fromReadableChannel = <A = Uint8Array, E = Cause.UnknownError>(options: {
39
72
  readonly evaluate: LazyArg<Readable | NodeJS.ReadableStream>
@@ -52,8 +85,12 @@ export const fromReadableChannel = <A = Uint8Array, E = Cause.UnknownError>(opti
52
85
  )
53
86
 
54
87
  /**
88
+ * Creates a `Channel` over a Node `Duplex`, writing upstream chunks with
89
+ * backpressure while emitting chunks read from the duplex and optionally ending
90
+ * the writable side when upstream completes.
91
+ *
55
92
  * @category constructors
56
- * @since 1.0.0
93
+ * @since 4.0.0
57
94
  */
58
95
  export const fromDuplex = <IE, I = Uint8Array, O = Uint8Array, E = Cause.UnknownError>(
59
96
  options: {
@@ -95,13 +132,19 @@ export const fromDuplex = <IE, I = Uint8Array, O = Uint8Array, E = Cause.Unknown
95
132
  })
96
133
 
97
134
  /**
135
+ * Pipes an Effect `Stream` through a Node `Duplex`, writing the stream's
136
+ * chunks to the duplex and emitting chunks read back from it.
137
+ *
98
138
  * @category combinators
99
- * @since 1.0.0
139
+ * @since 4.0.0
100
140
  */
101
141
  export const pipeThroughDuplex: {
102
142
  /**
143
+ * Pipes an Effect `Stream` through a Node `Duplex`, writing the stream's
144
+ * chunks to the duplex and emitting chunks read back from it.
145
+ *
103
146
  * @category combinators
104
- * @since 1.0.0
147
+ * @since 4.0.0
105
148
  */
106
149
  <B = Uint8Array, E2 = Cause.UnknownError>(
107
150
  options: {
@@ -114,8 +157,11 @@ export const pipeThroughDuplex: {
114
157
  }
115
158
  ): <R, E, A>(self: Stream.Stream<A, E, R>) => Stream.Stream<B, E2 | E, R>
116
159
  /**
160
+ * Pipes an Effect `Stream` through a Node `Duplex`, writing the stream's
161
+ * chunks to the duplex and emitting chunks read back from it.
162
+ *
117
163
  * @category combinators
118
- * @since 1.0.0
164
+ * @since 4.0.0
119
165
  */
120
166
  <R, E, A, B = Uint8Array, E2 = Cause.UnknownError>(
121
167
  self: Stream.Stream<A, E, R>,
@@ -145,18 +191,27 @@ export const pipeThroughDuplex: {
145
191
  ))
146
192
 
147
193
  /**
194
+ * Pipes a stream of strings or bytes through a Node `Duplex` using default
195
+ * options and `Cause.UnknownError` for stream failures.
196
+ *
148
197
  * @category combinators
149
- * @since 1.0.0
198
+ * @since 4.0.0
150
199
  */
151
200
  export const pipeThroughSimple: {
152
201
  /**
202
+ * Pipes a stream of strings or bytes through a Node `Duplex` using default
203
+ * options and `Cause.UnknownError` for stream failures.
204
+ *
153
205
  * @category combinators
154
- * @since 1.0.0
206
+ * @since 4.0.0
155
207
  */
156
208
  (duplex: LazyArg<Duplex>): <R, E>(self: Stream.Stream<string | Uint8Array, E, R>) => Stream.Stream<Uint8Array, E | Cause.UnknownError, R>
157
209
  /**
210
+ * Pipes a stream of strings or bytes through a Node `Duplex` using default
211
+ * options and `Cause.UnknownError` for stream failures.
212
+ *
158
213
  * @category combinators
159
- * @since 1.0.0
214
+ * @since 4.0.0
160
215
  */
161
216
  <R, E>(self: Stream.Stream<string | Uint8Array, E, R>, duplex: LazyArg<Duplex>): Stream.Stream<Uint8Array, Cause.UnknownError | E, R>
162
217
  } = dual(2, <R, E>(
@@ -165,28 +220,39 @@ export const pipeThroughSimple: {
165
220
  ): Stream.Stream<Uint8Array, Cause.UnknownError | E, R> => pipeThroughDuplex(self, { evaluate: duplex }))
166
221
 
167
222
  /**
168
- * @since 1.0.0
169
- * @category conversions
223
+ * Converts an Effect `Stream` into a Node `Readable`, using the caller's
224
+ * Effect context to run the stream and destroying the readable if the stream
225
+ * fails.
226
+ *
227
+ * @category converting
228
+ * @since 4.0.0
170
229
  */
171
230
  export const toReadable = <E, R>(stream: Stream.Stream<string | Uint8Array, E, R>): Effect.Effect<Readable, never, R> =>
172
231
  Effect.map(
173
- Effect.services<R>(),
232
+ Effect.context<R>(),
174
233
  (context) => new StreamAdapter(context, stream)
175
234
  )
176
235
 
177
236
  /**
178
- * @since 1.0.0
179
- * @category conversions
237
+ * Converts a service-free Effect `Stream` into a Node `Readable` using an
238
+ * empty Effect context.
239
+ *
240
+ * @category converting
241
+ * @since 4.0.0
180
242
  */
181
243
  export const toReadableNever = <E>(stream: Stream.Stream<string | Uint8Array, E, never>): Readable =>
182
244
  new StreamAdapter(
183
- ServiceMap.empty(),
245
+ Context.empty(),
184
246
  stream
185
247
  )
186
248
 
187
249
  /**
188
- * @since 1.0.0
189
- * @category conversions
250
+ * Consumes a Node readable stream into a string using the selected encoding,
251
+ * failing through `onError` on stream errors or when `maxBytes` is exceeded
252
+ * and destroying the stream on interruption or failure.
253
+ *
254
+ * @category converting
255
+ * @since 4.0.0
190
256
  */
191
257
  export const toString = <E = Cause.UnknownError>(
192
258
  readable: LazyArg<Readable | NodeJS.ReadableStream>,
@@ -234,8 +300,12 @@ export const toString = <E = Cause.UnknownError>(
234
300
  }
235
301
 
236
302
  /**
237
- * @since 1.0.0
238
- * @category conversions
303
+ * Consumes a Node readable stream into an `ArrayBuffer`, failing through
304
+ * `onError` on stream errors or when `maxBytes` is exceeded and destroying the
305
+ * stream on interruption or failure.
306
+ *
307
+ * @category converting
308
+ * @since 4.0.0
239
309
  */
240
310
  export const toArrayBuffer = <E = Cause.UnknownError>(
241
311
  readable: LazyArg<Readable | NodeJS.ReadableStream>,
@@ -248,7 +318,7 @@ export const toArrayBuffer = <E = Cause.UnknownError>(
248
318
  const onError = options?.onError ?? defaultOnError
249
319
  return Effect.callback((resume) => {
250
320
  const stream = readable() as Readable
251
- let buffer = Buffer.alloc(0)
321
+ const buffers: Array<Uint8Array> = []
252
322
  let bytes = 0
253
323
  stream.once("error", (err) => {
254
324
  if ("closed" in stream && !stream.closed) {
@@ -257,13 +327,16 @@ export const toArrayBuffer = <E = Cause.UnknownError>(
257
327
  resume(Effect.fail(onError(err) as E))
258
328
  })
259
329
  stream.once("end", () => {
260
- if (buffer.buffer.byteLength === buffer.byteLength) {
261
- return resume(Effect.succeed(buffer.buffer))
330
+ const buffer = buffers.length === 1 ? buffers[0] : Buffer.concat(buffers)
331
+ if (buffer.byteOffset === 0 && buffer.buffer.byteLength === buffer.byteLength) {
332
+ return resume(Effect.succeed(buffer.buffer as ArrayBuffer))
262
333
  }
263
- resume(Effect.succeed(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)))
334
+ resume(
335
+ Effect.succeed(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer)
336
+ )
264
337
  })
265
338
  stream.on("data", (chunk) => {
266
- buffer = Buffer.concat([buffer, chunk])
339
+ buffers.push(chunk)
267
340
  bytes += chunk.length
268
341
  if (maxBytesNumber && bytes > maxBytesNumber) {
269
342
  resume(Effect.fail(onError(new Error("maxBytes exceeded")) as E))
@@ -278,8 +351,11 @@ export const toArrayBuffer = <E = Cause.UnknownError>(
278
351
  }
279
352
 
280
353
  /**
281
- * @since 1.0.0
282
- * @category conversions
354
+ * Consumes a Node readable stream into a `Uint8Array`, using the same error
355
+ * mapping and `maxBytes` handling as `toArrayBuffer`.
356
+ *
357
+ * @category converting
358
+ * @since 4.0.0
283
359
  */
284
360
  export const toUint8Array = <E = Cause.UnknownError>(
285
361
  readable: LazyArg<Readable | NodeJS.ReadableStream>,
@@ -361,7 +437,7 @@ class StreamAdapter<E, R> extends Readable {
361
437
  private fiber: Fiber.Fiber<void, E> | undefined = undefined
362
438
 
363
439
  constructor(
364
- context: ServiceMap.ServiceMap<R>,
440
+ context: Context.Context<R>,
365
441
  stream: Stream.Stream<Uint8Array | string, E, R>
366
442
  ) {
367
443
  super({})
@@ -379,7 +455,7 @@ class StreamAdapter<E, R> extends Readable {
379
455
  }
380
456
  }))).pipe(
381
457
  this.readLatch.whenOpen,
382
- Effect.provideServices(context),
458
+ Effect.provideContext(context),
383
459
  Effect.runFork
384
460
  )
385
461
  this.fiber.addObserver((exit) => {
@@ -1,9 +1,40 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Shared Node.js implementation of Effect's `Terminal` service.
3
+ *
4
+ * `NodeTerminal` adapts Node's `readline` APIs plus the current process'
5
+ * `stdin` and `stdout` streams into {@link Terminal.Terminal}. It is the shared
6
+ * process-backed terminal used by Node platform packages for prompts, REPLs,
7
+ * command-line tools, and interactive programs that need line input, key input,
8
+ * terminal dimensions, or display output.
9
+ *
10
+ * **Mental model**
11
+ *
12
+ * {@link make} creates a scoped terminal around the global process streams, and
13
+ * {@link layer} provides that terminal with the default quit behavior for key
14
+ * input. While the scope is active, the module owns the Node `readline`
15
+ * interface it created; it does not own the process streams themselves.
16
+ *
17
+ * **Common tasks**
18
+ *
19
+ * Use {@link make} when a custom `shouldQuit` predicate should decide when key
20
+ * input ends. Use {@link layer} when Ctrl+C and Ctrl+D should end the key-input
21
+ * stream. For plain byte-oriented stdin/stdout access, use the `Stdio` service
22
+ * instead.
23
+ *
24
+ * **Gotchas**
25
+ *
26
+ * When stdin is a TTY, raw mode is enabled while the scoped terminal is active
27
+ * and restored on release. Raw mode changes how keys are delivered and can
28
+ * affect other code reading stdin. In non-TTY environments such as pipes,
29
+ * redirected input, or CI, raw mode is unavailable, keypress behavior is
30
+ * limited, and stdout dimensions may be reported as zero.
31
+ *
32
+ * @since 4.0.0
3
33
  */
4
34
  import type * as Cause from "effect/Cause"
5
35
  import * as Effect from "effect/Effect"
6
36
  import * as Layer from "effect/Layer"
37
+ import * as Option from "effect/Option"
7
38
  import { badArgument, type PlatformError } from "effect/PlatformError"
8
39
  import * as Predicate from "effect/Predicate"
9
40
  import * as Queue from "effect/Queue"
@@ -13,8 +44,12 @@ import * as Terminal from "effect/Terminal"
13
44
  import * as readline from "node:readline"
14
45
 
15
46
  /**
16
- * @since 1.0.0
47
+ * Creates a scoped process-backed `Terminal` using Node `readline`, enabling
48
+ * TTY raw mode while in scope and using the supplied predicate to decide when
49
+ * key input should end.
50
+ *
17
51
  * @category constructors
52
+ * @since 4.0.0
18
53
  */
19
54
  export const make: (
20
55
  shouldQuit?: (input: Terminal.UserInput) => boolean
@@ -46,13 +81,14 @@ export const make: (
46
81
  })
47
82
 
48
83
  const columns = Effect.sync(() => stdout.columns ?? 0)
84
+ const rows = Effect.sync(() => stdout.rows ?? 0)
49
85
 
50
86
  const readInput = Effect.gen(function*() {
51
87
  yield* RcRef.get(rlRef)
52
88
  const queue = yield* Queue.make<Terminal.UserInput, Cause.Done>()
53
89
  const handleKeypress = (s: string | undefined, k: readline.Key) => {
54
90
  const userInput = {
55
- input: s,
91
+ input: Option.fromUndefinedOr(s),
56
92
  key: { name: k.name ?? "", ctrl: !!k.ctrl, meta: !!k.meta, shift: !!k.shift }
57
93
  }
58
94
  Queue.offerUnsafe(queue, userInput)
@@ -93,6 +129,7 @@ export const make: (
93
129
 
94
130
  return Terminal.make({
95
131
  columns,
132
+ rows,
96
133
  readInput,
97
134
  readLine,
98
135
  display
@@ -101,8 +138,11 @@ export const make: (
101
138
  )
102
139
 
103
140
  /**
104
- * @since 1.0.0
141
+ * Provides the default process-backed `Terminal` service, ending key input on
142
+ * Ctrl+C or Ctrl+D.
143
+ *
105
144
  * @category layers
145
+ * @since 4.0.0
106
146
  */
107
147
  export const layer: Layer.Layer<Terminal.Terminal> = Layer.effect(Terminal.Terminal, make(defaultShouldQuit))
108
148
 
package/src/index.ts ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @since 4.0.0
3
+ */
4
+
5
+ // @barrel: Auto-generated exports. Do not edit manually.
6
+
7
+ /**
8
+ * @since 4.0.0
9
+ */
10
+ export * as NodeChildProcessSpawner from "./NodeChildProcessSpawner.ts"
11
+
12
+ /**
13
+ * @since 4.0.0
14
+ */
15
+ export * as NodeClusterSocket from "./NodeClusterSocket.ts"
16
+
17
+ /**
18
+ * @since 1.0.0
19
+ */
20
+ export * as NodeCrypto from "./NodeCrypto.ts"
21
+
22
+ /**
23
+ * @since 4.0.0
24
+ */
25
+ export * as NodeFileSystem from "./NodeFileSystem.ts"
26
+
27
+ /**
28
+ * @since 4.0.0
29
+ */
30
+ export * as NodePath from "./NodePath.ts"
31
+
32
+ /**
33
+ * @since 4.0.0
34
+ */
35
+ export * as NodeRuntime from "./NodeRuntime.ts"
36
+
37
+ /**
38
+ * @since 4.0.0
39
+ */
40
+ export * as NodeSink from "./NodeSink.ts"
41
+
42
+ /**
43
+ * @since 4.0.0
44
+ */
45
+ export * as NodeSocket from "./NodeSocket.ts"
46
+
47
+ /**
48
+ * @since 4.0.0
49
+ */
50
+ export * as NodeSocketServer from "./NodeSocketServer.ts"
51
+
52
+ /**
53
+ * @since 4.0.0
54
+ */
55
+ export * as NodeStdio from "./NodeStdio.ts"
56
+
57
+ /**
58
+ * @since 4.0.0
59
+ */
60
+ export * as NodeStream from "./NodeStream.ts"
61
+
62
+ /**
63
+ * @since 4.0.0
64
+ */
65
+ export * as NodeTerminal from "./NodeTerminal.ts"