@effect/platform 0.58.9 → 0.58.11
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/HttpServerRespondable/package.json +6 -0
- package/dist/cjs/HttpApp.js +10 -3
- package/dist/cjs/HttpApp.js.map +1 -1
- package/dist/cjs/HttpClientResponse.js +11 -1
- package/dist/cjs/HttpClientResponse.js.map +1 -1
- package/dist/cjs/HttpRouter.js +7 -1
- package/dist/cjs/HttpRouter.js.map +1 -1
- package/dist/cjs/HttpServerError.js +32 -2
- package/dist/cjs/HttpServerError.js.map +1 -1
- package/dist/cjs/HttpServerRequest.js +6 -1
- package/dist/cjs/HttpServerRequest.js.map +1 -1
- package/dist/cjs/HttpServerRespondable.js +88 -0
- package/dist/cjs/HttpServerRespondable.js.map +1 -0
- package/dist/cjs/HttpServerResponse.js.map +1 -1
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/internal/httpClientResponse.js +20 -1
- package/dist/cjs/internal/httpClientResponse.js.map +1 -1
- package/dist/cjs/internal/httpMiddleware.js +17 -25
- package/dist/cjs/internal/httpMiddleware.js.map +1 -1
- package/dist/cjs/internal/httpRouter.js +4 -2
- package/dist/cjs/internal/httpRouter.js.map +1 -1
- package/dist/cjs/internal/httpServerError.js +28 -3
- package/dist/cjs/internal/httpServerError.js.map +1 -1
- package/dist/cjs/internal/httpServerRequest.js +12 -1
- package/dist/cjs/internal/httpServerRequest.js.map +1 -1
- package/dist/cjs/internal/httpServerResponse.js +4 -0
- package/dist/cjs/internal/httpServerResponse.js.map +1 -1
- package/dist/cjs/internal/path.js +3 -3
- package/dist/cjs/internal/path.js.map +1 -1
- package/dist/dts/Cookies.d.ts.map +1 -1
- package/dist/dts/Error.d.ts.map +1 -1
- package/dist/dts/FileSystem.d.ts.map +1 -1
- package/dist/dts/Headers.d.ts.map +1 -1
- package/dist/dts/HttpApp.d.ts +1 -1
- package/dist/dts/HttpApp.d.ts.map +1 -1
- package/dist/dts/HttpBody.d.ts.map +1 -1
- package/dist/dts/HttpClientResponse.d.ts +44 -0
- package/dist/dts/HttpClientResponse.d.ts.map +1 -1
- package/dist/dts/HttpIncomingMessage.d.ts +1 -1
- package/dist/dts/HttpIncomingMessage.d.ts.map +1 -1
- package/dist/dts/HttpRouter.d.ts +17 -4
- package/dist/dts/HttpRouter.d.ts.map +1 -1
- package/dist/dts/HttpServerError.d.ts +22 -3
- package/dist/dts/HttpServerError.d.ts.map +1 -1
- package/dist/dts/HttpServerRequest.d.ts +6 -0
- package/dist/dts/HttpServerRequest.d.ts.map +1 -1
- package/dist/dts/HttpServerRespondable.d.ts +35 -0
- package/dist/dts/HttpServerRespondable.d.ts.map +1 -0
- package/dist/dts/HttpServerResponse.d.ts +2 -1
- package/dist/dts/HttpServerResponse.d.ts.map +1 -1
- package/dist/dts/HttpTraceContext.d.ts.map +1 -1
- package/dist/dts/PlatformConfigProvider.d.ts.map +1 -1
- package/dist/dts/Socket.d.ts.map +1 -1
- package/dist/dts/Transferable.d.ts.map +1 -1
- package/dist/dts/UrlParams.d.ts.map +1 -1
- package/dist/dts/WorkerError.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/httpRouter.d.ts +1 -1
- package/dist/dts/internal/httpRouter.d.ts.map +1 -1
- package/dist/esm/HttpApp.js +10 -3
- package/dist/esm/HttpApp.js.map +1 -1
- package/dist/esm/HttpClientResponse.js +10 -0
- package/dist/esm/HttpClientResponse.js.map +1 -1
- package/dist/esm/HttpRouter.js +5 -0
- package/dist/esm/HttpRouter.js.map +1 -1
- package/dist/esm/HttpServerError.js +31 -1
- package/dist/esm/HttpServerError.js.map +1 -1
- package/dist/esm/HttpServerRequest.js +5 -0
- package/dist/esm/HttpServerRequest.js.map +1 -1
- package/dist/esm/HttpServerRespondable.js +52 -0
- package/dist/esm/HttpServerRespondable.js.map +1 -0
- package/dist/esm/HttpServerResponse.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/httpClientResponse.js +19 -0
- package/dist/esm/internal/httpClientResponse.js.map +1 -1
- package/dist/esm/internal/httpMiddleware.js +17 -25
- package/dist/esm/internal/httpMiddleware.js.map +1 -1
- package/dist/esm/internal/httpRouter.js +4 -2
- package/dist/esm/internal/httpRouter.js.map +1 -1
- package/dist/esm/internal/httpServerError.js +25 -1
- package/dist/esm/internal/httpServerError.js.map +1 -1
- package/dist/esm/internal/httpServerRequest.js +10 -0
- package/dist/esm/internal/httpServerRequest.js.map +1 -1
- package/dist/esm/internal/httpServerResponse.js +4 -0
- package/dist/esm/internal/httpServerResponse.js.map +1 -1
- package/package.json +11 -3
- package/src/HttpApp.ts +14 -3
- package/src/HttpClientResponse.ts +77 -0
- package/src/HttpRouter.ts +22 -4
- package/src/HttpServerError.ts +37 -5
- package/src/HttpServerRequest.ts +7 -0
- package/src/HttpServerRespondable.ts +66 -0
- package/src/HttpServerResponse.ts +2 -1
- package/src/index.ts +5 -0
- package/src/internal/httpClientResponse.ts +82 -0
- package/src/internal/httpMiddleware.ts +23 -30
- package/src/internal/httpRouter.ts +9 -7
- package/src/internal/httpServerError.ts +35 -6
- package/src/internal/httpServerRequest.ts +11 -0
- package/src/internal/httpServerResponse.ts +9 -0
package/src/HttpServerError.ts
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
import type * as Cause from "effect/Cause"
|
|
5
|
+
import type * as Exit from "effect/Exit"
|
|
5
6
|
import type * as FiberId from "effect/FiberId"
|
|
7
|
+
import type * as Option from "effect/Option"
|
|
6
8
|
import { RefailError, TypeIdError } from "./Error.js"
|
|
7
9
|
import type * as ServerRequest from "./HttpServerRequest.js"
|
|
8
|
-
import
|
|
10
|
+
import * as Respondable from "./HttpServerRespondable.js"
|
|
11
|
+
import * as ServerResponse from "./HttpServerResponse.js"
|
|
9
12
|
import * as internal from "./internal/httpServerError.js"
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -33,7 +36,14 @@ export type HttpServerError = RequestError | ResponseError | RouteNotFound | Ser
|
|
|
33
36
|
export class RequestError extends RefailError(TypeId, "RequestError")<{
|
|
34
37
|
readonly request: ServerRequest.HttpServerRequest
|
|
35
38
|
readonly reason: "Transport" | "Decode"
|
|
36
|
-
}> {
|
|
39
|
+
}> implements Respondable.Respondable {
|
|
40
|
+
/**
|
|
41
|
+
* @since 1.0.0
|
|
42
|
+
*/
|
|
43
|
+
[Respondable.symbol]() {
|
|
44
|
+
return ServerResponse.empty({ status: 400 })
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
get methodAndUrl() {
|
|
38
48
|
return `${this.request.method} ${this.request.url}`
|
|
39
49
|
}
|
|
@@ -56,6 +66,13 @@ export const isServerError: (u: unknown) => u is HttpServerError = internal.isSe
|
|
|
56
66
|
export class RouteNotFound extends TypeIdError(TypeId, "RouteNotFound")<{
|
|
57
67
|
readonly request: ServerRequest.HttpServerRequest
|
|
58
68
|
}> {
|
|
69
|
+
/**
|
|
70
|
+
* @since 1.0.0
|
|
71
|
+
*/
|
|
72
|
+
[Respondable.symbol]() {
|
|
73
|
+
return ServerResponse.empty({ status: 404 })
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
get message() {
|
|
60
77
|
return `${this.request.method} ${this.request.url} not found`
|
|
61
78
|
}
|
|
@@ -70,6 +87,13 @@ export class ResponseError extends RefailError(TypeId, "ResponseError")<{
|
|
|
70
87
|
readonly response: ServerResponse.HttpServerResponse
|
|
71
88
|
readonly reason: "Decode"
|
|
72
89
|
}> {
|
|
90
|
+
/**
|
|
91
|
+
* @since 1.0.0
|
|
92
|
+
*/
|
|
93
|
+
[Respondable.symbol]() {
|
|
94
|
+
return ServerResponse.empty({ status: 500 })
|
|
95
|
+
}
|
|
96
|
+
|
|
73
97
|
get methodAndUrl() {
|
|
74
98
|
return `${this.request.method} ${this.request.url}`
|
|
75
99
|
}
|
|
@@ -83,8 +107,7 @@ export class ResponseError extends RefailError(TypeId, "ResponseError")<{
|
|
|
83
107
|
* @since 1.0.0
|
|
84
108
|
* @category error
|
|
85
109
|
*/
|
|
86
|
-
export class ServeError extends RefailError(TypeId, "ServeError")<{}> {
|
|
87
|
-
}
|
|
110
|
+
export class ServeError extends RefailError(TypeId, "ServeError")<{}> {}
|
|
88
111
|
|
|
89
112
|
/**
|
|
90
113
|
* @since 1.0.0
|
|
@@ -99,4 +122,13 @@ export const isClientAbortCause: <E>(cause: Cause.Cause<E>) => boolean = interna
|
|
|
99
122
|
/**
|
|
100
123
|
* @since 1.0.0
|
|
101
124
|
*/
|
|
102
|
-
export const
|
|
125
|
+
export const causeStatusStripped: <E>(
|
|
126
|
+
cause: Cause.Cause<E>
|
|
127
|
+
) => readonly [status: number, cause: Option.Option<Cause.Cause<E>>] = internal.causeStatusStripped
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @since 1.0.0
|
|
131
|
+
*/
|
|
132
|
+
export const exitResponse: <E>(
|
|
133
|
+
exit: Exit.Exit<ServerResponse.HttpServerResponse, E>
|
|
134
|
+
) => ServerResponse.HttpServerResponse = internal.exitResponse
|
package/src/HttpServerRequest.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { Channel } from "effect/Channel"
|
|
|
8
8
|
import type { Chunk } from "effect/Chunk"
|
|
9
9
|
import type * as Context from "effect/Context"
|
|
10
10
|
import type * as Effect from "effect/Effect"
|
|
11
|
+
import type { Option } from "effect/Option"
|
|
11
12
|
import type { ReadonlyRecord } from "effect/Record"
|
|
12
13
|
import type * as Scope from "effect/Scope"
|
|
13
14
|
import type * as Stream from "effect/Stream"
|
|
@@ -220,3 +221,9 @@ export const schemaBodyFormJson: <A, I, R>(
|
|
|
220
221
|
* @category conversions
|
|
221
222
|
*/
|
|
222
223
|
export const fromWeb: (request: Request) => HttpServerRequest = internal.fromWeb
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @since 1.0.0
|
|
227
|
+
* @category conversions
|
|
228
|
+
*/
|
|
229
|
+
export const toURL: (self: HttpServerRequest) => Option<URL> = internal.toURL
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as ParseResult from "@effect/schema/ParseResult"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import { hasProperty } from "effect/Predicate"
|
|
7
|
+
import type { HttpServerResponse } from "./HttpServerResponse.js"
|
|
8
|
+
import * as ServerResponse from "./HttpServerResponse.js"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category symbols
|
|
13
|
+
*/
|
|
14
|
+
export const symbol: unique symbol = Symbol.for("@effect/platform/HttpServerRespondable")
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category models
|
|
19
|
+
*/
|
|
20
|
+
export interface Respondable {
|
|
21
|
+
readonly [symbol]: () => Effect.Effect<HttpServerResponse, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category guards
|
|
27
|
+
*/
|
|
28
|
+
export const isRespondable = (u: unknown): u is Respondable => hasProperty(u, symbol)
|
|
29
|
+
|
|
30
|
+
const badRequest = ServerResponse.empty({ status: 400 })
|
|
31
|
+
const internalServerError = () => ServerResponse.empty({ status: 500 })
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category accessors
|
|
36
|
+
*/
|
|
37
|
+
export const toResponse = (self: Respondable): Effect.Effect<HttpServerResponse> => {
|
|
38
|
+
if (ServerResponse.isServerResponse(self)) {
|
|
39
|
+
return Effect.succeed(self)
|
|
40
|
+
}
|
|
41
|
+
return Effect.orDie(self[symbol]())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @since 1.0.0
|
|
46
|
+
* @category accessors
|
|
47
|
+
*/
|
|
48
|
+
export const toResponseOrElse = (
|
|
49
|
+
u: unknown,
|
|
50
|
+
orElse: () => Effect.Effect<HttpServerResponse, unknown>
|
|
51
|
+
): Effect.Effect<HttpServerResponse, unknown> => {
|
|
52
|
+
if (isRespondable(u)) {
|
|
53
|
+
return u[symbol]()
|
|
54
|
+
// add support for some commmon types
|
|
55
|
+
} else if (ParseResult.isParseError(u)) {
|
|
56
|
+
return Effect.succeed(badRequest)
|
|
57
|
+
}
|
|
58
|
+
return orElse()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
* @category accessors
|
|
64
|
+
*/
|
|
65
|
+
export const toResponseError = (u: unknown): Effect.Effect<HttpServerResponse, unknown> =>
|
|
66
|
+
toResponseOrElse(u, internalServerError)
|
|
@@ -12,6 +12,7 @@ import type * as FileSystem from "./FileSystem.js"
|
|
|
12
12
|
import type * as Headers from "./Headers.js"
|
|
13
13
|
import type * as Body from "./HttpBody.js"
|
|
14
14
|
import type * as Platform from "./HttpPlatform.js"
|
|
15
|
+
import type { Respondable } from "./HttpServerRespondable.js"
|
|
15
16
|
import * as internal from "./internal/httpServerResponse.js"
|
|
16
17
|
import type * as Template from "./Template.js"
|
|
17
18
|
import type * as UrlParams from "./UrlParams.js"
|
|
@@ -32,7 +33,7 @@ export type TypeId = typeof TypeId
|
|
|
32
33
|
* @since 1.0.0
|
|
33
34
|
* @category models
|
|
34
35
|
*/
|
|
35
|
-
export interface HttpServerResponse extends Effect.Effect<HttpServerResponse>, Inspectable {
|
|
36
|
+
export interface HttpServerResponse extends Effect.Effect<HttpServerResponse>, Inspectable, Respondable {
|
|
36
37
|
readonly [TypeId]: TypeId
|
|
37
38
|
readonly status: number
|
|
38
39
|
readonly statusText?: string | undefined
|
package/src/index.ts
CHANGED
|
@@ -114,6 +114,11 @@ export * as HttpServerError from "./HttpServerError.js"
|
|
|
114
114
|
*/
|
|
115
115
|
export * as HttpServerRequest from "./HttpServerRequest.js"
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
export * as HttpServerRespondable from "./HttpServerRespondable.js"
|
|
121
|
+
|
|
117
122
|
/**
|
|
118
123
|
* @since 1.0.0
|
|
119
124
|
*/
|
|
@@ -2,8 +2,10 @@ import type { ParseOptions } from "@effect/schema/AST"
|
|
|
2
2
|
import type * as ParseResult from "@effect/schema/ParseResult"
|
|
3
3
|
import * as Schema from "@effect/schema/Schema"
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
|
+
import { dual } from "effect/Function"
|
|
5
6
|
import * as Inspectable from "effect/Inspectable"
|
|
6
7
|
import * as Option from "effect/Option"
|
|
8
|
+
import type { Scope } from "effect/Scope"
|
|
7
9
|
import * as Stream from "effect/Stream"
|
|
8
10
|
import * as Cookies from "../Cookies.js"
|
|
9
11
|
import * as Headers from "../Headers.js"
|
|
@@ -248,3 +250,83 @@ export const schemaNoBodyScoped = <
|
|
|
248
250
|
return <E, R2>(effect: Effect.Effect<ClientResponse.HttpClientResponse, E, R2>) =>
|
|
249
251
|
Effect.scoped(Effect.flatMap(effect, decode))
|
|
250
252
|
}
|
|
253
|
+
|
|
254
|
+
/** @internal */
|
|
255
|
+
export const matchStatus = dual<
|
|
256
|
+
<
|
|
257
|
+
const Cases extends {
|
|
258
|
+
readonly [status: number]: (_: ClientResponse.HttpClientResponse) => any
|
|
259
|
+
readonly "2xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
260
|
+
readonly "3xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
261
|
+
readonly "4xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
262
|
+
readonly "5xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
263
|
+
readonly orElse: (_: ClientResponse.HttpClientResponse) => any
|
|
264
|
+
}
|
|
265
|
+
>(
|
|
266
|
+
cases: Cases
|
|
267
|
+
) => (self: ClientResponse.HttpClientResponse) => Cases[keyof Cases] extends (_: any) => infer R ? R : never,
|
|
268
|
+
<
|
|
269
|
+
const Cases extends {
|
|
270
|
+
readonly [status: number]: (_: ClientResponse.HttpClientResponse) => any
|
|
271
|
+
readonly "2xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
272
|
+
readonly "3xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
273
|
+
readonly "4xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
274
|
+
readonly "5xx"?: (_: ClientResponse.HttpClientResponse) => any
|
|
275
|
+
readonly orElse: (_: ClientResponse.HttpClientResponse) => any
|
|
276
|
+
}
|
|
277
|
+
>(self: ClientResponse.HttpClientResponse, cases: Cases) => Cases[keyof Cases] extends (_: any) => infer R ? R : never
|
|
278
|
+
>(2, (self, cases) => {
|
|
279
|
+
const status = self.status
|
|
280
|
+
if (cases[status]) {
|
|
281
|
+
return cases[status](self)
|
|
282
|
+
} else if (status >= 200 && status < 300 && cases["2xx"]) {
|
|
283
|
+
return cases["2xx"](self)
|
|
284
|
+
} else if (status >= 300 && status < 400 && cases["3xx"]) {
|
|
285
|
+
return cases["3xx"](self)
|
|
286
|
+
} else if (status >= 400 && status < 500 && cases["4xx"]) {
|
|
287
|
+
return cases["4xx"](self)
|
|
288
|
+
} else if (status >= 500 && status < 600 && cases["5xx"]) {
|
|
289
|
+
return cases["5xx"](self)
|
|
290
|
+
}
|
|
291
|
+
return cases.orElse(self)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
/** @internal */
|
|
295
|
+
export const matchStatusScoped = dual<
|
|
296
|
+
<
|
|
297
|
+
const Cases extends {
|
|
298
|
+
readonly [status: number]: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
299
|
+
readonly "2xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
300
|
+
readonly "3xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
301
|
+
readonly "4xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
302
|
+
readonly "5xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
303
|
+
readonly orElse: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
304
|
+
}
|
|
305
|
+
>(cases: Cases) => <E, R>(self: Effect.Effect<ClientResponse.HttpClientResponse, E, R>) => Effect.Effect<
|
|
306
|
+
Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _A : never,
|
|
307
|
+
E | (Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _E : never),
|
|
308
|
+
Exclude<
|
|
309
|
+
R | (Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _R : never),
|
|
310
|
+
Scope
|
|
311
|
+
>
|
|
312
|
+
>,
|
|
313
|
+
<
|
|
314
|
+
E,
|
|
315
|
+
R,
|
|
316
|
+
const Cases extends {
|
|
317
|
+
readonly [status: number]: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
318
|
+
readonly "2xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
319
|
+
readonly "3xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
320
|
+
readonly "4xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
321
|
+
readonly "5xx"?: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
322
|
+
readonly orElse: (_: ClientResponse.HttpClientResponse) => Effect.Effect<any, any, any>
|
|
323
|
+
}
|
|
324
|
+
>(self: Effect.Effect<ClientResponse.HttpClientResponse, E, R>, cases: Cases) => Effect.Effect<
|
|
325
|
+
Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _A : never,
|
|
326
|
+
E | (Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _E : never),
|
|
327
|
+
Exclude<
|
|
328
|
+
R | (Cases[keyof Cases] extends (_: any) => Effect.Effect<infer _A, infer _E, infer _R> ? _R : never),
|
|
329
|
+
Scope
|
|
330
|
+
>
|
|
331
|
+
>
|
|
332
|
+
>(2, (self, cases) => Effect.scoped(Effect.flatMap(self, matchStatus(cases) as any)))
|
|
@@ -82,19 +82,23 @@ export const logger = make((httpApp) => {
|
|
|
82
82
|
Effect.flatMap(Effect.exit(httpApp), (exit) => {
|
|
83
83
|
if (fiber.getFiberRef(loggerDisabled)) {
|
|
84
84
|
return exit
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Effect.annotateLogs(Effect.log(
|
|
89
|
-
"http.method": request.method,
|
|
90
|
-
"http.url": request.url,
|
|
91
|
-
"http.status": ServerError.causeStatusCode(exit.cause)
|
|
92
|
-
}) :
|
|
93
|
-
Effect.annotateLogs(Effect.log("Sent HTTP response"), {
|
|
85
|
+
} else if (exit._tag === "Failure") {
|
|
86
|
+
const [status, cause] = ServerError.causeStatusStripped(exit.cause)
|
|
87
|
+
return Effect.zipRight(
|
|
88
|
+
Effect.annotateLogs(Effect.log(cause._tag === "Some" ? cause.value : "Sent HTTP Response"), {
|
|
94
89
|
"http.method": request.method,
|
|
95
90
|
"http.url": request.url,
|
|
96
|
-
"http.status":
|
|
91
|
+
"http.status": status
|
|
97
92
|
}),
|
|
93
|
+
exit
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
return Effect.zipRight(
|
|
97
|
+
Effect.annotateLogs(Effect.log("Sent HTTP response"), {
|
|
98
|
+
"http.method": request.method,
|
|
99
|
+
"http.url": request.url,
|
|
100
|
+
"http.status": exit.value.status
|
|
101
|
+
}),
|
|
98
102
|
exit
|
|
99
103
|
)
|
|
100
104
|
}),
|
|
@@ -112,17 +116,10 @@ export const tracer = make((httpApp) =>
|
|
|
112
116
|
if (disabled) {
|
|
113
117
|
return httpApp
|
|
114
118
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
url = new URL(request.url, `${protocol}://${host}`)
|
|
120
|
-
if (url.username !== "" || url.password !== "") {
|
|
121
|
-
url.username = "REDACTED"
|
|
122
|
-
url.password = "REDACTED"
|
|
123
|
-
}
|
|
124
|
-
} catch (_) {
|
|
125
|
-
//
|
|
119
|
+
const url = Option.getOrUndefined(ServerRequest.toURL(request))
|
|
120
|
+
if (url !== undefined && (url.username !== "" || url.password !== "")) {
|
|
121
|
+
url.username = "REDACTED"
|
|
122
|
+
url.password = "REDACTED"
|
|
126
123
|
}
|
|
127
124
|
const redactedHeaderNames = fiber.getFiberRef(Headers.currentRedactedNames)
|
|
128
125
|
const redactedHeaders = Headers.redact(request.headers, redactedHeaderNames)
|
|
@@ -156,15 +153,11 @@ export const tracer = make((httpApp) =>
|
|
|
156
153
|
return Effect.flatMap(
|
|
157
154
|
Effect.exit(Effect.withParentSpan(httpApp, span)),
|
|
158
155
|
(exit) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
span.attribute(
|
|
164
|
-
const redactedHeaders = Headers.redact(response.headers, redactedHeaderNames)
|
|
165
|
-
for (const name in redactedHeaders) {
|
|
166
|
-
span.attribute(`http.response.header.${name}`, String(redactedHeaders[name]))
|
|
167
|
-
}
|
|
156
|
+
const response = ServerError.exitResponse(exit)
|
|
157
|
+
span.attribute("http.response.status_code", response.status)
|
|
158
|
+
const redactedHeaders = Headers.redact(response.headers, redactedHeaderNames)
|
|
159
|
+
for (const name in redactedHeaders) {
|
|
160
|
+
span.attribute(`http.response.header.${name}`, String(redactedHeaders[name]))
|
|
168
161
|
}
|
|
169
162
|
return exit
|
|
170
163
|
}
|
|
@@ -19,6 +19,7 @@ import type * as Method from "../HttpMethod.js"
|
|
|
19
19
|
import type * as Router from "../HttpRouter.js"
|
|
20
20
|
import * as Error from "../HttpServerError.js"
|
|
21
21
|
import * as ServerRequest from "../HttpServerRequest.js"
|
|
22
|
+
import * as Respondable from "../HttpServerRespondable.js"
|
|
22
23
|
import type * as ServerResponse from "../HttpServerResponse.js"
|
|
23
24
|
|
|
24
25
|
/** @internal */
|
|
@@ -149,7 +150,7 @@ class RouterImpl<E = never, R = never> extends Effectable.StructuralClass<
|
|
|
149
150
|
readonly mounts: Chunk.Chunk<
|
|
150
151
|
readonly [
|
|
151
152
|
prefix: string,
|
|
152
|
-
httpApp: App.
|
|
153
|
+
httpApp: App.HttpApp<Respondable.Respondable, E, R>,
|
|
153
154
|
options?: { readonly includePrefix?: boolean | undefined } | undefined
|
|
154
155
|
]
|
|
155
156
|
>
|
|
@@ -225,7 +226,7 @@ const toHttpApp = <E, R>(
|
|
|
225
226
|
context.unsafeMap.set(ServerRequest.HttpServerRequest.key, sliceRequestUrl(request, path))
|
|
226
227
|
}
|
|
227
228
|
return Effect.locally(
|
|
228
|
-
routeContext.route.handler as App.Default<E, R>,
|
|
229
|
+
Effect.flatMap(routeContext.route.handler, Respondable.toResponse) as App.Default<E, R>,
|
|
229
230
|
FiberRef.currentContext,
|
|
230
231
|
context
|
|
231
232
|
)
|
|
@@ -252,10 +253,11 @@ const toHttpApp = <E, R>(
|
|
|
252
253
|
span.value.attribute("http.route", route.path)
|
|
253
254
|
}
|
|
254
255
|
|
|
256
|
+
const handlerResponse = Effect.flatMap(route.handler, Respondable.toResponse)
|
|
255
257
|
return Effect.locally(
|
|
256
258
|
(route.uninterruptible ?
|
|
257
|
-
|
|
258
|
-
Effect.interruptible(
|
|
259
|
+
handlerResponse :
|
|
260
|
+
Effect.interruptible(handlerResponse)) as Effect.Effect<
|
|
259
261
|
ServerResponse.HttpServerResponse,
|
|
260
262
|
E,
|
|
261
263
|
Router.HttpRouter.ExcludeProvided<R>
|
|
@@ -505,11 +507,11 @@ export const options = route("OPTIONS")
|
|
|
505
507
|
/** @internal */
|
|
506
508
|
export const use = dual<
|
|
507
509
|
<E, R, R1, E1>(
|
|
508
|
-
f: (self: Router.Route.Handler<E, R>) => App.
|
|
510
|
+
f: (self: Router.Route.Handler<E, R>) => App.HttpApp<Respondable.Respondable, E1, R1>
|
|
509
511
|
) => (self: Router.HttpRouter<E, R>) => Router.HttpRouter<E1, Router.HttpRouter.ExcludeProvided<R1>>,
|
|
510
512
|
<E, R, R1, E1>(
|
|
511
513
|
self: Router.HttpRouter<E, R>,
|
|
512
|
-
f: (self: Router.Route.Handler<E, R>) => App.
|
|
514
|
+
f: (self: Router.Route.Handler<E, R>) => App.HttpApp<Respondable.Respondable, E1, R1>
|
|
513
515
|
) => Router.HttpRouter<E1, Router.HttpRouter.ExcludeProvided<R1>>
|
|
514
516
|
>(2, (self, f) =>
|
|
515
517
|
new RouterImpl<any, any>(
|
|
@@ -716,7 +718,7 @@ const makeService = <E, R>(): Router.HttpRouter.Service<E, R> => {
|
|
|
716
718
|
/* @internal */
|
|
717
719
|
export const Tag =
|
|
718
720
|
<const Name extends string>(id: Name) =>
|
|
719
|
-
<Self,
|
|
721
|
+
<Self, R = never, E = unknown>(): Router.HttpRouter.TagClass<Self, Name, E, R> => {
|
|
720
722
|
const Err = globalThis.Error as any
|
|
721
723
|
const limit = Err.stackTraceLimit
|
|
722
724
|
Err.stackTraceLimit = 2
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as Cause from "effect/Cause"
|
|
2
|
+
import type * as Exit from "effect/Exit"
|
|
2
3
|
import * as FiberId from "effect/FiberId"
|
|
3
4
|
import { globalValue } from "effect/GlobalValue"
|
|
4
5
|
import * as Option from "effect/Option"
|
|
5
6
|
import * as Predicate from "effect/Predicate"
|
|
6
7
|
import type * as Error from "../HttpServerError.js"
|
|
8
|
+
import type { HttpServerResponse } from "../HttpServerResponse.js"
|
|
9
|
+
import * as internalServerResponse from "./httpServerResponse.js"
|
|
7
10
|
|
|
8
11
|
/** @internal */
|
|
9
12
|
export const TypeId: Error.TypeId = Symbol.for(
|
|
@@ -28,10 +31,36 @@ export const isClientAbortCause = <E>(cause: Cause.Cause<E>): boolean =>
|
|
|
28
31
|
)
|
|
29
32
|
|
|
30
33
|
/** @internal */
|
|
34
|
+
export const causeStatusStripped = <E>(
|
|
35
|
+
cause: Cause.Cause<E>
|
|
36
|
+
): readonly [status: number, cause: Option.Option<Cause.Cause<E>>] => {
|
|
37
|
+
if (Cause.isInterruptedOnly(cause)) {
|
|
38
|
+
return [isClientAbortCause(cause) ? 499 : 503, Option.some(cause)]
|
|
39
|
+
}
|
|
40
|
+
let response: HttpServerResponse | undefined
|
|
41
|
+
const stripped = Cause.stripSomeDefects(cause, (defect) => {
|
|
42
|
+
if (internalServerResponse.isServerResponse(defect)) {
|
|
43
|
+
response = defect
|
|
44
|
+
return Option.some(Cause.die(defect))
|
|
45
|
+
}
|
|
46
|
+
return Option.none()
|
|
47
|
+
})
|
|
48
|
+
return [response?.status ?? 500, stripped]
|
|
49
|
+
}
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
const internalServerError = internalServerResponse.empty({ status: 500 })
|
|
52
|
+
|
|
53
|
+
/** @internal */
|
|
54
|
+
export const exitResponse = <E>(exit: Exit.Exit<HttpServerResponse, E>): HttpServerResponse => {
|
|
55
|
+
if (exit._tag === "Success") {
|
|
56
|
+
return exit.value
|
|
57
|
+
}
|
|
58
|
+
return Cause.reduce(
|
|
59
|
+
exit.cause,
|
|
60
|
+
internalServerError,
|
|
61
|
+
(_, cause) =>
|
|
62
|
+
cause._tag === "Die" && internalServerResponse.isServerResponse(cause.defect)
|
|
63
|
+
? Option.some(cause.defect)
|
|
64
|
+
: Option.none()
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -343,3 +343,14 @@ class ServerRequestImpl extends Inspectable.Class implements ServerRequest.HttpS
|
|
|
343
343
|
)
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
+
|
|
347
|
+
/** @internal */
|
|
348
|
+
export const toURL = (self: ServerRequest.HttpServerRequest): Option.Option<URL> => {
|
|
349
|
+
const host = self.headers.host ?? "localhost"
|
|
350
|
+
const protocol = self.headers["x-forwarded-proto"] === "https" ? "https" : "http"
|
|
351
|
+
try {
|
|
352
|
+
return Option.some(new URL(self.url, `${protocol}://${host}`))
|
|
353
|
+
} catch (_) {
|
|
354
|
+
return Option.none()
|
|
355
|
+
}
|
|
356
|
+
}
|
|
@@ -11,6 +11,7 @@ import type * as FileSystem from "../FileSystem.js"
|
|
|
11
11
|
import * as Headers from "../Headers.js"
|
|
12
12
|
import type * as Body from "../HttpBody.js"
|
|
13
13
|
import * as Platform from "../HttpPlatform.js"
|
|
14
|
+
import type * as Respondable from "../HttpServerRespondable.js"
|
|
14
15
|
import type * as ServerResponse from "../HttpServerResponse.js"
|
|
15
16
|
import * as Template from "../Template.js"
|
|
16
17
|
import * as UrlParams from "../UrlParams.js"
|
|
@@ -19,6 +20,10 @@ import * as internalBody from "./httpBody.js"
|
|
|
19
20
|
/** @internal */
|
|
20
21
|
export const TypeId: ServerResponse.TypeId = Symbol.for("@effect/platform/HttpServerResponse") as ServerResponse.TypeId
|
|
21
22
|
|
|
23
|
+
const respondableSymbol: typeof Respondable.symbol = Symbol.for(
|
|
24
|
+
"@effect/platform/HttpServerRespondable"
|
|
25
|
+
) as typeof Respondable.symbol
|
|
26
|
+
|
|
22
27
|
class ServerResponseImpl extends Effectable.StructuralClass<ServerResponse.HttpServerResponse>
|
|
23
28
|
implements ServerResponse.HttpServerResponse
|
|
24
29
|
{
|
|
@@ -51,6 +56,10 @@ class ServerResponseImpl extends Effectable.StructuralClass<ServerResponse.HttpS
|
|
|
51
56
|
return Effect.succeed(this)
|
|
52
57
|
}
|
|
53
58
|
|
|
59
|
+
[respondableSymbol](): Effect.Effect<ServerResponse.HttpServerResponse, unknown> {
|
|
60
|
+
return Effect.succeed(this)
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
[Inspectable.NodeInspectSymbol]() {
|
|
55
64
|
return this.toJSON()
|
|
56
65
|
}
|