@effect/platform-node 4.0.0-beta.8 → 4.0.0-beta.80

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 (123) hide show
  1. package/dist/Mime.d.ts +9 -3
  2. package/dist/Mime.d.ts.map +1 -1
  3. package/dist/Mime.js +9 -3
  4. package/dist/Mime.js.map +1 -1
  5. package/dist/NodeChildProcessSpawner.d.ts +1 -1
  6. package/dist/NodeChildProcessSpawner.js +1 -1
  7. package/dist/NodeClusterHttp.d.ts +25 -7
  8. package/dist/NodeClusterHttp.d.ts.map +1 -1
  9. package/dist/NodeClusterHttp.js +20 -10
  10. package/dist/NodeClusterHttp.js.map +1 -1
  11. package/dist/NodeClusterSocket.d.ts +37 -11
  12. package/dist/NodeClusterSocket.d.ts.map +1 -1
  13. package/dist/NodeClusterSocket.js +37 -11
  14. package/dist/NodeClusterSocket.js.map +1 -1
  15. package/dist/NodeCrypto.d.ts +10 -0
  16. package/dist/NodeCrypto.d.ts.map +1 -0
  17. package/dist/NodeCrypto.js +22 -0
  18. package/dist/NodeCrypto.js.map +1 -0
  19. package/dist/NodeFileSystem.d.ts +4 -2
  20. package/dist/NodeFileSystem.d.ts.map +1 -1
  21. package/dist/NodeFileSystem.js +12 -3
  22. package/dist/NodeFileSystem.js.map +1 -1
  23. package/dist/NodeHttpClient.d.ts +98 -29
  24. package/dist/NodeHttpClient.d.ts.map +1 -1
  25. package/dist/NodeHttpClient.js +120 -33
  26. package/dist/NodeHttpClient.js.map +1 -1
  27. package/dist/NodeHttpIncomingMessage.d.ts +36 -9
  28. package/dist/NodeHttpIncomingMessage.d.ts.map +1 -1
  29. package/dist/NodeHttpIncomingMessage.js +40 -8
  30. package/dist/NodeHttpIncomingMessage.js.map +1 -1
  31. package/dist/NodeHttpPlatform.d.ts +10 -4
  32. package/dist/NodeHttpPlatform.d.ts.map +1 -1
  33. package/dist/NodeHttpPlatform.js +20 -7
  34. package/dist/NodeHttpPlatform.js.map +1 -1
  35. package/dist/NodeHttpServer.d.ts +59 -19
  36. package/dist/NodeHttpServer.d.ts.map +1 -1
  37. package/dist/NodeHttpServer.js +102 -52
  38. package/dist/NodeHttpServer.js.map +1 -1
  39. package/dist/NodeHttpServerRequest.d.ts +18 -5
  40. package/dist/NodeHttpServerRequest.d.ts.map +1 -1
  41. package/dist/NodeHttpServerRequest.js +11 -4
  42. package/dist/NodeHttpServerRequest.js.map +1 -1
  43. package/dist/NodeMultipart.d.ts +24 -4
  44. package/dist/NodeMultipart.d.ts.map +1 -1
  45. package/dist/NodeMultipart.js +24 -4
  46. package/dist/NodeMultipart.js.map +1 -1
  47. package/dist/NodePath.d.ts +15 -6
  48. package/dist/NodePath.d.ts.map +1 -1
  49. package/dist/NodePath.js +23 -7
  50. package/dist/NodePath.js.map +1 -1
  51. package/dist/NodeRedis.d.ts +27 -9
  52. package/dist/NodeRedis.d.ts.map +1 -1
  53. package/dist/NodeRedis.js +30 -12
  54. package/dist/NodeRedis.js.map +1 -1
  55. package/dist/NodeRuntime.d.ts +27 -36
  56. package/dist/NodeRuntime.d.ts.map +1 -1
  57. package/dist/NodeRuntime.js +17 -13
  58. package/dist/NodeRuntime.js.map +1 -1
  59. package/dist/NodeServices.d.ts +19 -5
  60. package/dist/NodeServices.d.ts.map +1 -1
  61. package/dist/NodeServices.js +7 -3
  62. package/dist/NodeServices.js.map +1 -1
  63. package/dist/NodeSink.d.ts +2 -2
  64. package/dist/NodeSink.js +2 -2
  65. package/dist/NodeSocket.d.ts +18 -3
  66. package/dist/NodeSocket.d.ts.map +1 -1
  67. package/dist/NodeSocket.js +27 -4
  68. package/dist/NodeSocket.js.map +1 -1
  69. package/dist/NodeSocketServer.d.ts +2 -2
  70. package/dist/NodeSocketServer.js +2 -2
  71. package/dist/NodeStdio.d.ts +5 -2
  72. package/dist/NodeStdio.d.ts.map +1 -1
  73. package/dist/NodeStdio.js +13 -3
  74. package/dist/NodeStdio.js.map +1 -1
  75. package/dist/NodeStream.d.ts +2 -2
  76. package/dist/NodeStream.js +2 -2
  77. package/dist/NodeTerminal.d.ts +8 -2
  78. package/dist/NodeTerminal.d.ts.map +1 -1
  79. package/dist/NodeTerminal.js +15 -3
  80. package/dist/NodeTerminal.js.map +1 -1
  81. package/dist/NodeWorker.d.ts +9 -2
  82. package/dist/NodeWorker.d.ts.map +1 -1
  83. package/dist/NodeWorker.js +22 -6
  84. package/dist/NodeWorker.js.map +1 -1
  85. package/dist/NodeWorkerRunner.d.ts +5 -1
  86. package/dist/NodeWorkerRunner.d.ts.map +1 -1
  87. package/dist/NodeWorkerRunner.js +18 -5
  88. package/dist/NodeWorkerRunner.js.map +1 -1
  89. package/dist/Undici.d.ts +18 -5
  90. package/dist/Undici.d.ts.map +1 -1
  91. package/dist/Undici.js +18 -5
  92. package/dist/Undici.js.map +1 -1
  93. package/dist/index.d.ts +28 -26
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +28 -26
  96. package/dist/index.js.map +1 -1
  97. package/package.json +9 -9
  98. package/src/Mime.ts +10 -3
  99. package/src/NodeChildProcessSpawner.ts +1 -1
  100. package/src/NodeClusterHttp.ts +30 -11
  101. package/src/NodeClusterSocket.ts +37 -11
  102. package/src/NodeCrypto.ts +24 -0
  103. package/src/NodeFileSystem.ts +12 -3
  104. package/src/NodeHttpClient.ts +128 -38
  105. package/src/NodeHttpIncomingMessage.ts +48 -12
  106. package/src/NodeHttpPlatform.ts +21 -6
  107. package/src/NodeHttpServer.ts +124 -56
  108. package/src/NodeHttpServerRequest.ts +18 -5
  109. package/src/NodeMultipart.ts +24 -4
  110. package/src/NodePath.ts +23 -7
  111. package/src/NodeRedis.ts +32 -14
  112. package/src/NodeRuntime.ts +35 -37
  113. package/src/NodeServices.ts +21 -5
  114. package/src/NodeSink.ts +2 -2
  115. package/src/NodeSocket.ts +32 -4
  116. package/src/NodeSocketServer.ts +2 -2
  117. package/src/NodeStdio.ts +13 -3
  118. package/src/NodeStream.ts +2 -2
  119. package/src/NodeTerminal.ts +15 -3
  120. package/src/NodeWorker.ts +22 -6
  121. package/src/NodeWorkerRunner.ts +18 -5
  122. package/src/Undici.ts +18 -5
  123. package/src/index.ts +29 -26
