@effect/platform 0.13.1 → 0.13.3

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 (50) hide show
  1. package/Http/Headers.d.ts +3 -3
  2. package/Http/Headers.d.ts.map +1 -1
  3. package/Http/Headers.js +16 -11
  4. package/Http/Headers.js.map +1 -1
  5. package/Http/IncomingMessage.d.ts.map +1 -1
  6. package/Http/IncomingMessage.js +1 -1
  7. package/Http/IncomingMessage.js.map +1 -1
  8. package/Http/Middleware.d.ts +0 -8
  9. package/Http/Middleware.d.ts.map +1 -1
  10. package/Http/Middleware.js +2 -8
  11. package/Http/Middleware.js.map +1 -1
  12. package/Http/Router.d.ts +90 -0
  13. package/Http/Router.d.ts.map +1 -1
  14. package/Http/Router.js +43 -1
  15. package/Http/Router.js.map +1 -1
  16. package/internal/http/client.js +1 -1
  17. package/internal/http/client.js.map +1 -1
  18. package/internal/http/middleware.js +9 -11
  19. package/internal/http/middleware.js.map +1 -1
  20. package/internal/http/router.d.ts +3 -1
  21. package/internal/http/router.d.ts.map +1 -1
  22. package/internal/http/router.js +38 -17
  23. package/internal/http/router.js.map +1 -1
  24. package/internal/http/serverResponse.js +15 -12
  25. package/internal/http/serverResponse.js.map +1 -1
  26. package/mjs/Http/Headers.mjs +16 -11
  27. package/mjs/Http/Headers.mjs.map +1 -1
  28. package/mjs/Http/IncomingMessage.mjs +1 -1
  29. package/mjs/Http/IncomingMessage.mjs.map +1 -1
  30. package/mjs/Http/Middleware.mjs +0 -5
  31. package/mjs/Http/Middleware.mjs.map +1 -1
  32. package/mjs/Http/Router.mjs +35 -0
  33. package/mjs/Http/Router.mjs.map +1 -1
  34. package/mjs/internal/http/client.mjs +1 -1
  35. package/mjs/internal/http/client.mjs.map +1 -1
  36. package/mjs/internal/http/middleware.mjs +9 -10
  37. package/mjs/internal/http/middleware.mjs.map +1 -1
  38. package/mjs/internal/http/router.mjs +30 -16
  39. package/mjs/internal/http/router.mjs.map +1 -1
  40. package/mjs/internal/http/serverResponse.mjs +15 -12
  41. package/mjs/internal/http/serverResponse.mjs.map +1 -1
  42. package/package.json +4 -4
  43. package/src/Http/Headers.ts +23 -17
  44. package/src/Http/IncomingMessage.ts +1 -2
  45. package/src/Http/Middleware.ts +0 -14
  46. package/src/Http/Router.ts +118 -0
  47. package/src/internal/http/client.ts +18 -21
  48. package/src/internal/http/middleware.ts +23 -31
  49. package/src/internal/http/router.ts +158 -24
  50. package/src/internal/http/serverResponse.ts +15 -13
@@ -1,6 +1,5 @@
1
- import { dual } from "@effect/data/Function"
1
+ import { flow } from "@effect/data/Function"
2
2
  import * as Effect from "@effect/io/Effect"
3
- import type * as App from "@effect/platform/Http/App"
4
3
  import * as Headers from "@effect/platform/Http/Headers"
5
4
  import type * as Middleware from "@effect/platform/Http/Middleware"
6
5
  import * as ServerRequest from "@effect/platform/Http/ServerRequest"
@@ -11,16 +10,21 @@ export const make = <M extends Middleware.Middleware>(middleware: M): M => middl
11
10
  /** @internal */
