@effect/platform-node-shared 4.0.0-beta.7 → 4.0.0-beta.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/NodeChildProcessSpawner.d.ts +18 -4
- package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/dist/NodeChildProcessSpawner.js +76 -17
- package/dist/NodeChildProcessSpawner.js.map +1 -1
- package/dist/NodeClusterSocket.d.ts +10 -4
- package/dist/NodeClusterSocket.d.ts.map +1 -1
- package/dist/NodeClusterSocket.js +46 -8
- package/dist/NodeClusterSocket.js.map +1 -1
- package/dist/NodeCrypto.d.ts +42 -0
- package/dist/NodeCrypto.d.ts.map +1 -0
- package/dist/NodeCrypto.js +70 -0
- package/dist/NodeCrypto.js.map +1 -0
- package/dist/NodeFileSystem.d.ts +5 -2
- package/dist/NodeFileSystem.d.ts.map +1 -1
- package/dist/NodeFileSystem.js +56 -26
- package/dist/NodeFileSystem.js.map +1 -1
- package/dist/NodePath.d.ts +15 -6
- package/dist/NodePath.d.ts.map +1 -1
- package/dist/NodePath.js +39 -7
- package/dist/NodePath.js.map +1 -1
- package/dist/NodeRuntime.d.ts +48 -7
- package/dist/NodeRuntime.d.ts.map +1 -1
- package/dist/NodeRuntime.js +8 -8
- package/dist/NodeRuntime.js.map +1 -1
- package/dist/NodeSink.d.ts +49 -4
- package/dist/NodeSink.d.ts.map +1 -1
- package/dist/NodeSink.js +23 -4
- package/dist/NodeSink.js.map +1 -1
- package/dist/NodeSocket.d.ts +50 -9
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +31 -15
- package/dist/NodeSocket.js.map +1 -1
- package/dist/NodeSocketServer.d.ts +24 -7
- package/dist/NodeSocketServer.d.ts.map +1 -1
- package/dist/NodeSocketServer.js +28 -11
- package/dist/NodeSocketServer.js.map +1 -1
- package/dist/NodeStdio.d.ts +6 -5
- package/dist/NodeStdio.d.ts.map +1 -1
- package/dist/NodeStdio.js +46 -7
- package/dist/NodeStdio.js.map +1 -1
- package/dist/NodeStream.d.ts +93 -20
- package/dist/NodeStream.d.ts.map +1 -1
- package/dist/NodeStream.js +85 -23
- package/dist/NodeStream.js.map +1 -1
- package/dist/NodeTerminal.d.ts +9 -2
- package/dist/NodeTerminal.d.ts.map +1 -1
- package/dist/NodeTerminal.js +13 -3
- package/dist/NodeTerminal.js.map +1 -1
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/package.json +7 -6
- package/src/NodeChildProcessSpawner.ts +116 -28
- package/src/NodeClusterSocket.ts +46 -8
- package/src/NodeCrypto.ts +75 -0
- package/src/NodeFileSystem.ts +63 -28
- package/src/NodePath.ts +39 -7
- package/src/NodeRuntime.ts +50 -13
- package/src/NodeSink.ts +56 -4
- package/src/NodeSocket.ts +56 -16
- package/src/NodeSocketServer.ts +64 -12
- package/src/NodeStdio.ts +64 -23
- package/src/NodeStream.ts +106 -30
- package/src/NodeTerminal.ts +44 -4
- package/src/index.ts +65 -0
package/src/NodeSocketServer.ts
CHANGED
|
@@ -1,7 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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 =
|
|
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(
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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 =
|
|
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(
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
12
|
-
*
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
169
|
-
*
|
|
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.
|
|
232
|
+
Effect.context<R>(),
|
|
174
233
|
(context) => new StreamAdapter(context, stream)
|
|
175
234
|
)
|
|
176
235
|
|
|
177
236
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
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
|
-
|
|
245
|
+
Context.empty(),
|
|
184
246
|
stream
|
|
185
247
|
)
|
|
186
248
|
|
|
187
249
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
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
|
-
*
|
|
238
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
*
|
|
282
|
-
*
|
|
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:
|
|
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.
|
|
458
|
+
Effect.provideContext(context),
|
|
383
459
|
Effect.runFork
|
|
384
460
|
)
|
|
385
461
|
this.fiber.addObserver((exit) => {
|
package/src/NodeTerminal.ts
CHANGED
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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"
|