@@ -1,5 +1,14 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node.js socket layers for Effect Cluster runners.
3
+ *
4
+ * The main `layer` builds a sharding layer for socket transport, choosing
5
+ * serialization, runner health checks, runner storage, message storage, and
6
+ * optional client-only mode from the supplied options. This module also
7
+ * re-exports the shared socket client and server protocol layers, and provides
8
+ * Kubernetes-aware Undici dispatcher and HTTP client layers for runner health
9
+ * checks.
10
+ *
11
+ * @since 4.0.0
3
12
  */
4
13
  import { layerClientProtocol, layerSocketServer } from "@effect/platform-node-shared/NodeClusterSocket"
5
14
  import type { ConfigError } from "effect/Config"
@@ -25,20 +34,30 @@ import * as Undici from "./Undici.ts"
25
34
 
26
35
  export {
27
36
  /**
28
- * @since 1.0.0
29
- * @category Re-exports
37
+ * Provides the cluster `RpcClientProtocol` using the shared socket client
38
+ * implementation.
39
+ *
40
+ * @category re-exports
41
+ * @since 4.0.0
30
42
  */
31
43
  layerClientProtocol,
32
44
  /**
33
- * @since 1.0.0
34
- * @category Re-exports
45
+ * Provides the socket server used by Node cluster runners through the shared
46
+ * socket server implementation.
47
+ *
48
+ * @category re-exports
49
+ * @since 4.0.0
35
50
  */
36
51
  layerSocketServer
37
52
  }