12
11
  export const logger = make((httpApp) =>
13
12
  Effect.withLogSpan(
14
- Effect.onExit(
15
- httpApp,
16
- (exit) =>
17
- Effect.flatMap(
18
- ServerRequest.ServerRequest,
19
- (request) =>
20
- Effect.annotateLogs(Effect.log("", exit._tag === "Failure" ? exit.cause : undefined), {
13
+ Effect.flatMap(
14
+ ServerRequest.ServerRequest,
15
+ (request) =>
16
+ Effect.tap(
17
+ Effect.tapErrorCause(httpApp, (cause) =>
18
+ Effect.annotateLogs(Effect.log(cause), {
19
+ "http.method": request.method,
20
+ "http.url": request.url,
21
+ "http.status": 500
22
+ })),
23
+ (response) =>
24
+ Effect.annotateLogs(Effect.log(""), {
21
25
  "http.method": request.method,
22
26
  "http.url": request.url,
23
- "http.status": exit._tag === "Success" ? exit.value.status : 500
27
+ "http.status": response.status
24
28
  })
25
29
  )
26
30
  ),
@@ -46,27 +50,15 @@ export const tracer = make((httpApp) =>
46
50
 
47
51
  /** @internal */
48
52
  export const xForwardedHeaders = make((httpApp) =>
49
- Effect.flatMap(ServerRequest.ServerRequest, (request) => {
50
- const forwardedHost = Headers.get(request.headers, "x-forwarded-host")
51
- return forwardedHost._tag === "Some"
52
- ? Effect.updateService(httpApp, ServerRequest.ServerRequest, (_) =>
53
- _.replaceHeaders(Headers.set(request.headers, "host", forwardedHost.value)))
54
- : httpApp
55
- })
53
+ Effect.updateService(httpApp, ServerRequest.ServerRequest, (request) =>
54
+ request.headers["x-forwarded-host"]
55
+ ? request.replaceHeaders(Headers.set(
56
+ request.headers,
57
+ "host",
58
+ request.headers["x-forwarded-host"]
59
+ ))
60
+ : request)
56
61
  )
57
62
 
58
63
  /** @internal */
59
- export const compose = dual<
60
- <B extends App.Default<any, any>, C extends App.Default<any, any>>(
61
- that: (b: B) => C
62
- ) => <A extends App.Default<any, any>>(
63
- self: (a: A) => B
64
- ) => (a: A) => C,
65
- <A extends App.Default<any, any>, B extends App.Default<any, any>, C extends App.Default<any, any>>(
66
- self: (a: A) => B,
67
- that: (b: B) => C
68
- ) => (a: A) => C
69
- >(2, (self, that) => (inApp) => that(self(inApp)))
70
-
71
- /** @internal */
72
- export const loggerTracer = compose(tracer, logger)
64
+ export const loggerTracer = flow(tracer, logger)
@@ -5,6 +5,7 @@ import { dual } from "@effect/data/Function"
5
5
  import * as Hash from "@effect/data/Hash"
6
6
  import * as Option from "@effect/data/Option"
7
7
  import { pipeArguments } from "@effect/data/Pipeable"
8
+ import type * as Cause from "@effect/io/Cause"
8
9
  import * as Effect from "@effect/io/Effect"
9
10
  import type * as App from "@effect/platform/Http/App"
10
11
  import type * as Method from "@effect/platform/Http/Method"
@@ -95,15 +96,12 @@ const toHttpApp = <R, E>(
95
96
  self: Router.Router<R, E>
96
97
  ): App.Default<Exclude<R, Router.RouteContext>, E | Error.RouteNotFound> => {
97
98
  const router = FindMyWay()
98
- Chunk.forEach(self.mounts, ([path, app]) => {
99
- const fn = () => {}
100
- fn.handler = Effect.updateService(app, ServerRequest.ServerRequest, (request) => sliceRequestUrl(request, path))
101
- router.all(path, fn)
102
- router.all(path + "/*", fn)
103
- })
99
+ const mounts = Chunk.toReadonlyArray(self.mounts)
100
+ const mountsLen = mounts.length
104
101
  Chunk.forEach(self.routes, (route) => {
105
- const fn = () => {}
106
- fn.handler = route
102
+ function fn() {
103
+ return route
104
+ }
107
105
  if (route.method === "*") {
108
106
  router.all(route.path, fn)
109
107
  } else {
@@ -113,27 +111,36 @@ const toHttpApp = <R, E>(
113
111
  return Effect.flatMap(
114
112
  ServerRequest.ServerRequest,
115
113
  (request): App.Default<Exclude<R, Router.RouteContext>, E | Error.RouteNotFound> => {
114
+ if (mountsLen > 0) {
115
+ for (let i = 0; i < mountsLen; i++) {
116
+ const [path, app] = mounts[i]
117
+ if (request.url.startsWith(path)) {
118
+ return Effect.provideService(
119
+ app,
120
+ ServerRequest.ServerRequest,
121
+ sliceRequestUrl(request, path)
122
+ ) as App.Default<Exclude<R, Router.RouteContext>, E>
123
+ }
124
+ }
125
+ }
126
+
116
127
  const result = router.find(request.method as HTTPMethod, request.url)
117
128
  if (result === null) {
118
129
  return Effect.fail(Error.RouteNotFound({ request }))
119
130
  }
120
- const handler = (result.handler as any).handler
121
- if (RouteTypeId in handler) {
122
- const route = handler as Router.Route<R, E>
123
- if (route.prefix._tag === "Some") {
124
- request = sliceRequestUrl(request, route.prefix.value)
125
- }
126
- return Effect.mapInputContext(
127
- route.handler,
128
- (context) =>
129
- Context.add(
130
- Context.add(context, ServerRequest.ServerRequest, request),
131
- RouteContext,
132
- new RouteContextImpl(result.params, result.searchParams)
133
- ) as Context.Context<R>
134
- )
131
+ const route = (result.handler as any)() as Router.Route<R, E>
132
+ if (route.prefix._tag === "Some") {
133
+ request = sliceRequestUrl(request, route.prefix.value)
135
134
  }
136
- return (handler as App.Default<Exclude<R, Router.RouteContext>, E>)
135
+ return Effect.mapInputContext(
136
+ route.handler,
137
+ (context) =>
138
+ Context.add(
139
+ Context.add(context, ServerRequest.ServerRequest, request),
140
+ RouteContext,
141
+ new RouteContextImpl(result.params, result.searchParams)
142
+ ) as Context.Context<R>
143
+ )
137
144
  }
138
145
  )
139
146
  }
@@ -296,3 +303,130 @@ export const head = route("HEAD")
296
303
 
297
304
  /** @internal */
298
305
  export const options = route("OPTIONS")
306
+
307
+ /** @internal */
308
+ export const transform = dual<
309
+ <R, E, R1, E1>(
310
+ f: (self: Router.Route.Handler<R, E>) => Router.Route.Handler<R1, E1>
311
+ ) => (self: Router.Router<R, E>) => Router.Router<R1, E1>,
312
+ <R, E, R1, E1>(
313
+ self: Router.Router<R, E>,
314
+ f: (self: Router.Route.Handler<R, E>) => Router.Route.Handler<R1, E1>
315
+ ) => Router.Router<R1, E1>
316
+ >(2, (self, f) =>
317
+ new RouterImpl(
318
+ Chunk.map(
319
+ self.routes,
320
+ (route) => new RouteImpl(route.method, route.path, f(route.handler), route.prefix)
321
+ ),
322
+ Chunk.map(
323
+ self.mounts,
324
+ ([path, app]) => [path, f(app as any)]
325
+ )
326
+ ))
327
+
328
+ /** @internal */
329
+ export const catchAll = dual<
330
+ <E, R2, E2>(
331
+ f: (e: E) => Router.Route.Handler<R2, E2>
332
+ ) => <R>(self: Router.Router<R, E>) => Router.Router<R2 | R, E2>,
333
+ <R, E, R2, E2>(
334
+ self: Router.Router<R, E>,
335
+ f: (e: E) => Router.Route.Handler<R2, E2>
336
+ ) => Router.Router<R2 | R, E2>
337
+ >(2, (self, f) => transform(self, Effect.catchAll(f)))
338
+
339
+ /** @internal */
340
+ export const catchAllCause = dual<
341
+ <E, R2, E2>(
342
+ f: (e: Cause.Cause<E>) => Router.Route.Handler<R2, E2>
343
+ ) => <R>(self: Router.Router<R, E>) => Router.Router<R2 | R, E2>,
344
+ <R, E, R2, E2>(
345
+ self: Router.Router<R, E>,
346
+ f: (e: Cause.Cause<E>) => Router.Route.Handler<R2, E2>
347
+ ) => Router.Router<R2 | R, E2>
348
+ >(2, (self, f) => transform(self, Effect.catchAllCause(f)))
349
+
350
+ /** @internal */
351
+ export const catchTag = dual<
352
+ <K extends (E extends { _tag: string } ? E["_tag"] : never), E, R1, E1>(
353
+ k: K,
354
+ f: (e: Extract<E, { _tag: K }>) => Router.Route.Handler<R1, E1>
355
+ ) => <R>(self: Router.Router<R, E>) => Router.Router<R | R1, Exclude<E, { _tag: K }> | E1>,
356
+ <R, E, K extends (E extends { _tag: string } ? E["_tag"] : never), R1, E1>(
357
+ self: Router.Router<R, E>,
358
+ k: K,
359
+ f: (e: Extract<E, { _tag: K }>) => Router.Route.Handler<R1, E1>
360
+ ) => Router.Router<R | R1, Exclude<E, { _tag: K }> | E1>
361
+ >(3, (self, k, f) => transform(self, Effect.catchTag(k, f)))
362
+
363
+ /** @internal */
364
+ export const catchTags: {
365
+ <
366
+ E,
367
+ Cases extends (E extends { _tag: string } ? {
368
+ [K in E["_tag"]]+?: (error: Extract<E, { _tag: K }>) => Router.Route.Handler<any, any>
369
+ } :
370
+ {})
371
+ >(
372
+ cases: Cases
373
+ ): <R>(self: Router.Router<R, E>) => Router.Router<
374
+ | R
375
+ | {
376
+ [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<infer R, any, any>) ? R : never
377
+ }[keyof Cases],
378
+ | Exclude<E, { _tag: keyof Cases }>
379
+ | {
380
+ [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<any, infer E, any>) ? E : never
381
+ }[keyof Cases]
382
+ >
383
+ <
384
+ R,
385
+ E,
386
+ Cases extends (E extends { _tag: string } ? {
387
+ [K in E["_tag"]]+?: (error: Extract<E, { _tag: K }>) => Router.Route.Handler<any, any>
388
+ } :
389
+ {})
390
+ >(
391
+ self: Router.Router<R, E>,
392
+ cases: Cases
393
+ ): Router.Router<
394
+ | R
395
+ | {
396
+ [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<infer R, any, any>) ? R : never
397
+ }[keyof Cases],
398
+ | Exclude<E, { _tag: keyof Cases }>
399
+ | {
400
+ [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<any, infer E, any>) ? E : never
401
+ }[keyof Cases]
402
+ >
403
+ } = dual(2, (self: Router.Router<any, any>, cases: {}) => transform(self, Effect.catchTags(cases)))
404
+
405
+ export const provideService = dual<
406
+ <T extends Context.Tag<any, any>>(
407
+ tag: T,
408
+ service: Context.Tag.Service<T>
409
+ ) => <R, E>(self: Router.Router<R, E>) => Router.Router<Exclude<R, Context.Tag.Identifier<T>>, E>,
410
+ <R, E, T extends Context.Tag<any, any>>(
411
+ self: Router.Router<R, E>,
412
+ tag: T,
413
+ service: Context.Tag.Service<T>
414
+ ) => Router.Router<Exclude<R, Context.Tag.Identifier<T>>, E>
415
+ >(3, (self, tag, service) => transform(self, Effect.provideService(tag, service)))
416
+
417
+ /* @internal */
418
+ export const provideServiceEffect = dual<
419
+ <T extends Context.Tag<any, any>, R1, E1>(
420
+ tag: T,
421
+ effect: Effect.Effect<R1, E1, Context.Tag.Service<T>>
422
+ ) => <R, E>(self: Router.Router<R, E>) => Router.Router<R1 | Exclude<R, Context.Tag.Identifier<T>>, E | E1>,
423
+ <R, E, T extends Context.Tag<any, any>, R1, E1>(
424
+ self: Router.Router<R, E>,
425
+ tag: T,
426
+ effect: Effect.Effect<R1, E1, Context.Tag.Service<T>>
427
+ ) => Router.Router<R1 | Exclude<R, Context.Tag.Identifier<T>>, E | E1>
428
+ >(3, <R, E, T extends Context.Tag<any, any>, R1, E1>(
429
+ self: Router.Router<R, E>,
430
+ tag: T,
431
+ effect: Effect.Effect<R1, E1, Context.Tag.Service<T>>
432
+ ) => transform(self, Effect.provideServiceEffect(tag, effect)))
@@ -1,5 +1,4 @@
1
1
  import { dual } from "@effect/data/Function"
2
- import * as Option from "@effect/data/Option"
3
2
  import { pipeArguments } from "@effect/data/Pipeable"
4
3
  import * as Effect from "@effect/io/Effect"
5
4
  import type * as PlatformError from "@effect/platform/Error"
@@ -20,13 +19,26 @@ export const TypeId: ServerResponse.TypeId = Symbol.for("@effect/platform/Http/S
20
19
 
21
20
  class ServerResponseImpl implements ServerResponse.ServerResponse {
22
21
  readonly [TypeId]: ServerResponse.TypeId
22
+ readonly headers: Headers.Headers
23
23
  constructor(
24
24
  readonly status: number,
25
25
  readonly statusText: string | undefined,
26
- readonly headers: Headers.Headers,
26
+ headers: Headers.Headers,
27
27
  readonly body: Body.Body
28
28
  ) {
29
29
  this[TypeId] = TypeId
30
+ if (body.contentType || body.contentLength) {
31
+ const newHeaders = { ...headers }
32
+ if (body.contentType) {
33
+ newHeaders["content-type"] = body.contentType
34
+ }
35
+ if (body.contentLength) {
36
+ newHeaders["content-length"] = body.contentLength.toString()
37
+ }
38
+ this.headers = newHeaders
39
+ } else {
40
+ this.headers = headers
41
+ }
30
42
  }
31
43
  pipe() {
32
44
  return pipeArguments(this, arguments)
@@ -197,7 +209,7 @@ export const getContentType = (options?: ServerResponse.Options): string | undef
197
209
  if (options?.contentType) {
198
210
  return options.contentType
199
211
  } else if (options?.headers) {
200
- return Option.getOrUndefined(Headers.get("content-type")(options.headers))
212
+ return options.headers["content-type"]
201
213
  } else {
202
214
  return
203
215
  }
@@ -247,16 +259,6 @@ export const setBody = dual<
247
259
  let headers = self.headers
248
260
  if (body._tag === "Empty") {
249
261
  headers = Headers.remove(Headers.remove(headers, "Content-Type"), "Content-length")
250
- } else {
251
- const contentType = body.contentType
252
- if (contentType) {
253
- headers = Headers.set(headers, "content-type", contentType)
254
- }
255
-
256
- const contentLength = body.contentLength
257
- if (contentLength) {
258
- headers = Headers.set(headers, "content-length", contentLength.toString())
259
- }
260
262
  }
261
263
  return new ServerResponseImpl(
262
264
  self.status,