@effect/platform-node 4.0.0-beta.1 → 4.0.0-beta.100
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/README.md +1 -1
- package/dist/Mime.d.ts +9 -3
- package/dist/Mime.d.ts.map +1 -1
- package/dist/Mime.js +9 -3
- package/dist/Mime.js.map +1 -1
- package/dist/NodeChildProcessSpawner.d.ts +1 -1
- package/dist/NodeChildProcessSpawner.js +1 -1
- package/dist/NodeClusterHttp.d.ts +25 -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 +37 -11
- package/dist/NodeClusterSocket.d.ts.map +1 -1
- package/dist/NodeClusterSocket.js +37 -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 +22 -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 +12 -3
- package/dist/NodeFileSystem.js.map +1 -1
- package/dist/NodeHttpClient.d.ts +98 -29
- package/dist/NodeHttpClient.d.ts.map +1 -1
- package/dist/NodeHttpClient.js +120 -33
- package/dist/NodeHttpClient.js.map +1 -1
- package/dist/NodeHttpIncomingMessage.d.ts +36 -9
- package/dist/NodeHttpIncomingMessage.d.ts.map +1 -1
- package/dist/NodeHttpIncomingMessage.js +40 -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 +20 -7
- package/dist/NodeHttpPlatform.js.map +1 -1
- package/dist/NodeHttpServer.d.ts +60 -19
- package/dist/NodeHttpServer.d.ts.map +1 -1
- package/dist/NodeHttpServer.js +111 -54
- package/dist/NodeHttpServer.js.map +1 -1
- package/dist/NodeHttpServerRequest.d.ts +18 -5
- package/dist/NodeHttpServerRequest.d.ts.map +1 -1
- package/dist/NodeHttpServerRequest.js +11 -4
- package/dist/NodeHttpServerRequest.js.map +1 -1
- package/dist/NodeMultipart.d.ts +24 -4
- package/dist/NodeMultipart.d.ts.map +1 -1
- package/dist/NodeMultipart.js +24 -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 +23 -7
- package/dist/NodePath.js.map +1 -1
- package/dist/NodeRedis.d.ts +27 -9
- package/dist/NodeRedis.d.ts.map +1 -1
- package/dist/NodeRedis.js +30 -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 +17 -13
- package/dist/NodeRuntime.js.map +1 -1
- package/dist/NodeServices.d.ts +19 -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 +19 -4
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +27 -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 +13 -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 +15 -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 +22 -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 +18 -5
- package/dist/NodeWorkerRunner.js.map +1 -1
- package/dist/Undici.d.ts +18 -5
- package/dist/Undici.d.ts.map +1 -1
- package/dist/Undici.js +18 -5
- package/dist/Undici.js.map +1 -1
- package/dist/index.d.ts +28 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -26
- package/dist/index.js.map +1 -1
- package/package.json +11 -12
- package/src/Mime.ts +10 -3
- package/src/NodeChildProcessSpawner.ts +1 -1
- package/src/NodeClusterHttp.ts +30 -11
- package/src/NodeClusterSocket.ts +37 -11
- package/src/NodeCrypto.ts +24 -0
- package/src/NodeFileSystem.ts +12 -3
- package/src/NodeHttpClient.ts +128 -38
- package/src/NodeHttpIncomingMessage.ts +48 -12
- package/src/NodeHttpPlatform.ts +21 -6
- package/src/NodeHttpServer.ts +134 -59
- package/src/NodeHttpServerRequest.ts +18 -5
- package/src/NodeMultipart.ts +24 -4
- package/src/NodePath.ts +23 -7
- package/src/NodeRedis.ts +32 -14
- package/src/NodeRuntime.ts +35 -37
- package/src/NodeServices.ts +21 -5
- package/src/NodeSink.ts +2 -2
- package/src/NodeSocket.ts +33 -5
- package/src/NodeSocketServer.ts +2 -2
- package/src/NodeStdio.ts +13 -3
- package/src/NodeStream.ts +2 -2
- package/src/NodeTerminal.ts +15 -3
- package/src/NodeWorker.ts +22 -6
- package/src/NodeWorkerRunner.ts +18 -5
- package/src/Undici.ts +18 -5
- package/src/index.ts +29 -26
package/src/NodeHttpServer.ts
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node.js implementation of the Effect `HttpServer`.
|
|
3
|
+
*
|
|
4
|
+
* This module adapts a supplied Node `http.Server` into Effect's
|
|
5
|
+
* platform-independent HTTP server service. It starts the server with Node
|
|
6
|
+
* `listen` options, converts `request` events into `HttpServerRequest` values,
|
|
7
|
+
* writes `HttpServerResponse` bodies through Node's `ServerResponse`, and
|
|
8
|
+
* handles `upgrade` events by exposing the upgraded socket through
|
|
9
|
+
* `HttpServerRequest.upgrade`. It also exports request and upgrade handler
|
|
10
|
+
* constructors plus layers for the server alone, HTTP support services, the
|
|
11
|
+
* combined server, configurable options, and tests.
|
|
12
|
+
*
|
|
13
|
+
* @since 4.0.0
|
|
3
14
|
*/
|
|
4
15
|
import * as Cause from "effect/Cause"
|
|
5
16
|
import * as Config from "effect/Config"
|
|
17
|
+
import * as Context from "effect/Context"
|
|
18
|
+
import * as Duration from "effect/Duration"
|
|
6
19
|
import * as Effect from "effect/Effect"
|
|
7
20
|
import * as Fiber from "effect/Fiber"
|
|
8
21
|
import type * as FileSystem from "effect/FileSystem"
|
|
9
22
|
import { flow, type LazyArg } from "effect/Function"
|
|
23
|
+
import * as Latch from "effect/Latch"
|
|
10
24
|
import * as Layer from "effect/Layer"
|
|
25
|
+
import type * as Option from "effect/Option"
|
|
11
26
|
import type * as Path from "effect/Path"
|
|
12
27
|
import type * as Record from "effect/Record"
|
|
13
28
|
import * as Scope from "effect/Scope"
|
|
14
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
15
29
|
import * as Stream from "effect/Stream"
|
|
16
30
|
import * as Cookies from "effect/unstable/http/Cookies"
|
|
17
31
|
import * as Etag from "effect/unstable/http/Etag"
|
|
@@ -26,7 +40,7 @@ import type * as HttpPlatform from "effect/unstable/http/HttpPlatform"
|
|
|
26
40
|
import * as HttpServer from "effect/unstable/http/HttpServer"
|
|
27
41
|
import {
|
|
28
42
|
causeResponse,
|
|
29
|
-
|
|
43
|
+
ClientAbort,
|
|
30
44
|
HttpServerError,
|
|
31
45
|
RequestParseError,
|
|
32
46
|
ResponseError,
|
|
@@ -49,30 +63,44 @@ import * as NodeServices from "./NodeServices.ts"
|
|
|
49
63
|
import { NodeWS } from "./NodeSocket.ts"
|
|
50
64
|
|
|
51
65
|
/**
|
|
52
|
-
*
|
|
66
|
+
* Creates a scoped `HttpServer` from a Node `http.Server`, starts listening
|
|
67
|
+
* with the supplied options, registers request and upgrade handling, and closes
|
|
68
|
+
* the server during scope finalization with optional graceful-shutdown control.
|
|
69
|
+
*
|
|
53
70
|
* @category constructors
|
|
71
|
+
* @since 4.0.0
|
|
54
72
|
*/
|
|
55
73
|
export const make = Effect.fnUntraced(function*(
|
|
56
74
|
evaluate: LazyArg<Http.Server>,
|
|
57
|
-
options: Net.ListenOptions
|
|
75
|
+
options: Net.ListenOptions & {
|
|
76
|
+
readonly disablePreemptiveShutdown?: boolean | undefined
|
|
77
|
+
readonly gracefulShutdownTimeout?: Duration.Input | undefined
|
|
78
|
+
}
|
|
58
79
|
) {
|
|
59
80
|
const scope = yield* Effect.scope
|
|
60
81
|
const server = evaluate()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
|
|
83
|
+
const shutdown = yield* Effect.callback<void>((resume) => {
|
|
84
|
+
if (!server.listening) {
|
|
85
|
+
return resume(Effect.void)
|
|
86
|
+
}
|
|
87
|
+
server.close((error) => {
|
|
88
|
+
if (error) {
|
|
89
|
+
resume(Effect.die(error))
|
|
90
|
+
} else {
|
|
91
|
+
resume(Effect.void)
|
|
66
92
|
}
|
|
67
|
-
server.close((error) => {
|
|
68
|
-
if (error) {
|
|
69
|
-
resume(Effect.die(error))
|
|
70
|
-
} else {
|
|
71
|
-
resume(Effect.void)
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
93
|
})
|
|
75
|
-
)
|
|
94
|
+
}).pipe(Effect.cached)
|
|
95
|
+
|
|
96
|
+
const preemptiveShutdown = options.disablePreemptiveShutdown ?
|
|
97
|
+
Effect.void :
|
|
98
|
+
Effect.timeoutOrElse(shutdown, {
|
|
99
|
+
duration: options.gracefulShutdownTimeout ?? Duration.seconds(20),
|
|
100
|
+
orElse: () => Effect.void
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
yield* Scope.addFinalizer(scope, shutdown)
|
|
76
104
|
|
|
77
105
|
yield* Effect.callback<void, ServeError>((resume) => {
|
|
78
106
|
function onError(cause: Error) {
|
|
@@ -110,7 +138,8 @@ export const make = Effect.fnUntraced(function*(
|
|
|
110
138
|
port: address.port
|
|
111
139
|
},
|
|
112
140
|
serve: Effect.fnUntraced(function*(httpApp, middleware) {
|
|
113
|
-
const
|
|
141
|
+
const serveScope = yield* Effect.scope
|
|
142
|
+
const scope = Scope.forkUnsafe(serveScope, "parallel")
|
|
114
143
|
const handler = yield* (makeHandler(httpApp, {
|
|
115
144
|
middleware: middleware as any,
|
|
116
145
|
scope
|
|
@@ -119,12 +148,11 @@ export const make = Effect.fnUntraced(function*(
|
|
|
119
148
|
middleware: middleware as any,
|
|
120
149
|
scope
|
|
121
150
|
})
|
|
122
|
-
yield*
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
151
|
+
yield* Scope.addFinalizerExit(serveScope, () => {
|
|
152
|
+
server.off("request", handler)
|
|
153
|
+
server.off("upgrade", upgradeHandler)
|
|
154
|
+
return preemptiveShutdown
|
|
155
|
+
})
|
|
128
156
|
server.on("request", handler)
|
|
129
157
|
server.on("upgrade", upgradeHandler)
|
|
130
158
|
})
|
|
@@ -132,8 +160,12 @@ export const make = Effect.fnUntraced(function*(
|
|
|
132
160
|
})
|
|
133
161
|
|
|
134
162
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
163
|
+
* Creates a Node `request` event handler for an Effect HTTP application,
|
|
164
|
+
* injecting a `HttpServerRequest` and interrupting the request fiber if the
|
|
165
|
+
* client closes the response before it finishes.
|
|
166
|
+
*
|
|
167
|
+
* @category handlers
|
|
168
|
+
* @since 4.0.0
|
|
137
169
|
*/
|
|
138
170
|
export const makeHandler = <
|
|
139
171
|
R,
|
|
@@ -151,26 +183,31 @@ export const makeHandler = <
|
|
|
151
183
|
Exclude<Effect.Services<App>, HttpServerRequest | Scope.Scope>
|
|
152
184
|
> => {
|
|
153
185
|
const handled = HttpEffect.toHandled(httpEffect, handleResponse, options.middleware as any)
|
|
154
|
-
return Effect.
|
|
155
|
-
|
|
186
|
+
return Effect.withFiber((parent) => {
|
|
187
|
+
const services = parent.context
|
|
188
|
+
return Effect.succeed(function handler(
|
|
156
189
|
nodeRequest: Http.IncomingMessage,
|
|
157
190
|
nodeResponse: Http.ServerResponse
|
|
158
191
|
) {
|
|
159
192
|
const map = new Map(services.mapUnsafe)
|
|
160
193
|
map.set(HttpServerRequest.key, new ServerRequestImpl(nodeRequest, nodeResponse))
|
|
161
|
-
const fiber = Fiber.runIn(Effect.runForkWith(
|
|
194
|
+
const fiber = Fiber.runIn(Effect.runForkWith(Context.makeUnsafe<any>(map))(handled), options.scope)
|
|
162
195
|
nodeResponse.on("close", () => {
|
|
163
196
|
if (!nodeResponse.writableEnded) {
|
|
164
|
-
fiber.interruptUnsafe(
|
|
197
|
+
fiber.interruptUnsafe(parent.id, ClientAbort.annotation)
|
|
165
198
|
}
|
|
166
199
|
})
|
|
167
|
-
}
|
|
200
|
+
})
|
|
168
201
|
})
|
|
169
202
|
}
|
|
170
203
|
|
|
171
204
|
/**
|
|
172
|
-
*
|
|
173
|
-
*
|
|
205
|
+
* Creates a Node `upgrade` event handler for an Effect HTTP application,
|
|
206
|
+
* exposing the upgraded WebSocket as the request's `upgrade` effect and
|
|
207
|
+
* interrupting the request fiber when the socket closes early.
|
|
208
|
+
*
|
|
209
|
+
* @category handlers
|
|
210
|
+
* @since 4.0.0
|
|
174
211
|
*/
|
|
175
212
|
export const makeUpgradeHandler = <
|
|
176
213
|
R,
|
|
@@ -189,8 +226,13 @@ export const makeUpgradeHandler = <
|
|
|
189
226
|
Exclude<Effect.Services<App>, HttpServerRequest | Scope.Scope>
|
|
190
227
|
> => {
|
|
191
228
|
const handledApp = HttpEffect.toHandled(httpEffect, handleResponse, options.middleware as any)
|
|
192
|
-
return Effect.
|
|
193
|
-
|
|
229
|
+
return Effect.withFiber((parent) => {
|
|
230
|
+
const services = parent.context
|
|
231
|
+
return Effect.succeed(function handler(
|
|
232
|
+
nodeRequest: Http.IncomingMessage,
|
|
233
|
+
socket: Duplex,
|
|
234
|
+
head: Buffer
|
|
235
|
+
) {
|
|
194
236
|
let nodeResponse_: Http.ServerResponse | undefined = undefined
|
|
195
237
|
const nodeResponse = () => {
|
|
196
238
|
if (nodeResponse_ === undefined) {
|
|
@@ -216,13 +258,14 @@ export const makeUpgradeHandler = <
|
|
|
216
258
|
))
|
|
217
259
|
const map = new Map(services.mapUnsafe)
|
|
218
260
|
map.set(HttpServerRequest.key, new ServerRequestImpl(nodeRequest, nodeResponse, upgradeEffect))
|
|
219
|
-
const fiber = Fiber.runIn(Effect.runForkWith(
|
|
261
|
+
const fiber = Fiber.runIn(Effect.runForkWith(Context.makeUnsafe<any>(map))(handledApp), options.scope)
|
|
220
262
|
socket.on("close", () => {
|
|
221
263
|
if (!socket.writableEnded) {
|
|
222
|
-
fiber.interruptUnsafe(
|
|
264
|
+
fiber.interruptUnsafe(parent.id, ClientAbort.annotation)
|
|
223
265
|
}
|
|
224
266
|
})
|
|
225
|
-
})
|
|
267
|
+
})
|
|
268
|
+
})
|
|
226
269
|
}
|
|
227
270
|
|
|
228
271
|
class ServerRequestImpl extends NodeHttpIncomingMessage<HttpServerError> implements HttpServerRequest {
|
|
@@ -238,7 +281,7 @@ class ServerRequestImpl extends NodeHttpIncomingMessage<HttpServerError> impleme
|
|
|
238
281
|
upgradeEffect?: Effect.Effect<Socket.Socket, HttpServerError>,
|
|
239
282
|
url = source.url!,
|
|
240
283
|
headersOverride?: Headers.Headers,
|
|
241
|
-
remoteAddressOverride?: string
|
|
284
|
+
remoteAddressOverride?: Option.Option<string>
|
|
242
285
|
) {
|
|
243
286
|
super(source, (cause) =>
|
|
244
287
|
new HttpServerError({
|
|
@@ -270,7 +313,7 @@ class ServerRequestImpl extends NodeHttpIncomingMessage<HttpServerError> impleme
|
|
|
270
313
|
options: {
|
|
271
314
|
readonly url?: string | undefined
|
|
272
315
|
readonly headers?: Headers.Headers | undefined
|
|
273
|
-
readonly remoteAddress?: string | undefined
|
|
316
|
+
readonly remoteAddress?: Option.Option<string> | undefined
|
|
274
317
|
}
|
|
275
318
|
) {
|
|
276
319
|
return new ServerRequestImpl(
|
|
@@ -279,7 +322,7 @@ class ServerRequestImpl extends NodeHttpIncomingMessage<HttpServerError> impleme
|
|
|
279
322
|
this.upgradeEffect,
|
|
280
323
|
options.url ?? this.url,
|
|
281
324
|
options.headers ?? this.headersOverride,
|
|
282
|
-
options.remoteAddress
|
|
325
|
+
"remoteAddress" in options ? options.remoteAddress : this.remoteAddressOverride
|
|
283
326
|
)
|
|
284
327
|
}
|
|
285
328
|
|
|
@@ -346,17 +389,26 @@ class ServerRequestImpl extends NodeHttpIncomingMessage<HttpServerError> impleme
|
|
|
346
389
|
}
|
|
347
390
|
|
|
348
391
|
/**
|
|
349
|
-
*
|
|
350
|
-
*
|
|
392
|
+
* Provides an `HttpServer` by creating and managing a scoped Node
|
|
393
|
+
* `http.Server` with the supplied listen and shutdown options.
|
|
394
|
+
*
|
|
395
|
+
* @category layers
|
|
396
|
+
* @since 4.0.0
|
|
351
397
|
*/
|
|
352
398
|
export const layerServer: (
|
|
353
399
|
evaluate: LazyArg<Http.Server<typeof Http.IncomingMessage, typeof Http.ServerResponse>>,
|
|
354
|
-
options: Net.ListenOptions
|
|
400
|
+
options: Net.ListenOptions & {
|
|
401
|
+
readonly disablePreemptiveShutdown?: boolean | undefined
|
|
402
|
+
readonly gracefulShutdownTimeout?: Duration.Input | undefined
|
|
403
|
+
}
|
|
355
404
|
) => Layer.Layer<HttpServer.HttpServer, ServeError> = flow(make, Layer.effect(HttpServer.HttpServer))
|
|
356
405
|
|
|
357
406
|
/**
|
|
358
|
-
*
|
|
359
|
-
*
|
|
407
|
+
* Provides the Node HTTP support services used by `NodeHttpServer`, including
|
|
408
|
+
* the HTTP platform, ETag generator, and core Node platform services.
|
|
409
|
+
*
|
|
410
|
+
* @category layers
|
|
411
|
+
* @since 4.0.0
|
|
360
412
|
*/
|
|
361
413
|
export const layerHttpServices: Layer.Layer<
|
|
362
414
|
NodeServices.NodeServices | HttpPlatform.HttpPlatform | Etag.Generator
|
|
@@ -367,12 +419,18 @@ export const layerHttpServices: Layer.Layer<
|
|
|
367
419
|
)
|
|
368
420
|
|
|
369
421
|
/**
|
|
370
|
-
*
|
|
371
|
-
*
|
|
422
|
+
* Provides a Node `HttpServer` together with the Node HTTP platform, ETag, and
|
|
423
|
+
* core platform services required to serve requests.
|
|
424
|
+
*
|
|
425
|
+
* @category layers
|
|
426
|
+
* @since 4.0.0
|
|
372
427
|
*/
|
|
373
428
|
export const layer = (
|
|
374
429
|
evaluate: LazyArg<Http.Server>,
|
|
375
|
-
options: Net.ListenOptions
|
|
430
|
+
options: Net.ListenOptions & {
|
|
431
|
+
readonly disablePreemptiveShutdown?: boolean | undefined
|
|
432
|
+
readonly gracefulShutdownTimeout?: Duration.Input | undefined
|
|
433
|
+
}
|
|
376
434
|
): Layer.Layer<
|
|
377
435
|
HttpServer.HttpServer | NodeServices.NodeServices | HttpPlatform.HttpPlatform | Etag.Generator,
|
|
378
436
|
ServeError
|
|
@@ -383,26 +441,38 @@ export const layer = (
|
|
|
383
441
|
)
|
|
384
442
|
|
|
385
443
|
/**
|
|
386
|
-
*
|
|
387
|
-
*
|
|
444
|
+
* Provides a Node `HttpServer` together with the Node HTTP platform, ETag,
|
|
445
|
+
* and core Node platform services, reading the listen and shutdown options from
|
|
446
|
+
* a `Config` value.
|
|
447
|
+
*
|
|
448
|
+
* @category layers
|
|
449
|
+
* @since 4.0.0
|
|
388
450
|
*/
|
|
389
451
|
export const layerConfig = (
|
|
390
452
|
evaluate: LazyArg<Http.Server>,
|
|
391
|
-
options: Config.Wrap<
|
|
453
|
+
options: Config.Wrap<
|
|
454
|
+
Net.ListenOptions & {
|
|
455
|
+
readonly disablePreemptiveShutdown?: boolean | undefined
|
|
456
|
+
readonly gracefulShutdownTimeout?: Duration.Input | undefined
|
|
457
|
+
}
|
|
458
|
+
>
|
|
392
459
|
): Layer.Layer<
|
|
393
|
-
HttpServer.HttpServer |
|
|
460
|
+
HttpServer.HttpServer | NodeServices.NodeServices | HttpPlatform.HttpPlatform | Etag.Generator,
|
|
394
461
|
ServeError | Config.ConfigError
|
|
395
462
|
> =>
|
|
396
463
|
Layer.mergeAll(
|
|
397
464
|
Layer.effect(HttpServer.HttpServer)(
|
|
398
|
-
Effect.flatMap(Config.unwrap(options)
|
|
465
|
+
Effect.flatMap(Config.unwrap(options), (options) => make(evaluate, options))
|
|
399
466
|
),
|
|
400
467
|
layerHttpServices
|
|
401
468
|
)
|
|
402
469
|
|
|
403
470
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
471
|
+
* Provides a test HTTP server listening on an ephemeral port together with a
|
|
472
|
+
* Fetch-backed `HttpClient` configured for server integration tests.
|
|
473
|
+
*
|
|
474
|
+
* @category testing
|
|
475
|
+
* @since 4.0.0
|
|
406
476
|
*/
|
|
407
477
|
export const layerTest: Layer.Layer<
|
|
408
478
|
| HttpServer.HttpServer
|
|
@@ -448,7 +518,12 @@ const handleResponse = (
|
|
|
448
518
|
if (request.method === "HEAD") {
|
|
449
519
|
nodeResponse.writeHead(response.status, headers)
|
|
450
520
|
return Effect.callback<void>((resume) => {
|
|
451
|
-
|
|
521
|
+
const done = () => {
|
|
522
|
+
nodeResponse.off("close", done)
|
|
523
|
+
resume(Effect.void)
|
|
524
|
+
}
|
|
525
|
+
nodeResponse.once("close", done)
|
|
526
|
+
nodeResponse.end(done)
|
|
452
527
|
})
|
|
453
528
|
}
|
|
454
529
|
const body = response.body
|
|
@@ -528,7 +603,7 @@ const handleResponse = (
|
|
|
528
603
|
}
|
|
529
604
|
case "Stream": {
|
|
530
605
|
nodeResponse.writeHead(response.status, headers)
|
|
531
|
-
const drainLatch =
|
|
606
|
+
const drainLatch = Latch.makeUnsafe()
|
|
532
607
|
nodeResponse.on("drain", () => drainLatch.openUnsafe())
|
|
533
608
|
return body.stream.pipe(
|
|
534
609
|
Stream.orDie,
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Accessors for the Node.js objects behind an Effect HTTP server request.
|
|
3
|
+
*
|
|
4
|
+
* `toIncomingMessage` returns the underlying Node `http.IncomingMessage`.
|
|
5
|
+
* `toServerResponse` returns the underlying Node `http.ServerResponse`,
|
|
6
|
+
* evaluating the stored response thunk when the response was created lazily.
|
|
7
|
+
*
|
|
8
|
+
* @since 4.0.0
|
|
3
9
|
*/
|
|
4
10
|
import type { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"
|
|
5
11
|
import type * as Http from "node:http"
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
14
|
+
* Returns the underlying Node `IncomingMessage` for a platform Node
|
|
15
|
+
* `HttpServerRequest`.
|
|
16
|
+
*
|
|
17
|
+
* @category accessors
|
|
18
|
+
* @since 4.0.0
|
|
10
19
|
*/
|
|
11
20
|
export const toIncomingMessage = (self: HttpServerRequest): Http.IncomingMessage => self.source as any
|
|
12
21
|
|
|
13
22
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
23
|
+
* Returns the underlying Node `ServerResponse` for a platform Node
|
|
24
|
+
* `HttpServerRequest`, evaluating the stored response thunk when the response
|
|
25
|
+
* was created lazily.
|
|
26
|
+
*
|
|
27
|
+
* @category accessors
|
|
28
|
+
* @since 4.0.0
|
|
16
29
|
*/
|
|
17
30
|
export const toServerResponse = (self: HttpServerRequest): Http.ServerResponse => {
|
|
18
31
|
const res = (self as any).response
|
package/src/NodeMultipart.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node.js multipart parsing for HTTP `multipart/form-data` request bodies.
|
|
3
|
+
*
|
|
4
|
+
* `NodeMultipart` adapts a Node `Readable` plus incoming HTTP headers into
|
|
5
|
+
* Effect's shared multipart model. It can expose form parts as a stream or
|
|
6
|
+
* collect a complete persisted form by writing file uploads to scoped temporary
|
|
7
|
+
* files through the current `FileSystem` and `Path` services. `fileToReadable`
|
|
8
|
+
* returns the underlying Node readable stream for file parts produced by this
|
|
9
|
+
* parser.
|
|
10
|
+
*
|
|
11
|
+
* @since 4.0.0
|
|
3
12
|
*/
|
|
4
13
|
import * as Effect from "effect/Effect"
|
|
5
14
|
import type * as FileSystem from "effect/FileSystem"
|
|
@@ -16,8 +25,12 @@ import * as NodeStreamP from "node:stream/promises"
|
|
|
16
25
|
import * as NodeStream from "./NodeStream.ts"
|
|
17
26
|
|
|
18
27
|
/**
|
|
19
|
-
*
|
|
28
|
+
* Parses multipart data from a Node readable request body and headers into a
|
|
29
|
+
* stream of `Multipart.Part` values, converting parser failures to
|
|
30
|
+
* `MultipartError`.
|
|
31
|
+
*
|
|
20
32
|
* @category constructors
|
|
33
|
+
* @since 4.0.0
|
|
21
34
|
*/
|
|
22
35
|
export const stream = (
|
|
23
36
|
source: Readable,
|
|
@@ -39,8 +52,11 @@ export const stream = (
|
|
|
39
52
|
)
|
|
40
53
|
|
|
41
54
|
/**
|
|
42
|
-
*
|
|
55
|
+
* Parses multipart data from a Node readable request body and persists file
|
|
56
|
+
* parts using the current `FileSystem`, `Path`, and `Scope` services.
|
|
57
|
+
*
|
|
43
58
|
* @category constructors
|
|
59
|
+
* @since 4.0.0
|
|
44
60
|
*/
|
|
45
61
|
export const persisted = (
|
|
46
62
|
source: Readable,
|
|
@@ -57,7 +73,11 @@ export const persisted = (
|
|
|
57
73
|
}))
|
|
58
74
|
|
|
59
75
|
/**
|
|
60
|
-
*
|
|
76
|
+
* Returns the underlying Node readable stream for a multipart file produced by
|
|
77
|
+
* the Node multipart parser.
|
|
78
|
+
*
|
|
79
|
+
* @category converting
|
|
80
|
+
* @since 4.0.0
|
|
61
81
|
*/
|
|
62
82
|
export const fileToReadable = (file: Multipart.File): Readable => (file as FileImpl).file
|
|
63
83
|
|
package/src/NodePath.ts
CHANGED
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node.js layers for Effect's `Path` service.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the default, POSIX, and Windows variants of the
|
|
5
|
+
* platform-independent `Path` service by reusing the shared Node path
|
|
6
|
+
* implementation. The provided path services include Node file URL conversion
|
|
7
|
+
* behavior.
|
|
8
|
+
*
|
|
9
|
+
* @since 4.0.0
|
|
3
10
|
*/
|
|
4
11
|
import * as NodePath from "@effect/platform-node-shared/NodePath"
|
|
5
12
|
import type * as Layer from "effect/Layer"
|
|
6
13
|
import type { Path } from "effect/Path"
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
16
|
+
* Provides the default Node `Path` service using the platform's `node:path`
|
|
17
|
+
* implementation.
|
|
18
|
+
*
|
|
19
|
+
* @category layers
|
|
20
|
+
* @since 4.0.0
|
|
11
21
|
*/
|
|
12
22
|
export const layer: Layer.Layer<Path> = NodePath.layer
|
|
13
23
|
|
|
14
24
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
25
|
+
* Provides the `Path` service using Node's POSIX path implementation,
|
|
26
|
+
* regardless of the host platform.
|
|
27
|
+
*
|
|
28
|
+
* @category layers
|
|
29
|
+
* @since 4.0.0
|
|
17
30
|
*/
|
|
18
31
|
export const layerPosix: Layer.Layer<Path> = NodePath.layerPosix
|
|
19
32
|
|
|
20
33
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
34
|
+
* Provides the `Path` service using Node's Windows path implementation,
|
|
35
|
+
* regardless of the host platform.
|
|
36
|
+
*
|
|
37
|
+
* @category layers
|
|
38
|
+
* @since 4.0.0
|
|
23
39
|
*/
|
|
24
40
|
export const layerWin32: Layer.Layer<Path> = NodePath.layerWin32
|
package/src/NodeRedis.ts
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Node.js Redis integration backed by `ioredis`.
|
|
3
|
+
*
|
|
4
|
+
* This module creates a scoped `ioredis` client and exposes it in two forms:
|
|
5
|
+
* the generic `Redis` service and the {@link NodeRedis} service for direct
|
|
6
|
+
* access to the underlying client. `layer` accepts ioredis options directly,
|
|
7
|
+
* while `layerConfig` reads them from Effect config. Both layers close the
|
|
8
|
+
* client when the layer scope ends.
|
|
9
|
+
*
|
|
10
|
+
* @since 4.0.0
|
|
3
11
|
*/
|
|
4
12
|
import * as Config from "effect/Config"
|
|
13
|
+
import * as Context from "effect/Context"
|
|
5
14
|
import * as Effect from "effect/Effect"
|
|
6
15
|
import * as Fn from "effect/Function"
|
|
7
16
|
import * as Layer from "effect/Layer"
|
|
8
17
|
import * as Scope from "effect/Scope"
|
|
9
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
10
18
|
import * as Redis from "effect/unstable/persistence/Redis"
|
|
11
19
|
import * as IoRedis from "ioredis"
|
|
12
20
|
|
|
13
21
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
22
|
+
* Service tag for the Node Redis integration, exposing the underlying
|
|
23
|
+
* `ioredis` client and a `use` helper that maps client failures to
|
|
24
|
+
* `RedisError`.
|
|
25
|
+
*
|
|
26
|
+
* @category services
|
|
27
|
+
* @since 4.0.0
|
|
16
28
|
*/
|
|
17
|
-
export class NodeRedis extends
|
|
29
|
+
export class NodeRedis extends Context.Service<NodeRedis, {
|
|
18
30
|
readonly client: IoRedis.Redis
|
|
19
31
|
readonly use: <A>(f: (client: IoRedis.Redis) => Promise<A>) => Effect.Effect<A, Redis.RedisError>
|
|
20
32
|
}>()("@effect/platform-node/NodeRedis") {}
|
|
@@ -45,30 +57,36 @@ const make = Effect.fnUntraced(function*(
|
|
|
45
57
|
use
|
|
46
58
|
})
|
|
47
59
|
|
|
48
|
-
return
|
|
49
|
-
|
|
60
|
+
return Context.make(NodeRedis, nodeRedis).pipe(
|
|
61
|
+
Context.add(Redis.Redis, redis)
|
|
50
62
|
)
|
|
51
63
|
})
|
|
52
64
|
|
|
53
65
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
66
|
+
* Provides `Redis` and `NodeRedis` services backed by an `ioredis` client
|
|
67
|
+
* created with the supplied options and closed when the layer scope ends.
|
|
68
|
+
*
|
|
69
|
+
* @category layers
|
|
70
|
+
* @since 4.0.0
|
|
56
71
|
*/
|
|
57
72
|
export const layer = (
|
|
58
73
|
options?: IoRedis.RedisOptions | undefined
|
|
59
|
-
): Layer.Layer<Redis.Redis | NodeRedis> => Layer.
|
|
74
|
+
): Layer.Layer<Redis.Redis | NodeRedis> => Layer.effectContext(make(options))
|
|
60
75
|
|
|
61
76
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
77
|
+
* Provides `Redis` and `NodeRedis` services from `Config`-backed ioredis
|
|
78
|
+
* options, closing the client when the layer scope ends.
|
|
79
|
+
*
|
|
80
|
+
* @category layers
|
|
81
|
+
* @since 4.0.0
|
|
64
82
|
*/
|
|
65
83
|
export const layerConfig: (
|
|
66
84
|
options: Config.Wrap<IoRedis.RedisOptions>
|
|
67
85
|
) => Layer.Layer<Redis.Redis | NodeRedis, Config.ConfigError> = (
|
|
68
86
|
options: Config.Wrap<IoRedis.RedisOptions>
|
|
69
87
|
): Layer.Layer<Redis.Redis | NodeRedis, Config.ConfigError> =>
|
|
70
|
-
Layer.
|
|
71
|
-
Config.unwrap(options).
|
|
88
|
+
Layer.effectContext(
|
|
89
|
+
Config.unwrap(options).pipe(
|
|
72
90
|
Effect.flatMap(make)
|
|
73
91
|
)
|
|
74
92
|
)
|