38
53
 
39
54
  /**
40
- * @since 1.0.0
41
- * @category Layers
55
+ * Builds the Node cluster socket sharding layer, configuring RPC
56
+ * serialization, message storage, runner health checks, and optional
57
+ * client-only mode.
58
+ *
59
+ * @category layers
60
+ * @since 4.0.0
42
61
  */
43
62
  export const layer = <
44
63
  const ClientOnly extends boolean = false,
@@ -111,8 +130,12 @@ export const layer = <
111
130
  }
112
131
 
113
132
  /**
114
- * @since 1.0.0
115
- * @category Layers
133
+ * Provides an Undici dispatcher for Kubernetes API calls, using the service
134
+ * account CA certificate when it is available and falling back to the default
135
+ * dispatcher otherwise.
136
+ *
137
+ * @category layers
138
+ * @since 4.0.0
116
139
  */
117
140
  export const layerDispatcherK8s: Layer.Layer<NodeHttpClient.Dispatcher> = Layer.effect(NodeHttpClient.Dispatcher)(
118
141
  Effect.gen(function*() {
@@ -140,8 +163,11 @@ export const layerDispatcherK8s: Layer.Layer<NodeHttpClient.Dispatcher> = Layer.
140
163
  )
141
164
 
142
165
  /**
143
- * @since 1.0.0
144
- * @category Layers
166
+ * Provides a `K8sHttpClient` backed by the Undici HTTP client and the
167
+ * Kubernetes-aware dispatcher.
168
+ *
169
+ * @category layers
170
+ * @since 4.0.0
145
171
  */
146
172
  export const layerK8sHttpClient: Layer.Layer<K8sHttpClient.K8sHttpClient> = K8sHttpClient.layer.pipe(
147
173
  Layer.provide(Layer.fresh(NodeHttpClient.layerUndiciNoDispatcher)),
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The `NodeCrypto` module provides the Node.js `Crypto` service layer for
3
+ * Effect programs. Provide {@link layer} at the edge of a Node application,
4
+ * CLI, script, or test to satisfy `effect/Crypto` with Node's `node:crypto`
5
+ * implementation for secure random bytes, UUID generation, random values, and
6
+ * SHA digest operations.
7
+ *
8
+ * This module is the public Node adapter around the shared Node-compatible
9
+ * implementation. Digest failures are reported as platform errors, and SHA-1
10
+ * remains available only for interoperability with existing protocols.
11
+ *
12
+ * @since 1.0.0
13
+ */
14
+ import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
15
+ import type * as Crypto from "effect/Crypto"
16
+ import type * as Layer from "effect/Layer"
17
+
18
+ /**
19
+ * Layer that provides the Node.js Crypto service implementation.
20
+ *
21
+ * @category layers
22
+ * @since 1.0.0
23
+ */
24
+ export const layer: Layer.Layer<Crypto.Crypto> = NodeCrypto.layer
@@ -1,12 +1,21 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node.js `FileSystem` layer for programs that perform real filesystem I/O.
3
+ *
4
+ * The exported layer satisfies the platform-independent `FileSystem` service
5
+ * with Node-backed operations for files, directories, metadata, permissions,
6
+ * links, temporary paths, and path watching. Effects still call the service from
7
+ * `effect/FileSystem`; this module only chooses the Node implementation.
8
+ *
9
+ * @since 4.0.0
3
10
  */
4
11
  import * as NodeFileSystem from "@effect/platform-node-shared/NodeFileSystem"
5
12
  import type { FileSystem } from "effect/FileSystem"
6
13
  import type * as Layer from "effect/Layer"
7
14
 
8
15
  /**
9
- * @since 1.0.0
10
- * @category layer
16
+ * Provides the `FileSystem` service backed by Node filesystem APIs.
17
+ *
18
+ * @category layers
19
+ * @since 4.0.0
11
20
  */
12
21
  export const layer: Layer.Layer<FileSystem> = NodeFileSystem.layer
@@ -1,12 +1,23 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node.js implementations of the Effect `HttpClient`.
3
+ *
4
+ * This module supplies Node runtime backends for the platform-independent
5
+ * Effect HTTP client API. It re-exports the fetch-based `Fetch`, `RequestInit`,
6
+ * and `layerFetch` APIs, defines an Undici-backed client with dispatcher
7
+ * services and request options, and defines a lower-level `node:http` /
8
+ * `node:https` client with scoped HTTP agent layers.
9
+ *
10
+ * @since 4.0.0
3
11
  */
12
+ import * as Context from "effect/Context"
4
13
  import * as Effect from "effect/Effect"
5
14
  import { flow } from "effect/Function"
6
15
  import * as Inspectable from "effect/Inspectable"
7
16
  import * as Layer from "effect/Layer"
17
+ import * as Option from "effect/Option"
18
+ import { type Pipeable, pipeArguments } from "effect/Pipeable"
19
+ import type * as Schema from "effect/Schema"
8
20
  import type * as Scope from "effect/Scope"
9
- import * as ServiceMap from "effect/ServiceMap"
10
21
  import * as Stream from "effect/Stream"
11
22
  import * as Cookies from "effect/unstable/http/Cookies"
12
23
  import * as Headers from "effect/unstable/http/Headers"
@@ -33,18 +44,33 @@ import * as Undici from "./Undici.ts"
33
44
 
34
45
  export {
35
46
  /**
36
- * @since 1.0.0
37
- * @category Fetch
47
+ * Provides a fetch-based HTTP client implementation for Node.js.
48
+ *
49
+ * **When to use**
50
+ *
51
+ * Use to access or override the fetch implementation used by the Node
52
+ * fetch-based HTTP client.
53
+ *
54
+ * @category fetch
55
+ * @since 4.0.0
38
56
  */
39
57
  Fetch,
40
58
  /**
41
- * @since 1.0.0
42
- * @category Fetch
59
+ * Layer that provides the fetch-based HTTP client implementation.
60
+ *
61
+ * @category fetch
62
+ * @since 4.0.0
43
63
  */
44
64
  layer as layerFetch,
45
65
  /**
46
- * @since 1.0.0
47
- * @category Fetch
66
+ * Provides request initialization options accepted by the fetch-based HTTP client.
67
+ *
68
+ * **When to use**
69
+ *
70
+ * Use to provide default fetch request options for Node HTTP requests.
71
+ *
72
+ * @category fetch
73
+ * @since 4.0.0
48
74
  */
49
75
  RequestInit
50
76
  } from "effect/unstable/http/FetchHttpClient"
@@ -54,16 +80,22 @@ export {
54
80
  // -----------------------------------------------------------------------------
55
81
 
56
82
  /**
57
- * @since 1.0.0
83
+ * Service tag for the Undici `Dispatcher` used by the Undici-backed HTTP
84
+ * client.
85
+ *
58
86
  * @category Dispatcher
87
+ * @since 4.0.0
59
88
  */
60
- export class Dispatcher extends ServiceMap.Service<Dispatcher, Undici.Dispatcher>()(
89
+ export class Dispatcher extends Context.Service<Dispatcher, Undici.Dispatcher>()(
61
90
  "@effect/platform-node/NodeHttpClient/Dispatcher"
62
91
  ) {}
63
92
 
64
93
  /**
65
- * @since 1.0.0
94
+ * Acquires a new Undici `Agent` dispatcher and destroys it when the enclosing
95
+ * scope is finalized.
96
+ *
66
97
  * @category Dispatcher
98
+ * @since 4.0.0
67
99
  */
68
100
  export const makeDispatcher: Effect.Effect<Undici.Dispatcher, never, Scope.Scope> = Effect.acquireRelease(
69
101
  Effect.sync(() => new Undici.Agent()),
@@ -71,29 +103,41 @@ export const makeDispatcher: Effect.Effect<Undici.Dispatcher, never, Scope.Scope
71
103
  )
72
104
 
73
105
  /**
74
- * @since 1.0.0
106
+ * Provides the `Dispatcher` service using a scoped Undici `Agent`.
107
+ *
75
108
  * @category Dispatcher
109
+ * @since 4.0.0
76
110
  */
77
111
  export const layerDispatcher: Layer.Layer<Dispatcher> = Layer.effect(Dispatcher)(makeDispatcher)
78
112
 
79
113
  /**
80
- * @since 1.0.0
114
+ * Provides the `Dispatcher` service from Undici's process-global dispatcher,
115
+ * without creating or owning a new agent.
116
+ *
81
117
  * @category Dispatcher
118
+ * @since 4.0.0
82
119
  */
83
120
  export const dispatcherLayerGlobal: Layer.Layer<Dispatcher> = Layer.sync(Dispatcher)(() => Undici.getGlobalDispatcher())
84
121
 
85
122
  /**
86
- * @since 1.0.0
87
- * @category undici
123
+ * Fiber reference containing default Undici request options applied to requests
124
+ * sent by `makeUndici`.
125
+ *
126
+ * @category Undici
127
+ * @since 4.0.0
88
128
  */
89
- export const UndiciOptions = ServiceMap.Reference<Partial<Undici.Dispatcher.RequestOptions>>(
129
+ export const UndiciOptions = Context.Reference<Partial<Undici.Dispatcher.RequestOptions>>(
90
130
  "@effect/platform-node/NodeHttpClient/UndiciOptions",
91
131
  { defaultValue: () => ({}) }
92
132
  )
93
133
 
94
134
  /**
95
- * @since 1.0.0
96
- * @category undici
135
+ * Creates an `HttpClient` that sends requests through the current Undici
136
+ * `Dispatcher`, converts Effect HTTP bodies to Undici bodies, and maps
137
+ * transport and decode failures to `HttpClientError`.
138
+ *
139
+ * @category Undici
140
+ * @since 4.0.0
97
141
  */
98
142
  export const makeUndici = Effect.gen(function*() {
99
143
  const dispatcher = yield* Dispatcher
@@ -150,7 +194,7 @@ function convertBody(
150
194
 
151
195
  function noopErrorHandler(_: any) {}
152
196
 
153
- class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
197
+ class UndiciResponse extends Inspectable.Class implements HttpClientResponse, Pipeable {
154
198
  readonly [IncomingMessage.TypeId]: typeof IncomingMessage.TypeId
155
199
  readonly [Response.TypeId]: typeof Response.TypeId
156
200
  readonly request: HttpClientRequest
@@ -189,8 +233,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
189
233
  return this.cachedCookies = header ? Cookies.fromSetCookie(header) : Cookies.empty
190
234
  }
191
235
 
192
- get remoteAddress(): string | undefined {
193
- return undefined
236
+ get remoteAddress(): Option.Option<string> {
237
+ return Option.none()
194
238
  }
195
239
 
196
240
  get stream(): Stream.Stream<Uint8Array, Error.HttpClientError> {
@@ -207,10 +251,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
207
251
  })
208
252
  }
209
253
 
210
- get json(): Effect.Effect<unknown, Error.HttpClientError> {
254
+ get json(): Effect.Effect<Schema.Json, Error.HttpClientError> {
211
255
  return Effect.flatMap(this.text, (text) =>
212
256
  Effect.try({
213
- try: () => text === "" ? null : JSON.parse(text) as unknown,
257
+ try: () => text === "" ? null : JSON.parse(text),
214
258
  catch: (cause) =>
215
259
  new Error.HttpClientError({
216
260
  reason: new Error.DecodeError({
@@ -224,7 +268,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
224
268
 
225
269
  private textBody?: Effect.Effect<string, Error.HttpClientError>
226
270
  get text(): Effect.Effect<string, Error.HttpClientError> {
227
- return this.textBody ??= Effect.tryPromise({
271
+ if (this.textBody) {
272
+ return this.textBody
273
+ }
274
+ this.textBody = Effect.tryPromise({
228
275
  try: () => this.source.body.text(),
229
276
  catch: (cause) =>
230
277
  new Error.HttpClientError({
@@ -235,6 +282,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
235
282
  })
236
283
  })
237
284
  }).pipe(Effect.cached, Effect.runSync)
285
+ this.arrayBufferBody = Effect.map(this.textBody, (_) => new TextEncoder().encode(_).buffer)
286
+ return this.textBody
238
287
  }
239
288
 
240
289
  get urlParamsBody(): Effect.Effect<UrlParams.UrlParams, Error.HttpClientError> {
@@ -269,7 +318,10 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
269
318
 
270
319
  private arrayBufferBody?: Effect.Effect<ArrayBuffer, Error.HttpClientError>
271
320
  get arrayBuffer(): Effect.Effect<ArrayBuffer, Error.HttpClientError> {
272
- return this.arrayBufferBody ??= Effect.tryPromise({
321
+ if (this.arrayBufferBody) {
322
+ return this.arrayBufferBody
323
+ }
324
+ this.arrayBufferBody = Effect.tryPromise({
273
325
  try: () => this.source.body.arrayBuffer(),
274
326
  catch: (cause) =>
275
327
  new Error.HttpClientError({
@@ -280,6 +332,8 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
280
332
  })
281
333
  })
282
334
  }).pipe(Effect.cached, Effect.runSync)
335
+ this.textBody = Effect.map(this.arrayBufferBody, (_) => new TextDecoder().decode(_))
336
+ return this.arrayBufferBody
283
337
  }
284
338
 
285
339
  toJSON(): unknown {
@@ -289,21 +343,31 @@ class UndiciResponse extends Inspectable.Class implements HttpClientResponse {
289
343
  status: this.status
290
344
  })
291
345
  }
346
+
347
+ pipe() {
348
+ return pipeArguments(this, arguments)
349
+ }
292
350
  }
293
351
 
294
352
  /**
295
- * @since 1.0.0
353
+ * Provides an Undici-backed `HttpClient` using the current `Dispatcher`
354
+ * service.
355
+ *
296
356
  * @category Undici
357
+ * @since 4.0.0
297
358
  */
298
359
  export const layerUndiciNoDispatcher: Layer.Layer<
299
360
  Client.HttpClient,
300
361
  never,
301
362
  Dispatcher
302
- > = Client.layerMergedServices(makeUndici)
363
+ > = Client.layerMergedContext(makeUndici)
303
364
 
304
365
  /**
305
- * @since 1.0.0
366
+ * Provides an Undici-backed `HttpClient` together with a scoped default
367
+ * Undici `Agent` dispatcher.
368
+ *
306
369
  * @category Undici
370
+ * @since 4.0.0
307
371
  */
308
372
  export const layerUndici: Layer.Layer<Client.HttpClient> = Layer.provide(layerUndiciNoDispatcher, layerDispatcher)
309
373
 
@@ -312,17 +376,23 @@ export const layerUndici: Layer.Layer<Client.HttpClient> = Layer.provide(layerUn
312
376
  // -----------------------------------------------------------------------------
313
377
 
314
378
  /**
315
- * @since 1.0.0
379
+ * Service tag for the paired Node `http` and `https` agents used by the
380
+ * node:http-backed HTTP client.
381
+ *
316
382
  * @category HttpAgent
383
+ * @since 4.0.0
317
384
  */
318
- export class HttpAgent extends ServiceMap.Service<HttpAgent, {
385
+ export class HttpAgent extends Context.Service<HttpAgent, {
319
386
  readonly http: Http.Agent
320
387
  readonly https: Https.Agent
321
388
  }>()("@effect/platform-node/NodeHttpClient/HttpAgent") {}
322
389
 
323
390
  /**
324
- * @since 1.0.0
391
+ * Acquires Node `http` and `https` agents with the supplied options and
392
+ * destroys both agents when the enclosing scope is finalized.
393
+ *
325
394
  * @category HttpAgent
395
+ * @since 4.0.0
326
396
  */
327
397
  export const makeAgent = (options?: Https.AgentOptions): Effect.Effect<HttpAgent["Service"], never, Scope.Scope> =>
328
398
  Effect.zipWith(
@@ -338,22 +408,32 @@ export const makeAgent = (options?: Https.AgentOptions): Effect.Effect<HttpAgent
338
408
  )
339
409
 
340
410
  /**
341
- * @since 1.0.0
411
+ * Provides the `HttpAgent` service using scoped Node `http` and `https`
412
+ * agents configured with the supplied options.
413
+ *
342
414
  * @category HttpAgent
415
+ * @since 4.0.0
343
416
  */
344
417
  export const layerAgentOptions: (options?: Https.AgentOptions | undefined) => Layer.Layer<
345
418
  HttpAgent
346
419
  > = flow(makeAgent, Layer.effect(HttpAgent))
347
420
 
348
421
  /**
349
- * @since 1.0.0
422
+ * Provides the `HttpAgent` service using default scoped Node `http` and
423
+ * `https` agents.
424
+ *
350
425
  * @category HttpAgent
426
+ * @since 4.0.0
351
427
  */
352
428
  export const layerAgent: Layer.Layer<HttpAgent> = layerAgentOptions()
353
429
 
354
430
  /**
355
- * @since 1.0.0
431
+ * Creates an `HttpClient` backed by Node `http` and `https`, using the
432
+ * current `HttpAgent`, streaming request bodies, and wrapping Node responses
433
+ * as `HttpClientResponse` values.
434
+ *
356
435
  * @category node:http
436
+ * @since 4.0.0
357
437
  */
358
438
  export const makeNodeHttp = Effect.gen(function*() {
359
439
  const agent = yield* HttpAgent
@@ -490,7 +570,7 @@ const waitForFinish = (nodeRequest: Http.ClientRequest, request: HttpClientReque
490
570
  })
491
571
  })
492
572
 
493
- class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> implements HttpClientResponse {
573
+ class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> implements HttpClientResponse, Pipeable {
494
574
  readonly [Response.TypeId]: typeof Response.TypeId
495
575
  readonly request: HttpClientRequest
496
576
 
@@ -555,20 +635,30 @@ class NodeHttpResponse extends NodeHttpIncomingMessage<Error.HttpClientError> im
555
635
  status: this.status
556
636
  })
557
637
  }
638
+
639
+ pipe() {
640
+ return pipeArguments(this, arguments)
641
+ }
558
642
  }
559
643
 
560
644
  /**
561
- * @since 1.0.0
645
+ * Provides a node:http-backed `HttpClient` using the current `HttpAgent`
646
+ * service.
647
+ *
562
648
  * @category node:http
649
+ * @since 4.0.0
563
650
  */
564
651
  export const layerNodeHttpNoAgent: Layer.Layer<
565
652
  Client.HttpClient,
566
653
  never,
567
654
  HttpAgent
568
- > = Client.layerMergedServices(makeNodeHttp)
655
+ > = Client.layerMergedContext(makeNodeHttp)
569
656
 
570
657
  /**
571
- * @since 1.0.0
658
+ * Provides a node:http-backed `HttpClient` together with default scoped Node
659
+ * `http` and `https` agents.
660
+ *
572
661
  * @category node:http
662
+ * @since 4.0.0
573
663
  */
574
664
  export const layerNodeHttp: Layer.Layer<Client.HttpClient> = Layer.provide(layerNodeHttpNoAgent, layerAgent)
@@ -1,8 +1,20 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Adapter base for exposing Node `http.IncomingMessage` values as Effect HTTP
3
+ * incoming messages.
4
+ *
5
+ * Server requests and Node client responses both arrive as Node readable
6
+ * streams with raw header objects, socket metadata, and one-shot body
7
+ * consumption. This module's `NodeHttpIncomingMessage` class keeps the original
8
+ * Node message available while presenting Effect's `HttpIncomingMessage` shape:
9
+ * typed headers, remote address lookup, stream access, and text, JSON,
10
+ * URL-encoded, and array-buffer body readers.
11
+ *
12
+ * @since 4.0.0
3
13
  */
4
14
  import * as Effect from "effect/Effect"
5
15
  import * as Inspectable from "effect/Inspectable"
16
+ import * as Option from "effect/Option"
17
+ import type * as Schema from "effect/Schema"
6
18
  import type * as Stream from "effect/Stream"
7
19
  import * as Headers from "effect/unstable/http/Headers"
8
20
  import * as IncomingMessage from "effect/unstable/http/HttpIncomingMessage"
@@ -11,24 +23,38 @@ import type * as Http from "node:http"
11
23
  import * as NodeStream from "./NodeStream.ts"
12
24
 
13
25
  /**
14
- * @since 1.0.0
15
- * @category Constructors
26
+ * Adapts a Node `IncomingMessage` to Effect HTTP incoming messages.
27
+ *
28
+ * **When to use**
29
+ *
30
+ * Use to implement Node HTTP request or response adapters that expose the
31
+ * Effect HTTP incoming-message interface.
32
+ *
33
+ * **Details**
34
+ *
35
+ * The adapter exposes headers, remote address, stream access, and cached body
36
+ * decoders. Subclasses provide the error mapping for unknown Node errors.
37
+ *
38
+ * @category constructors
39
+ * @since 4.0.0
16
40
  */
17
41
  export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
18
42
  implements IncomingMessage.HttpIncomingMessage<E>
19
43
  {
20
44
  /**
21
- * @since 1.0.0
45
+ * Marks this value as an HTTP incoming message for runtime guards.
46
+ *
47
+ * @since 4.0.0
22
48
  */
23
49
  readonly [IncomingMessage.TypeId]: typeof IncomingMessage.TypeId
24
50
  readonly source: Http.IncomingMessage
25
51
  readonly onError: (error: unknown) => E
26
- readonly remoteAddressOverride?: string | undefined
52
+ readonly remoteAddressOverride?: Option.Option<string> | undefined
27
53
 
28
54
  constructor(
29
55
  source: Http.IncomingMessage,
30
56
  onError: (error: unknown) => E,
31
- remoteAddressOverride?: string
57
+ remoteAddressOverride?: Option.Option<string>
32
58
  ) {
33
59
  super()
34
60
  this[IncomingMessage.TypeId] = IncomingMessage.TypeId
@@ -42,7 +68,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
42
68
  }
43
69
 
44
70
  get remoteAddress() {
45
- return this.remoteAddressOverride ?? this.source.socket.remoteAddress
71
+ return this.remoteAddressOverride ?? Option.fromNullishOr(this.source.socket.remoteAddress)
46
72
  }
47
73
 
48
74
  private textEffect: Effect.Effect<string, E> | undefined
@@ -52,7 +78,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
52
78
  }
53
79
  this.textEffect = Effect.runSync(Effect.cached(
54
80
  Effect.flatMap(
55
- IncomingMessage.MaxBodySize.asEffect(),
81
+ IncomingMessage.MaxBodySize,
56
82
  (maxBodySize) =>
57
83
  NodeStream.toString(() => this.source, {
58
84
  onError: this.onError,
@@ -60,6 +86,7 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
60
86
  })
61
87
  )
62
88
  ))
89
+ this.arrayBufferEffect = Effect.map(this.textEffect, (_) => new TextEncoder().encode(_).buffer)
63
90
  return this.textEffect
64
91
  }
65
92
 
@@ -67,15 +94,15 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
67
94
  return Effect.runSync(this.text)
68
95
  }
69
96
 
70
- get json(): Effect.Effect<unknown, E> {
97
+ get json(): Effect.Effect<Schema.Json, E> {
71
98
  return Effect.flatMap(this.text, (text) =>
72
99
  Effect.try({
73
- try: () => text === "" ? null : JSON.parse(text) as unknown,
100
+ try: () => text === "" ? null : JSON.parse(text),
74
101
  catch: this.onError
75
102
  }))
76
103
  }
77
104
 
78
- get jsonUnsafe(): unknown {
105
+ get jsonUnsafe(): Schema.Json {
79
106
  return Effect.runSync(this.json)
80
107
  }
81
108
 
@@ -94,12 +121,21 @@ export abstract class NodeHttpIncomingMessage<E> extends Inspectable.Class
94
121
  })
95
122
  }
96
123
 
124
+ private arrayBufferEffect: Effect.Effect<ArrayBuffer, E> | undefined
97
125
  get arrayBuffer(): Effect.Effect<ArrayBuffer, E> {
98
- return Effect.withFiber((fiber) =>
126
+ if (this.arrayBufferEffect) {
127
+ return this.arrayBufferEffect
128
+ }
129
+ this.arrayBufferEffect = Effect.withFiber((fiber) =>
99
130
  NodeStream.toArrayBuffer(() => this.source, {
100
131
  onError: this.onError,
101
132
  maxBytes: fiber.getRef(IncomingMessage.MaxBodySize)
102
133
  })
134
+ ).pipe(
135
+ Effect.cached,
136
+ Effect.runSync
103
137
  )
138
+ this.textEffect = Effect.map(this.arrayBufferEffect, (_) => new TextDecoder().decode(_))
139
+ return this.arrayBufferEffect
104
140
  }
105
141
  }
@@ -1,5 +1,12 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Node.js implementation of the Effect HTTP platform service.
3
+ *
4
+ * This module connects the portable `HttpPlatform` file response helpers to
5
+ * Node runtime primitives. It serves local files through Node readable streams,
6
+ * supports byte ranges, converts Web `File` values to readable streams, and
7
+ * fills in content type and content length headers when needed.
8
+ *
9
+ * @since 4.0.0
3
10
  */
4
11
  import { pipe } from "effect/Function"
5
12
  import * as Layer from "effect/Layer"
@@ -13,12 +20,17 @@ import Mime from "./Mime.ts"
13
20
  import * as NodeFileSystem from "./NodeFileSystem.ts"
14
21
 
15
22
  /**
16
- * @since 1.0.0
17
- * @category Constructors
23
+ * Creates the Node `HttpPlatform`, serving file responses from Node readable
24
+ * streams and adding MIME type and content-length headers when needed.
25
+ *
26
+ * @category constructors
27
+ * @since 4.0.0
18
28
  */
19
29
  export const make = Platform.make({
20
30
  fileResponse(path, status, statusText, headers, start, end, contentLength) {
21
- const stream = Fs.createReadStream(path, { start, end })
31
+ const stream = contentLength === 0
32
+ ? Readable.from([])
33
+ : Fs.createReadStream(path, { start, end: end === undefined ? undefined : end - 1 })
22
34
  return ServerResponse.raw(stream, {
23
35
  headers: {
24
36
  ...headers,
@@ -45,8 +57,11 @@ export const make = Platform.make({
45
57
  })
46
58
 
47
59
  /**
48
- * @since 1.0.0
49
- * @category Layers
60
+ * Provides the Node `HttpPlatform` together with the filesystem and ETag
61
+ * services it needs for file responses.
62
+ *
63
+ * @category layers
64
+ * @since 4.0.0
50
65
  */
51
66
  export const layer: Layer.Layer<Platform.HttpPlatform> = pipe(
52
67
  Layer.effect(Platform.HttpPlatform)(make),