@effect/platform-node 4.0.0-beta.7 → 4.0.0-beta.70
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/Mime.d.ts +3 -3
- package/dist/Mime.js +3 -3
- package/dist/NodeChildProcessSpawner.d.ts +1 -1
- package/dist/NodeChildProcessSpawner.js +1 -1
- package/dist/NodeClusterHttp.d.ts +49 -7
- package/dist/NodeClusterHttp.d.ts.map +1 -1
- package/dist/NodeClusterHttp.js +20 -10
- package/dist/NodeClusterHttp.js.map +1 -1
- package/dist/NodeClusterSocket.d.ts +57 -11
- package/dist/NodeClusterSocket.d.ts.map +1 -1
- package/dist/NodeClusterSocket.js +57 -11
- package/dist/NodeClusterSocket.js.map +1 -1
- package/dist/NodeCrypto.d.ts +10 -0
- package/dist/NodeCrypto.d.ts.map +1 -0
- package/dist/NodeCrypto.js +14 -0
- package/dist/NodeCrypto.js.map +1 -0
- package/dist/NodeFileSystem.d.ts +4 -2
- package/dist/NodeFileSystem.d.ts.map +1 -1
- package/dist/NodeFileSystem.js +22 -3
- package/dist/NodeFileSystem.js.map +1 -1
- package/dist/NodeHttpClient.d.ts +102 -24
- package/dist/NodeHttpClient.d.ts.map +1 -1
- package/dist/NodeHttpClient.js +124 -28
- package/dist/NodeHttpClient.js.map +1 -1
- package/dist/NodeHttpIncomingMessage.d.ts +30 -9
- package/dist/NodeHttpIncomingMessage.d.ts.map +1 -1
- package/dist/NodeHttpIncomingMessage.js +34 -8
- package/dist/NodeHttpIncomingMessage.js.map +1 -1
- package/dist/NodeHttpPlatform.d.ts +10 -4
- package/dist/NodeHttpPlatform.d.ts.map +1 -1
- package/dist/NodeHttpPlatform.js +34 -7
- package/dist/NodeHttpPlatform.js.map +1 -1
- package/dist/NodeHttpServer.d.ts +56 -16
- package/dist/NodeHttpServer.d.ts.map +1 -1
- package/dist/NodeHttpServer.js +117 -49
- package/dist/NodeHttpServer.js.map +1 -1
- package/dist/NodeHttpServerRequest.d.ts +29 -3
- package/dist/NodeHttpServerRequest.d.ts.map +1 -1
- package/dist/NodeHttpServerRequest.js +9 -2
- package/dist/NodeHttpServerRequest.js.map +1 -1
- package/dist/NodeMultipart.d.ts +32 -4
- package/dist/NodeMultipart.d.ts.map +1 -1
- package/dist/NodeMultipart.js +32 -4
- package/dist/NodeMultipart.js.map +1 -1
- package/dist/NodePath.d.ts +15 -6
- package/dist/NodePath.d.ts.map +1 -1
- package/dist/NodePath.js +30 -7
- package/dist/NodePath.js.map +1 -1
- package/dist/NodeRedis.d.ts +38 -9
- package/dist/NodeRedis.d.ts.map +1 -1
- package/dist/NodeRedis.js +41 -12
- package/dist/NodeRedis.js.map +1 -1
- package/dist/NodeRuntime.d.ts +27 -36
- package/dist/NodeRuntime.d.ts.map +1 -1
- package/dist/NodeRuntime.js +24 -13
- package/dist/NodeRuntime.js.map +1 -1
- package/dist/NodeServices.d.ts +29 -5
- package/dist/NodeServices.d.ts.map +1 -1
- package/dist/NodeServices.js +7 -3
- package/dist/NodeServices.js.map +1 -1
- package/dist/NodeSink.d.ts +2 -2
- package/dist/NodeSink.js +2 -2
- package/dist/NodeSocket.d.ts +18 -3
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +36 -4
- package/dist/NodeSocket.js.map +1 -1
- package/dist/NodeSocketServer.d.ts +2 -2
- package/dist/NodeSocketServer.js +2 -2
- package/dist/NodeStdio.d.ts +5 -2
- package/dist/NodeStdio.d.ts.map +1 -1
- package/dist/NodeStdio.js +22 -3
- package/dist/NodeStdio.js.map +1 -1
- package/dist/NodeStream.d.ts +2 -2
- package/dist/NodeStream.js +2 -2
- package/dist/NodeTerminal.d.ts +8 -2
- package/dist/NodeTerminal.d.ts.map +1 -1
- package/dist/NodeTerminal.js +19 -3
- package/dist/NodeTerminal.js.map +1 -1
- package/dist/NodeWorker.d.ts +9 -2
- package/dist/NodeWorker.d.ts.map +1 -1
- package/dist/NodeWorker.js +31 -6
- package/dist/NodeWorker.js.map +1 -1
- package/dist/NodeWorkerRunner.d.ts +5 -1
- package/dist/NodeWorkerRunner.d.ts.map +1 -1
- package/dist/NodeWorkerRunner.js +27 -5
- package/dist/NodeWorkerRunner.js.map +1 -1
- package/dist/Undici.d.ts +3 -3
- package/dist/Undici.js +3 -3
- package/dist/index.d.ts +376 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +376 -24
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/Mime.ts +3 -3
- package/src/NodeChildProcessSpawner.ts +1 -1
- package/src/NodeClusterHttp.ts +54 -11
- package/src/NodeClusterSocket.ts +57 -11
- package/src/NodeCrypto.ts +16 -0
- package/src/NodeFileSystem.ts +22 -3
- package/src/NodeHttpClient.ts +132 -33
- package/src/NodeHttpIncomingMessage.ts +42 -12
- package/src/NodeHttpPlatform.ts +35 -6
- package/src/NodeHttpServer.ts +139 -53
- package/src/NodeHttpServerRequest.ts +29 -3
- package/src/NodeMultipart.ts +32 -4
- package/src/NodePath.ts +30 -7
- package/src/NodeRedis.ts +43 -14
- package/src/NodeRuntime.ts +42 -37
- package/src/NodeServices.ts +31 -5
- package/src/NodeSink.ts +2 -2
- package/src/NodeSocket.ts +41 -4
- package/src/NodeSocketServer.ts +2 -2
- package/src/NodeStdio.ts +22 -3
- package/src/NodeStream.ts +2 -2
- package/src/NodeTerminal.ts +19 -3
- package/src/NodeWorker.ts +31 -6
- package/src/NodeWorkerRunner.ts +27 -5
- package/src/Undici.ts +3 -3
- package/src/index.ts +377 -24
package/src/NodeClusterSocket.ts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The `NodeClusterSocket` module provides the Node.js socket transport for
|
|
3
|
+
* Effect Cluster runners. It wires `SocketRunner` to Node TCP sockets, supplies
|
|
4
|
+
* RPC client and server protocol layers, and builds a complete sharding layer
|
|
5
|
+
* with serialization, runner health, runner storage, and message storage.
|
|
6
|
+
*
|
|
7
|
+
* **Common tasks**
|
|
8
|
+
*
|
|
9
|
+
* - Run a Node process as a cluster runner over raw TCP sockets with
|
|
10
|
+
* {@link layer}
|
|
11
|
+
* - Connect a client-only process to an existing socket cluster without
|
|
12
|
+
* starting a runner server
|
|
13
|
+
* - Use SQL-backed storage for durable multi-process clusters, `local` storage
|
|
14
|
+
* for short-lived development, or `byo` storage when the deployment owns the
|
|
15
|
+
* persistence boundary
|
|
16
|
+
* - Check runner health with socket pings or Kubernetes pod readiness through
|
|
17
|
+
* {@link layerK8sHttpClient}
|
|
18
|
+
*
|
|
19
|
+
* **Gotchas**
|
|
20
|
+
*
|
|
21
|
+
* - `runnerAddress` is the host and port advertised to other runners; set
|
|
22
|
+
* `runnerListenAddress` when the local bind address differs from the
|
|
23
|
+
* externally reachable address
|
|
24
|
+
* - The socket transport is point-to-point RPC, not cluster gossip: runner
|
|
25
|
+
* membership, shard ownership, and persisted delivery are coordinated through
|
|
26
|
+
* `RunnerStorage`, `MessageStorage`, and `RunnerHealth`
|
|
27
|
+
* - `clientOnly` does not start a socket server or receive shard assignments
|
|
28
|
+
* - Ping health checks use the same socket protocol, so unreachable ports,
|
|
29
|
+
* firewalls, or serialization mismatches can make a runner appear unhealthy
|
|
30
|
+
*
|
|
31
|
+
* @since 4.0.0
|
|
3
32
|
*/
|
|
4
33
|
import { layerClientProtocol, layerSocketServer } from "@effect/platform-node-shared/NodeClusterSocket"
|
|
5
34
|
import type { ConfigError } from "effect/Config"
|
|
@@ -25,20 +54,30 @@ import * as Undici from "./Undici.ts"
|
|
|
25
54
|
|
|
26
55
|
export {
|
|
27
56
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
57
|
+
* Provides the cluster `RpcClientProtocol` using the shared socket client
|
|
58
|
+
* implementation.
|
|
59
|
+
*
|
|
60
|
+
* @category re-exports
|
|
61
|
+
* @since 4.0.0
|
|
30
62
|
*/
|
|
31
63
|
layerClientProtocol,
|
|
32
64
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
65
|
+
* Provides the socket server used by Node cluster runners through the shared
|
|
66
|
+
* socket server implementation.
|
|
67
|
+
*
|
|
68
|
+
* @category re-exports
|
|
69
|
+
* @since 4.0.0
|
|
35
70
|
*/
|
|
36
71
|
layerSocketServer
|
|
37
72
|
}
|
|
38
73
|
|
|
39
74
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
75
|
+
* Builds the Node cluster socket sharding layer, configuring RPC
|
|
76
|
+
* serialization, message storage, runner health checks, and optional
|
|
77
|
+
* client-only mode.
|
|
78
|
+
*
|
|
79
|
+
* @category layers
|
|
80
|
+
* @since 4.0.0
|
|
42
81
|
*/
|
|
43
82
|
export const layer = <
|
|
44
83
|
const ClientOnly extends boolean = false,
|
|
@@ -111,8 +150,12 @@ export const layer = <
|
|
|
111
150
|
}
|
|
112
151
|
|
|
113
152
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
153
|
+
* Provides an Undici dispatcher for Kubernetes API calls, using the service
|
|
154
|
+
* account CA certificate when it is available and falling back to the default
|
|
155
|
+
* dispatcher otherwise.
|
|
156
|
+
*
|
|
157
|
+
* @category layers
|
|
158
|
+
* @since 4.0.0
|
|
116
159
|
*/
|
|
117
160
|
export const layerDispatcherK8s: Layer.Layer<NodeHttpClient.Dispatcher> = Layer.effect(NodeHttpClient.Dispatcher)(
|
|
118
161
|
Effect.gen(function*() {
|
|
@@ -140,8 +183,11 @@ export const layerDispatcherK8s: Layer.Layer<NodeHttpClient.Dispatcher> = Layer.
|
|
|
140
183
|
)
|
|
141
184
|
|
|
142
185
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
186
|
+
* Provides a `K8sHttpClient` backed by the Undici HTTP client and the
|
|
187
|
+
* Kubernetes-aware dispatcher.
|
|
188
|
+
*
|
|
189
|
+
* @category layers
|
|
190
|
+
* @since 4.0.0
|
|
145
191
|
*/
|
|
146
192
|
export const layerK8sHttpClient: Layer.Layer<K8sHttpClient.K8sHttpClient> = K8sHttpClient.layer.pipe(
|
|
147
193
|
Layer.provide(Layer.fresh(NodeHttpClient.layerUndiciNoDispatcher)),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js platform Crypto service layer.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
7
|
+
import type * as Crypto from "effect/Crypto"
|
|
8
|
+
import type * as Layer from "effect/Layer"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A layer that provides the Node.js Crypto service implementation.
|
|
12
|
+
*
|
|
13
|
+
* @category layers
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
*/
|
|
16
|
+
export const layer: Layer.Layer<Crypto.Crypto> = NodeCrypto.layer
|
package/src/NodeFileSystem.ts
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Provides the Node.js `FileSystem` layer for Effect programs.
|
|
3
|
+
*
|
|
4
|
+
* Use this module when a Node application, CLI, script, or test needs to
|
|
5
|
+
* satisfy the `FileSystem` service with real filesystem access for reading and
|
|
6
|
+
* writing files, creating directories and temporary files, inspecting metadata,
|
|
7
|
+
* managing links, or watching paths for changes.
|
|
8
|
+
*
|
|
9
|
+
* This module only exposes the Node-backed layer; filesystem operations are
|
|
10
|
+
* accessed through the `FileSystem` service from `effect/FileSystem`. Provide
|
|
11
|
+
* `NodeFileSystem.layer` at the edge of the program, or use
|
|
12
|
+
* `NodeServices.layer` when you also want the standard Node path, stdio,
|
|
13
|
+
* terminal, and child process services. The implementation is shared with
|
|
14
|
+
* other Node-compatible platform packages, so optional services such as
|
|
15
|
+
* `FileSystem.WatchBackend` are honored when present; otherwise file watching
|
|
16
|
+
* follows Node's `node:fs.watch` behavior. Paths are interpreted by Node, so
|
|
17
|
+
* relative paths use the current working directory and platform path rules.
|
|
18
|
+
*
|
|
19
|
+
* @since 4.0.0
|
|
3
20
|
*/
|
|
4
21
|
import * as NodeFileSystem from "@effect/platform-node-shared/NodeFileSystem"
|
|
5
22
|
import type { FileSystem } from "effect/FileSystem"
|
|
6
23
|
import type * as Layer from "effect/Layer"
|
|
7
24
|
|
|
8
25
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
26
|
+
* Provides the `FileSystem` service backed by Node filesystem APIs.
|
|
27
|
+
*
|
|
28
|
+
* @category layers
|
|
29
|
+
* @since 4.0.0
|
|
11
30
|
*/
|
|
12
31
|
export const layer: Layer.Layer<FileSystem> = NodeFileSystem.layer
|
package/src/NodeHttpClient.ts
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node.js implementations of the Effect `HttpClient`.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the Node-specific layers and constructors for sending
|
|
5
|
+
* Effect HTTP client requests. It re-exports the fetch-based client for
|
|
6
|
+
* programs that want to use `globalThis.fetch`, provides an Undici-backed
|
|
7
|
+
* client for applications that need Undici dispatcher control, and provides a
|
|
8
|
+
* lower-level `node:http` / `node:https` client for integrations that need
|
|
9
|
+
* native Node agent configuration.
|
|
10
|
+
*
|
|
11
|
+
* Use these clients in server-side applications, CLIs, tests, and integrations
|
|
12
|
+
* where requests should participate in Effect resource management, interruption,
|
|
13
|
+
* streaming, and typed transport / decode errors. The Undici path sends each
|
|
14
|
+
* request through the current `Dispatcher`; `layerUndici` owns a scoped
|
|
15
|
+
* `Agent`, while `dispatcherLayerGlobal` uses Undici's process-global dispatcher
|
|
16
|
+
* without destroying it. The `node:http` path uses separate scoped HTTP and
|
|
17
|
+
* HTTPS agents, making it the right choice when native agent options such as
|
|
18
|
+
* TLS, proxy, keep-alive, or socket behavior need to be configured directly.
|
|
19
|
+
*
|
|
20
|
+
* The backends are not completely interchangeable. Fetch, Undici, and
|
|
21
|
+
* `node:http` expose different agent and dispatcher hooks, body implementations,
|
|
22
|
+
* abort behavior, upgrade support, and response body readers. This module
|
|
23
|
+
* converts Effect request bodies to the selected runtime representation:
|
|
24
|
+
* streams remain streaming, `FormData` may contribute generated content headers,
|
|
25
|
+
* and body read failures are reported as `HttpClientError` decode or transport
|
|
26
|
+
* errors.
|
|
27
|
+
*
|
|
28
|
+
* @since 4.0.0
|
|
3
29
|
*/
|
|
30
|
+
import * as Context from "effect/Context"
|
|
4
31
|
import * as Effect from "effect/Effect"
|
|
5
32
|
import { flow } from "effect/Function"
|
|
6
33
|
import * as Inspectable from "effect/Inspectable"
|
|
7
34
|
import * as Layer from "effect/Layer"
|
|
35
|
+
import * as Option from "effect/Option"
|
|
36
|
+
import { type Pipeable, pipeArguments } from "effect/Pipeable"
|
|
37
|
+
import type * as Schema from "effect/Schema"
|
|
8
38
|
import type * as Scope from "effect/Scope"
|
|
9
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
10
39
|
import * as Stream from "effect/Stream"
|
|
11
40
|
import * as Cookies from "effect/unstable/http/Cookies"
|
|
12
41
|
import * as Headers from "effect/unstable/http/Headers"
|
|
@@ -33,18 +62,24 @@ import * as Undici from "./Undici.ts"
|
|
|
33
62
|
|
|
34
63
|
export {
|
|
35
64
|
/**
|
|
36
|
-
*
|
|
65
|
+
* Fetch-based HTTP client implementation for Node.js.
|
|
66
|
+
*
|
|
37
67
|
* @category Fetch
|
|
68
|
+
* @since 4.0.0
|
|
38
69
|
*/
|
|
39
70
|
Fetch,
|
|
40
71
|
/**
|
|
41
|
-
*
|
|
72
|
+
* Layer that provides the fetch-based HTTP client implementation.
|
|
73
|
+
*
|
|
42
74
|
* @category Fetch
|
|
75
|
+
* @since 4.0.0
|
|
43
76
|
*/
|
|
44
77
|
layer as layerFetch,
|
|
45
78
|
/**
|
|
46
|
-
*
|
|
79
|
+
* Request initialization options accepted by the fetch-based HTTP client.
|
|
80
|
+
*
|
|
47
81
|
* @category Fetch
|
|
82
|
+
* @since 4.0.0
|
|
48
83
|
*/
|
|
49
84
|
RequestInit
|
|
50
85
|
} from "effect/unstable/http/FetchHttpClient"
|
|
@@ -54,16 +89,22 @@ export {
|
|
|
54
89
|
// -----------------------------------------------------------------------------
|
|
55
90
|
|
|
56
91
|
/**
|
|
57
|
-
*
|
|
92
|
+
* Service tag for the Undici `Dispatcher` used by the Undici-backed HTTP
|
|
93
|
+
* client.
|
|
94
|
+
*
|
|
58
95
|
* @category Dispatcher
|
|
96
|
+
* @since 4.0.0
|
|
59
97
|
*/
|
|
60
|
-
export class Dispatcher extends
|
|
98
|
+
export class Dispatcher extends Context.Service<Dispatcher, Undici.Dispatcher>()(
|
|
61
99
|
"@effect/platform-node/NodeHttpClient/Dispatcher"
|
|
62
100
|
) {}
|
|
63
101
|
|
|
64
102
|
/**
|
|
65
|
-
*
|
|
103
|
+
* Acquires a new Undici `Agent` dispatcher and destroys it when the enclosing
|
|
104
|
+
* scope is finalized.
|
|
105
|
+
*
|
|
66
106
|
* @category Dispatcher
|
|
107
|
+
* @since 4.0.0
|
|
67
108
|
*/
|
|
68
109
|
export const makeDispatcher: Effect.Effect<Undici.Dispatcher, never, Scope.Scope> = Effect.acquireRelease(
|
|
69
110
|
Effect.sync(() => new Undici.Agent()),
|
|
@@ -71,29 +112,41 @@ export const makeDispatcher: Effect.Effect<Undici.Dispatcher, never, Scope.Scope
|
|
|
71
112
|
)
|
|
72
113
|
|
|
73
114
|
/**
|
|
74
|
-
*
|
|
115
|
+
* Provides the `Dispatcher` service using a scoped Undici `Agent`.
|
|
116
|
+
*
|
|
75
117
|
* @category Dispatcher
|
|
118
|
+
* @since 4.0.0
|
|
76
119
|
*/
|
|
77
120
|
export const layerDispatcher: Layer.Layer<Dispatcher> = Layer.effect(Dispatcher)(makeDispatcher)
|
|
78
121
|
|
|
79
122
|
/**
|
|
80
|
-
*
|
|
123
|
+
* Provides the `Dispatcher` service from Undici's process-global dispatcher,
|
|
124
|
+
* without creating or owning a new agent.
|
|
125
|
+
*
|
|
81
126
|
* @category Dispatcher
|
|
127
|
+
* @since 4.0.0
|
|
82
128
|
*/
|
|
83
129
|
export const dispatcherLayerGlobal: Layer.Layer<Dispatcher> = Layer.sync(Dispatcher)(() => Undici.getGlobalDispatcher())
|
|
84
130
|
|
|
85
131
|
/**
|
|
86
|
-
*
|
|
132
|
+
* Fiber reference containing default Undici request options applied to requests
|
|
133
|
+
* sent by `makeUndici`.
|
|
134
|
+
*
|
|
87
135
|
* @category undici
|
|
136
|
+
* @since 4.0.0
|
|
88
137
|
*/
|
|
89
|
-
export const UndiciOptions =
|
|
138
|
+
export const UndiciOptions = Context.Reference<Partial<Undici.Dispatcher.RequestOptions>>(
|
|
90
139
|
"@effect/platform-node/NodeHttpClient/UndiciOptions",
|
|
91
140
|
{ defaultValue: () => ({}) }
|
|
92
141
|
)
|
|
93
142
|
|
|
94
143
|
/**
|
|
95
|
-
*
|
|
144
|
+
* Creates an `HttpClient` that sends requests through the current Undici
|
|
145
|
+
* `Dispatcher`, converts Effect HTTP bodies to Undici bodies, and maps
|
|
146
|
+
* transport and decode failures to `HttpClientError`.
|
|
147
|
+
*
|
|
96
148
|
* @category undici
|
|
149
|
+
* @since 4.0.0
|
|
97
150
|
*/
|
|
98
151
|
export const makeUndici = Effect.gen(function*() {
|
|
99
152
|
const dispatcher = yield* Dispatcher
|
|
@@ -150,7 +203,7 @@ function convertBody(
|
|
|
150
203
|
|
|
151
204
|
function noopErrorHandler(_: any) {}
|
|
152
205
|
|
|
153
|
-
class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
206
|
+
class UndiciResponse extends Inspectable.Class implements HttpClientResponse, Pipeable {
|
|
154
207
|
readonly [IncomingMessage.TypeId]: typeof IncomingMessage.TypeId
|
|
155
208
|
readonly [Response.TypeId]: typeof Response.TypeId
|
|
156
209
|
readonly request: HttpClientRequest
|
|
@@ -189,8 +242,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
189
242
|
return this.cachedCookies = header ? Cookies.fromSetCookie(header) : Cookies.empty
|
|
190
243
|
}
|
|
191
244
|
|
|
192
|
-
get remoteAddress(): string
|
|
193
|
-
return
|
|
245
|
+
get remoteAddress(): Option.Option<string> {
|
|
246
|
+
return Option.none()
|
|
194
247
|
}
|
|
195
248
|
|
|
196
249
|
get stream(): Stream.Stream<Uint8Array, Error.HttpClientError> {
|
|
@@ -207,10 +260,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
207
260
|
})
|
|
208
261
|
}
|
|
209
262
|
|
|
210
|
-
get json(): Effect.Effect<
|
|
263
|
+
get json(): Effect.Effect<Schema.Json, Error.HttpClientError> {
|
|
211
264
|
return Effect.flatMap(this.text, (text) =>
|
|
212
265
|
Effect.try({
|
|
213
|
-
try: () => text === "" ? null : JSON.parse(text)
|
|
266
|
+
try: () => text === "" ? null : JSON.parse(text),
|
|
214
267
|
catch: (cause) =>
|
|
215
268
|
new Error.HttpClientError({
|
|
216
269
|
reason: new Error.DecodeError({
|
|
@@ -224,7 +277,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
224
277
|
|
|
225
278
|
private textBody?: Effect.Effect<string, Error.HttpClientError>
|
|
226
279
|
get text(): Effect.Effect<string, Error.HttpClientError> {
|
|
227
|
-
|
|
280
|
+
if (this.textBody) {
|
|
281
|
+
return this.textBody
|
|
282
|
+
}
|
|
283
|
+
this.textBody = Effect.tryPromise({
|
|
228
284
|
try: () => this.source.body.text(),
|
|
229
285
|
catch: (cause) =>
|
|
230
286
|
new Error.HttpClientError({
|
|
@@ -235,6 +291,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
235
291
|
})
|
|
236
292
|
})
|
|
237
293
|
}).pipe(Effect.cached, Effect.runSync)
|
|
294
|
+
this.arrayBufferBody = Effect.map(this.textBody, (_) => new TextEncoder().encode(_).buffer)
|
|
295
|
+
return this.textBody
|
|
238
296
|
}
|
|
239
297
|
|
|
240
298
|
get urlParamsBody(): Effect.Effect<UrlParams.UrlParams, Error.HttpClientError> {
|
|
@@ -269,7 +327,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
269
327
|
|
|
270
328
|
private arrayBufferBody?: Effect.Effect<ArrayBuffer, Error.HttpClientError>
|
|
271
329
|
get arrayBuffer(): Effect.Effect<ArrayBuffer, Error.HttpClientError> {
|
|
272
|
-
|
|
330
|
+
if (this.arrayBufferBody) {
|
|
331
|
+
return this.arrayBufferBody
|
|
332
|
+
}
|
|
333
|
+
this.arrayBufferBody = Effect.tryPromise({
|
|
273
334
|
try: () => this.source.body.arrayBuffer(),
|
|
274
335
|
catch: (cause) =>
|
|
275
336
|
new Error.HttpClientError({
|
|
@@ -280,6 +341,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
280
341
|
})
|
|
281
342
|
})
|
|
282
343
|
}).pipe(Effect.cached, Effect.runSync)
|
|
344
|
+
this.textBody = Effect.map(this.arrayBufferBody, (_) => new TextDecoder().decode(_))
|
|
345
|
+
return this.arrayBufferBody
|
|
283
346
|
}
|
|
284
347
|
|
|
285
348
|
toJSON(): unknown {
|
|
@@ -289,21 +352,31 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
|
|
|
289
352
|
status: this.status
|
|
290
353
|
})
|
|
291
354
|
}
|
|
355
|
+
|
|
356
|
+
pipe() {
|
|
357
|
+
return pipeArguments(this, arguments)
|
|
358
|
+
}
|
|
292
359
|
}
|
|
293
360
|
|
|
294
361
|
/**
|
|
295
|
-
*
|
|
362
|
+
* Provides an Undici-backed `HttpClient` using the current `Dispatcher`
|
|
363
|
+
* service.
|
|
364
|
+
*
|
|
296
365
|
* @category Undici
|
|
366
|
+
* @since 4.0.0
|
|
297
367
|
*/
|
|
298
368
|
export const layerUndiciNoDispatcher: Layer.Layer<
|
|
299
369
|
Client.HttpClient,
|
|
300
370
|
never,
|
|
301
371
|
Dispatcher
|
|
302
|
-
> = Client.
|
|
372
|
+
> = Client.layerMergedContext(makeUndici)
|
|
303
373
|
|
|
304
374
|
/**
|
|
305
|
-
*
|
|
375
|
+
* Provides an Undici-backed `HttpClient` together with a scoped default
|
|
376
|
+
* Undici `Agent` dispatcher.
|
|
377
|
+
*
|
|
306
378
|
* @category Undici
|
|
379
|
+
* @since 4.0.0
|
|
307
380
|
*/
|
|
308
381
|
export const layerUndici: Layer.Layer<Client.HttpClient> = Layer.provide(layerUndiciNoDispatcher, layerDispatcher)
|
|
309
382
|
|
|
@@ -312,17 +385,23 @@ export const layerUndici: Layer.Layer<Client.HttpClient> = Layer.provide(layerUn
|
|
|
312
385
|
// -----------------------------------------------------------------------------
|
|
313
386
|
|
|
314
387
|
/**
|
|
315
|
-
*
|
|
388
|
+
* Service tag for the paired Node `http` and `https` agents used by the
|
|
389
|
+
* node:http-backed HTTP client.
|
|
390
|
+
*
|
|
316
391
|
* @category HttpAgent
|
|
392
|
+
* @since 4.0.0
|
|
317
393
|
*/
|
|
318
|
-
export class HttpAgent extends
|
|
394
|
+
export class HttpAgent extends Context.Service<HttpAgent, {
|
|
319
395
|
readonly http: Http.Agent
|
|
320
396
|
readonly https: Https.Agent
|
|
321
397
|
}>()("@effect/platform-node/NodeHttpClient/HttpAgent") {}
|
|
322
398
|
|
|
323
399
|
/**
|
|
324
|
-
*
|
|
400
|
+
* Acquires Node `http` and `https` agents with the supplied options and
|
|
401
|
+
* destroys both agents when the enclosing scope is finalized.
|
|
402
|
+
*
|
|
325
403
|
* @category HttpAgent
|
|
404
|
+
* @since 4.0.0
|
|
326
405
|
*/
|
|
327
406
|
export const makeAgent = (options?: Https.AgentOptions): Effect.Effect<HttpAgent["Service"], never, Scope.Scope> =>
|
|
328
407
|
Effect.zipWith(
|
|
@@ -338,22 +417,32 @@ export const makeAgent = (options?: Https.AgentOptions): Effect.Effect<HttpAgent
|
|
|
338
417
|
)
|
|
339
418
|
|
|
340
419
|
/**
|
|
341
|
-
*
|
|
420
|
+
* Provides the `HttpAgent` service using scoped Node `http` and `https`
|
|
421
|
+
* agents configured with the supplied options.
|
|
422
|
+
*
|
|
342
423
|
* @category HttpAgent
|
|
424
|
+
* @since 4.0.0
|
|
343
425
|
*/
|
|
344
426
|
export const layerAgentOptions: (options?: Https.AgentOptions | undefined) => Layer.Layer<
|
|
345
427
|
HttpAgent
|
|
346
428
|
> = flow(makeAgent, Layer.effect(HttpAgent))
|
|
347
429
|
|
|
348
430
|
/**
|
|
349
|
-
*
|
|
431
|
+
* Provides the `HttpAgent` service using default scoped Node `http` and
|
|
432
|
+
* `https` agents.
|
|
433
|
+
*
|
|
350
434
|
* @category HttpAgent
|
|
435
|
+
* @since 4.0.0
|
|
351
436
|
*/
|
|
352
437
|
export const layerAgent: Layer.Layer<HttpAgent> = layerAgentOptions()
|
|
353
438
|
|
|
354
439
|
/**
|
|
355
|
-
*
|
|
440
|
+
* Creates an `HttpClient` backed by Node `http` and `https`, using the
|
|
441
|
+
* current `HttpAgent`, streaming request bodies, and wrapping Node responses
|
|
442
|
+
* as `HttpClientResponse` values.
|
|
443
|
+
*
|
|
356
444
|
* @category node:http
|
|
445
|
+
* @since 4.0.0
|
|
357
446
|
*/
|
|
358
447
|
export const makeNodeHttp = Effect.gen(function*() {
|
|
359
448
|
const agent = yield* HttpAgent
|
|
@@ -490,7 +579,7 @@ const waitForFinish = (nodeRequest: Http.ClientRequest, request: HttpClientReque
|
|
|
490
579
|
})
|
|
491
580
|
})
|
|
492
581
|
|
|
493
|
-
class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> implements HttpClientResponse {
|
|
582
|
+
class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> implements HttpClientResponse, Pipeable {
|
|
494
583
|
readonly [Response.TypeId]: typeof Response.TypeId
|
|
495
584
|
readonly request: HttpClientRequest
|
|
496
585
|
|
|
@@ -555,20 +644,30 @@ class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> im
|
|
|
555
644
|
status: this.status
|
|
556
645
|
})
|
|
557
646
|
}
|
|
647
|
+
|
|
648
|
+
pipe() {
|
|
649
|
+
return pipeArguments(this, arguments)
|
|
650
|
+
}
|
|
558
651
|
}
|
|
559
652
|
|
|
560
653
|
/**
|
|
561
|
-
*
|
|
654
|
+
* Provides a node:http-backed `HttpClient` using the current `HttpAgent`
|
|
655
|
+
* service.
|
|
656
|
+
*
|
|
562
657
|
* @category node:http
|
|
658
|
+
* @since 4.0.0
|
|
563
659
|
*/
|
|
564
660
|
export const layerNodeHttpNoAgent: Layer.Layer<
|
|
565
661
|
Client.HttpClient,
|
|
566
662
|
never,
|
|
567
663
|
HttpAgent
|
|
568
|
-
> = Client.
|
|
664
|
+
> = Client.layerMergedContext(makeNodeHttp)
|
|
569
665
|
|
|
570
666
|
/**
|
|
571
|
-
*
|
|
667
|
+
* Provides a node:http-backed `HttpClient` together with default scoped Node
|
|
668
|
+
* `http` and `https` agents.
|
|
669
|
+
*
|
|
572
670
|
* @category node:http
|
|
671
|
+
* @since 4.0.0
|
|
573
672
|
*/
|
|
574
673
|
export const layerNodeHttp: Layer.Layer<Client.HttpClient> = Layer.provide(layerNodeHttpNoAgent, layerAgent)
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Utilities for adapting Node `http.IncomingMessage` values to the Effect HTTP
|
|
3
|
+
* incoming message interface used by the platform Node server and client
|
|
4
|
+
* implementations.
|
|
5
|
+
*
|
|
6
|
+
* This module is useful when code needs to keep access to Node's request or
|
|
7
|
+
* response object while also exposing Effect's typed headers, remote address,
|
|
8
|
+
* body decoders, and stream interface. The body helpers consume Node's readable
|
|
9
|
+
* stream, cache decoded text and array-buffer results, and honor the
|
|
10
|
+
* `HttpIncomingMessage.MaxBodySize` fiber ref. Prefer a single body access
|
|
11
|
+
* strategy per message: raw `stream` access is not cached, and Node request
|
|
12
|
+
* bodies cannot be replayed once the underlying stream has been consumed.
|
|
13
|
+
*
|
|
14
|
+
* @since 4.0.0
|
|
3
15
|
*/
|
|
4
16
|
import * as Effect from "effect/Effect"
|
|
5
17
|
import * as Inspectable from "effect/Inspectable"
|
|
18
|
+
import * as Option from "effect/Option"
|
|
19
|
+
import type * as Schema from "effect/Schema"
|
|
6
20
|
import type * as Stream from "effect/Stream"
|
|
7
21
|
import * as Headers from "effect/unstable/http/Headers"
|
|
8
22
|
import * as IncomingMessage from "effect/unstable/http/HttpIncomingMessage"
|
|
@@ -11,24 +25,30 @@ import type * as Http from "node:http"
|
|
|
11
25
|
import * as NodeStream from "./NodeStream.ts"
|
|
12
26
|
|
|
13
27
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
28
|
+
* Base adapter from Node `IncomingMessage` to Effect HTTP incoming messages,
|
|
29
|
+
* exposing headers, remote address, stream access, and cached text, JSON, URL
|
|
30
|
+
* parameter, and array-buffer body decoders with caller-provided error mapping.
|
|
31
|
+
*
|
|
32
|
+
* @category constructors
|
|
33
|
+
* @since 4.0.0
|
|
16
34
|
*/
|
|
17
35
|
export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
18
36
|
implements IncomingMessage.HttpIncomingMessage<E>
|
|
19
37
|
{
|
|
20
38
|
/**
|
|
21
|
-
*
|
|
39
|
+
* Marks this value as an HTTP incoming message for runtime guards.
|
|
40
|
+
*
|
|
41
|
+
* @since 4.0.0
|
|
22
42
|
*/
|
|
23
43
|
readonly [IncomingMessage.TypeId]: typeof IncomingMessage.TypeId
|
|
24
44
|
readonly source: Http.IncomingMessage
|
|
25
45
|
readonly onError: (error: unknown) => E
|
|
26
|
-
readonly remoteAddressOverride?: string | undefined
|
|
46
|
+
readonly remoteAddressOverride?: Option.Option<string> | undefined
|
|
27
47
|
|
|
28
48
|
constructor(
|
|
29
49
|
source: Http.IncomingMessage,
|
|
30
50
|
onError: (error: unknown) => E,
|
|
31
|
-
remoteAddressOverride?: string
|
|
51
|
+
remoteAddressOverride?: Option.Option<string>
|
|
32
52
|
) {
|
|
33
53
|
super()
|
|
34
54
|
this[IncomingMessage.TypeId] = IncomingMessage.TypeId
|
|
@@ -42,7 +62,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
get remoteAddress() {
|
|
45
|
-
return this.remoteAddressOverride ?? this.source.socket.remoteAddress
|
|
65
|
+
return this.remoteAddressOverride ?? Option.fromNullishOr(this.source.socket.remoteAddress)
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
private textEffect: Effect.Effect<string, E> | undefined
|
|
@@ -52,7 +72,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
|
52
72
|
}
|
|
53
73
|
this.textEffect = Effect.runSync(Effect.cached(
|
|
54
74
|
Effect.flatMap(
|
|
55
|
-
IncomingMessage.MaxBodySize
|
|
75
|
+
IncomingMessage.MaxBodySize,
|
|
56
76
|
(maxBodySize) =>
|
|
57
77
|
NodeStream.toString(() => this.source, {
|
|
58
78
|
onError: this.onError,
|
|
@@ -60,6 +80,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
|
60
80
|
})
|
|
61
81
|
)
|
|
62
82
|
))
|
|
83
|
+
this.arrayBufferEffect = Effect.map(this.textEffect, (_) => new TextEncoder().encode(_).buffer)
|
|
63
84
|
return this.textEffect
|
|
64
85
|
}
|
|
65
86
|
|
|
@@ -67,15 +88,15 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
|
67
88
|
return Effect.runSync(this.text)
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
get json(): Effect.Effect<
|
|
91
|
+
get json(): Effect.Effect<Schema.Json, E> {
|
|
71
92
|
return Effect.flatMap(this.text, (text) =>
|
|
72
93
|
Effect.try({
|
|
73
|
-
try: () => text === "" ? null : JSON.parse(text)
|
|
94
|
+
try: () => text === "" ? null : JSON.parse(text),
|
|
74
95
|
catch: this.onError
|
|
75
96
|
}))
|
|
76
97
|
}
|
|
77
98
|
|
|
78
|
-
get jsonUnsafe():
|
|
99
|
+
get jsonUnsafe(): Schema.Json {
|
|
79
100
|
return Effect.runSync(this.json)
|
|
80
101
|
}
|
|
81
102
|
|
|
@@ -94,12 +115,21 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
|
|
|
94
115
|
})
|
|
95
116
|
}
|
|
96
117
|
|
|
118
|
+
private arrayBufferEffect: Effect.Effect<ArrayBuffer, E> | undefined
|
|
97
119
|
get arrayBuffer(): Effect.Effect<ArrayBuffer, E> {
|
|
98
|
-
|
|
120
|
+
if (this.arrayBufferEffect) {
|
|
121
|
+
return this.arrayBufferEffect
|
|
122
|
+
}
|
|
123
|
+
this.arrayBufferEffect = Effect.withFiber((fiber) =>
|
|
99
124
|
NodeStream.toArrayBuffer(() => this.source, {
|
|
100
125
|
onError: this.onError,
|
|
101
126
|
maxBytes: fiber.getRef(IncomingMessage.MaxBodySize)
|
|
102
127
|
})
|
|
128
|
+
).pipe(
|
|
129
|
+
Effect.cached,
|
|
130
|
+
Effect.runSync
|
|
103
131
|
)
|
|
132
|
+
this.textEffect = Effect.map(this.arrayBufferEffect, (_) => new TextDecoder().decode(_))
|
|
133
|
+
return this.arrayBufferEffect
|
|
104
134
|
}
|
|
105
135
|
}
|