@effect/platform 0.11.5 → 0.12.1

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 (193) hide show
  1. package/FileSystem.d.ts +43 -11
  2. package/FileSystem.d.ts.map +1 -1
  3. package/FileSystem.js +33 -3
  4. package/FileSystem.js.map +1 -1
  5. package/Http/App.d.ts +18 -0
  6. package/Http/App.d.ts.map +1 -0
  7. package/Http/App.js +6 -0
  8. package/Http/App.js.map +1 -0
  9. package/Http/Body.d.ts +37 -14
  10. package/Http/Body.d.ts.map +1 -1
  11. package/Http/Body.js +17 -5
  12. package/Http/Body.js.map +1 -1
  13. package/Http/Client.d.ts +2 -2
  14. package/Http/Client.d.ts.map +1 -1
  15. package/Http/Client.js +3 -3
  16. package/Http/Client.js.map +1 -1
  17. package/Http/ClientRequest.d.ts +44 -1
  18. package/Http/ClientRequest.d.ts.map +1 -1
  19. package/Http/ClientRequest.js +21 -3
  20. package/Http/ClientRequest.js.map +1 -1
  21. package/Http/ClientResponse.d.ts +13 -1
  22. package/Http/ClientResponse.d.ts.map +1 -1
  23. package/Http/ClientResponse.js +14 -2
  24. package/Http/ClientResponse.js.map +1 -1
  25. package/Http/FormData.d.ts +97 -4
  26. package/Http/FormData.d.ts.map +1 -1
  27. package/Http/FormData.js +77 -2
  28. package/Http/FormData.js.map +1 -1
  29. package/Http/Headers.d.ts +29 -3
  30. package/Http/Headers.d.ts.map +1 -1
  31. package/Http/Headers.js +13 -1
  32. package/Http/Headers.js.map +1 -1
  33. package/Http/IncomingMessage.d.ts +26 -5
  34. package/Http/IncomingMessage.d.ts.map +1 -1
  35. package/Http/IncomingMessage.js +38 -3
  36. package/Http/IncomingMessage.js.map +1 -1
  37. package/Http/Middleware.d.ts +56 -0
  38. package/Http/Middleware.d.ts.map +1 -0
  39. package/Http/Middleware.js +46 -0
  40. package/Http/Middleware.js.map +1 -0
  41. package/Http/Router.d.ts +221 -0
  42. package/Http/Router.d.ts.map +1 -0
  43. package/Http/Router.js +148 -0
  44. package/Http/Router.js.map +1 -0
  45. package/Http/Server.d.ts +85 -0
  46. package/Http/Server.d.ts.map +1 -0
  47. package/Http/Server.js +34 -0
  48. package/Http/Server.js.map +1 -0
  49. package/Http/ServerError.d.ts +96 -0
  50. package/Http/ServerError.d.ts.map +1 -0
  51. package/Http/ServerError.js +40 -0
  52. package/Http/ServerError.js.map +1 -0
  53. package/Http/ServerRequest.d.ts +82 -0
  54. package/Http/ServerRequest.d.ts.map +1 -0
  55. package/Http/ServerRequest.js +66 -0
  56. package/Http/ServerRequest.js.map +1 -0
  57. package/Http/ServerResponse.d.ts +168 -0
  58. package/Http/ServerResponse.d.ts.map +1 -0
  59. package/Http/ServerResponse.js +116 -0
  60. package/Http/ServerResponse.js.map +1 -0
  61. package/Http/UrlParams.d.ts +20 -5
  62. package/Http/UrlParams.d.ts.map +1 -1
  63. package/Http/UrlParams.js.map +1 -1
  64. package/HttpServer.d.ts +44 -0
  65. package/HttpServer.d.ts.map +1 -0
  66. package/HttpServer.js +29 -0
  67. package/HttpServer.js.map +1 -0
  68. package/internal/fileSystem.js +33 -12
  69. package/internal/fileSystem.js.map +1 -1
  70. package/internal/http/body.js +28 -28
  71. package/internal/http/body.js.map +1 -1
  72. package/internal/http/client.d.ts.map +1 -1
  73. package/internal/http/client.js +32 -26
  74. package/internal/http/client.js.map +1 -1
  75. package/internal/http/clientRequest.js +22 -6
  76. package/internal/http/clientRequest.js.map +1 -1
  77. package/internal/http/clientResponse.js +15 -12
  78. package/internal/http/clientResponse.js.map +1 -1
  79. package/internal/http/formData.d.ts +8 -0
  80. package/internal/http/formData.d.ts.map +1 -0
  81. package/internal/http/formData.js +88 -0
  82. package/internal/http/formData.js.map +1 -0
  83. package/internal/http/middleware.d.ts +2 -0
  84. package/internal/http/middleware.d.ts.map +1 -0
  85. package/internal/http/middleware.js +43 -0
  86. package/internal/http/middleware.js.map +1 -0
  87. package/internal/http/router.d.ts +2 -0
  88. package/internal/http/router.d.ts.map +1 -0
  89. package/internal/http/router.js +187 -0
  90. package/internal/http/router.js.map +1 -0
  91. package/internal/http/server.d.ts +2 -0
  92. package/internal/http/server.d.ts.map +1 -0
  93. package/internal/http/server.js +30 -0
  94. package/internal/http/server.js.map +1 -0
  95. package/internal/http/serverError.d.ts +2 -0
  96. package/internal/http/serverError.d.ts.map +1 -0
  97. package/internal/http/serverError.js +30 -0
  98. package/internal/http/serverError.js.map +1 -0
  99. package/internal/http/serverRequest.d.ts +2 -0
  100. package/internal/http/serverRequest.d.ts.map +1 -0
  101. package/internal/http/serverRequest.js +57 -0
  102. package/internal/http/serverRequest.js.map +1 -0
  103. package/internal/http/serverResponse.d.ts +2 -0
  104. package/internal/http/serverResponse.d.ts.map +1 -0
  105. package/internal/http/serverResponse.js +109 -0
  106. package/internal/http/serverResponse.js.map +1 -0
  107. package/mjs/FileSystem.mjs +26 -1
  108. package/mjs/FileSystem.mjs.map +1 -1
  109. package/mjs/Http/App.mjs +2 -0
  110. package/mjs/Http/App.mjs.map +1 -0
  111. package/mjs/Http/Body.mjs +12 -2
  112. package/mjs/Http/Body.mjs.map +1 -1
  113. package/mjs/Http/Client.mjs +1 -1
  114. package/mjs/Http/Client.mjs.map +1 -1
  115. package/mjs/Http/ClientRequest.mjs +16 -1
  116. package/mjs/Http/ClientRequest.mjs.map +1 -1
  117. package/mjs/Http/ClientResponse.mjs +11 -1
  118. package/mjs/Http/ClientResponse.mjs.map +1 -1
  119. package/mjs/Http/FormData.mjs +62 -1
  120. package/mjs/Http/FormData.mjs.map +1 -1
  121. package/mjs/Http/Headers.mjs +10 -0
  122. package/mjs/Http/Headers.mjs.map +1 -1
  123. package/mjs/Http/IncomingMessage.mjs +32 -1
  124. package/mjs/Http/IncomingMessage.mjs.map +1 -1
  125. package/mjs/Http/Middleware.mjs +32 -0
  126. package/mjs/Http/Middleware.mjs.map +1 -0
  127. package/mjs/Http/Router.mjs +117 -0
  128. package/mjs/Http/Router.mjs.map +1 -0
  129. package/mjs/Http/Server.mjs +22 -0
  130. package/mjs/Http/Server.mjs.map +1 -0
  131. package/mjs/Http/ServerError.mjs +27 -0
  132. package/mjs/Http/ServerError.mjs.map +1 -0
  133. package/mjs/Http/ServerRequest.mjs +48 -0
  134. package/mjs/Http/ServerRequest.mjs.map +1 -0
  135. package/mjs/Http/ServerResponse.mjs +90 -0
  136. package/mjs/Http/ServerResponse.mjs.map +1 -0
  137. package/mjs/Http/UrlParams.mjs.map +1 -1
  138. package/mjs/HttpServer.mjs +24 -0
  139. package/mjs/HttpServer.mjs.map +1 -0
  140. package/mjs/internal/fileSystem.mjs +27 -11
  141. package/mjs/internal/fileSystem.mjs.map +1 -1
  142. package/mjs/internal/http/body.mjs +23 -25
  143. package/mjs/internal/http/body.mjs.map +1 -1
  144. package/mjs/internal/http/client.mjs +30 -24
  145. package/mjs/internal/http/client.mjs.map +1 -1
  146. package/mjs/internal/http/clientRequest.mjs +16 -4
  147. package/mjs/internal/http/clientRequest.mjs.map +1 -1
  148. package/mjs/internal/http/clientResponse.mjs +15 -12
  149. package/mjs/internal/http/clientResponse.mjs.map +1 -1
  150. package/mjs/internal/http/formData.mjs +67 -0
  151. package/mjs/internal/http/formData.mjs.map +1 -0
  152. package/mjs/internal/http/middleware.mjs +29 -0
  153. package/mjs/internal/http/middleware.mjs.map +1 -0
  154. package/mjs/internal/http/router.mjs +155 -0
  155. package/mjs/internal/http/router.mjs.map +1 -0
  156. package/mjs/internal/http/server.mjs +17 -0
  157. package/mjs/internal/http/server.mjs.map +1 -0
  158. package/mjs/internal/http/serverError.mjs +17 -0
  159. package/mjs/internal/http/serverError.mjs.map +1 -0
  160. package/mjs/internal/http/serverRequest.mjs +41 -0
  161. package/mjs/internal/http/serverRequest.mjs.map +1 -0
  162. package/mjs/internal/http/serverResponse.mjs +82 -0
  163. package/mjs/internal/http/serverResponse.mjs.map +1 -0
  164. package/package.json +2 -1
  165. package/src/FileSystem.ts +52 -27
  166. package/src/Http/App.ts +19 -0
  167. package/src/Http/Body.ts +41 -14
  168. package/src/Http/Client.ts +6 -3
  169. package/src/Http/ClientRequest.ts +52 -2
  170. package/src/Http/ClientResponse.ts +13 -1
  171. package/src/Http/FormData.ts +121 -5
  172. package/src/Http/Headers.ts +37 -3
  173. package/src/Http/IncomingMessage.ts +46 -3
  174. package/src/Http/Middleware.ts +69 -0
  175. package/src/Http/Router.ts +342 -0
  176. package/src/Http/Server.ts +134 -0
  177. package/src/Http/ServerError.ts +111 -0
  178. package/src/Http/ServerRequest.ts +120 -0
  179. package/src/Http/ServerResponse.ts +202 -0
  180. package/src/Http/UrlParams.ts +20 -5
  181. package/src/HttpServer.ts +44 -0
  182. package/src/internal/fileSystem.ts +28 -7
  183. package/src/internal/http/body.ts +65 -39
  184. package/src/internal/http/client.ts +45 -36
  185. package/src/internal/http/clientRequest.ts +64 -4
  186. package/src/internal/http/clientResponse.ts +21 -13
  187. package/src/internal/http/formData.ts +140 -0
  188. package/src/internal/http/middleware.ts +72 -0
  189. package/src/internal/http/router.ts +298 -0
  190. package/src/internal/http/server.ts +80 -0
  191. package/src/internal/http/serverError.ts +26 -0
  192. package/src/internal/http/serverRequest.ts +71 -0
  193. package/src/internal/http/serverResponse.ts +255 -0
@@ -9,6 +9,7 @@ import type * as Body from "@effect/platform/Http/Body"
9
9
  import type * as Client from "@effect/platform/Http/Client"
10
10
  import type * as Error from "@effect/platform/Http/ClientError"
11
11
  import type * as ClientRequest from "@effect/platform/Http/ClientRequest"
12
+ import type * as ClientResponse from "@effect/platform/Http/ClientResponse"
12
13
  import * as Method from "@effect/platform/Http/Method"
13
14
  import * as UrlParams from "@effect/platform/Http/UrlParams"
14
15
  import * as internalBody from "@effect/platform/internal/http/body"
@@ -22,15 +23,33 @@ import * as Stream from "@effect/stream/Stream"
22
23
  /** @internal */
23
24
  export const tag = Context.Tag<Client.Client.Default>("@effect/platform/Http/Client")
24
25
 
26
+ const clientProto = {
27
+ pipe() {
28
+ return pipeArguments(this, arguments)
29
+ }
30
+ }
31
+
32
+ const setProto = <R, E, A>(
33
+ f: (request: ClientRequest.ClientRequest.NonEffectBody) => Effect.Effect<R, E, A>
34
+ ) => {
35
+ Object.setPrototypeOf(f, clientProto)
36
+ return f as Client.Client<R, E, A>
37
+ }
38
+
25
39
  /** @internal */
26
- export const make = <R, E, A>(
27
- f: (request: ClientRequest.ClientRequest) => Effect.Effect<R, E, A>
28
- ): Client.Client<R, E, A> =>
29
- Object.assign(f, {
30
- pipe() {
31
- return pipeArguments(this, arguments)
32
- }
33
- })
40
+ export const make = (
41
+ f: (
42
+ request: ClientRequest.ClientRequest.NonEffectBody
43
+ ) => Effect.Effect<never, Error.HttpClientError, ClientResponse.ClientResponse>
44
+ ): Client.Client.Default => {
45
+ function client(request: ClientRequest.ClientRequest) {
46
+ return Effect.flatMap(
47
+ internalRequest.resolveBody(request),
48
+ (request) => f(request)
49
+ )
50
+ }
51
+ return setProto(client)
52
+ }
34
53
 
35
54
  /** @internal */
36
55
  export const fetch = (
@@ -68,30 +87,20 @@ export const fetch = (
68
87
  (_) => internalResponse.fromWeb(request, _)
69
88
  )
70
89
  if (Method.hasBody(request.method)) {
71
- return request.body._tag === "BytesEffect" ?
72
- Effect.flatMap(
73
- Effect.mapError(request.body.body, (error) =>
74
- internalError.requestError({
75
- reason: "Encode",
76
- request,
77
- error
78
- })),
79
- (body) => send(body)
80
- ) :
81
- send(convertBody(request.body))
90
+ return send(convertBody(request.body))
82
91
  }
83
92
  return send(undefined)
84
93
  })
85
94
  )
86
95
  )
87
96
 
88
- const convertBody = (body: Exclude<Body.Body, Body.BytesEffect>): BodyInit | undefined => {
97
+ const convertBody = (body: Body.NonEffect): BodyInit | undefined => {
89
98
  switch (body._tag) {
90
99
  case "Empty":
91
100
  return undefined
92
101
  case "Raw":
93
102
  return body.body as any
94
- case "Bytes":
103
+ case "Uint8Array":
95
104
  return body.body
96
105
  case "FormData":
97
106
  return body.formData
@@ -105,7 +114,7 @@ export const fetchOk = (
105
114
  ): Client.Client.Default => filterStatusOk(fetch(options))
106
115
 
107
116
  /** @internal */
108
- export const fetchLayer = Layer.succeed(tag, fetch())
117
+ export const layer = Layer.succeed(tag, fetch())
109
118
 
110
119
  /** @internal */
111
120
  export const catchTag: {
@@ -127,7 +136,7 @@ export const catchTag: {
127
136
  tag: K,
128
137
  f: (e: Extract<E, { _tag: K }>) => Effect.Effect<R1, E1, A1>
129
138
  ): Client.Client<R1 | R, E1 | Exclude<E, { _tag: K }>, A1 | A> =>
130
- make((request) => Effect.catchTag(self(request), tag, f))
139
+ setProto((request) => Effect.catchTag(self(request), tag, f))
131
140
  )
132
141
 
133
142
  /** @internal */
@@ -237,7 +246,7 @@ export const catchTags: {
237
246
  ) => Effect.Effect<any, any, infer A> ? A
238
247
  : never
239
248
  }[keyof Cases]
240
- > => make((request) => Effect.catchTags(self(request), cases))
249
+ > => setProto((request) => Effect.catchTags(self(request), cases))
241
250
  )
242
251
 
243
252
  /** @internal */
@@ -254,7 +263,7 @@ export const catchAll: {
254
263
  <R, E, A, R2, E2, A2>(
255
264
  self: Client.Client<R, E, A>,
256
265
  f: (e: E) => Effect.Effect<R2, E2, A2>
257
- ): Client.Client<R | R2, E2, A2 | A> => make((request) => Effect.catchAll(self(request), f))
266
+ ): Client.Client<R | R2, E2, A2 | A> => setProto((request) => Effect.catchAll(self(request), f))
258
267
  )
259
268
 
260
269
  /** @internal */
@@ -267,7 +276,7 @@ export const filterOrElse = dual<
267
276
  f: Predicate.Predicate<A>,
268
277
  orElse: (a: A) => Effect.Effect<R2, E2, B>
269
278
  ) => Client.Client<R2 | R, E2 | E, A | B>
270
- >(3, (self, f, orElse) => make((request) => Effect.filterOrElse(self(request), f, orElse)))
279
+ >(3, (self, f, orElse) => setProto((request) => Effect.filterOrElse(self(request), f, orElse)))
271
280
 
272
281
  /** @internal */
273
282
  export const filterOrFail = dual<
@@ -279,7 +288,7 @@ export const filterOrFail = dual<
279
288
  f: Predicate.Predicate<A>,
280
289
  orFailWith: (a: A) => E2
281
290
  ) => Client.Client<R, E2 | E, A>
282
- >(3, (self, f, orFailWith) => make((request) => Effect.filterOrFail(self(request), f, orFailWith)))
291
+ >(3, (self, f, orFailWith) => setProto((request) => Effect.filterOrFail(self(request), f, orFailWith)))
283
292
 
284
293
  /** @internal */
285
294
  export const filterStatus = dual<
@@ -293,7 +302,7 @@ export const filterStatus = dual<
293
302
  >(
294
303
  2,
295
304
  (self, f) =>
296
- make((request) =>
305
+ setProto((request) =>
297
306
  Effect.filterOrFail(
298
307
  self(request),
299
308
  (response) => f(response.status),
@@ -322,7 +331,7 @@ export const map = dual<
322
331
  self: Client.Client<R, E, A>,
323
332
  f: (a: A) => B
324
333
  ) => Client.Client<R, E, B>
325
- >(2, (self, f) => make((request) => Effect.map(self(request), f)))
334
+ >(2, (self, f) => setProto((request) => Effect.map(self(request), f)))
326
335
 
327
336
  /** @internal */
328
337
  export const mapEffect = dual<
@@ -333,7 +342,7 @@ export const mapEffect = dual<
333
342
  self: Client.Client<R, E, A>,
334
343
  f: (a: A) => Effect.Effect<R2, E2, B>
335
344
  ) => Client.Client<R | R2, E | E2, B>
336
- >(2, (self, f) => make((request) => Effect.flatMap(self(request), f)))
345
+ >(2, (self, f) => setProto((request) => Effect.flatMap(self(request), f)))
337
346
 
338
347
  /** @internal */
339
348
  export const mapRequest = dual<
@@ -344,7 +353,7 @@ export const mapRequest = dual<
344
353
  self: Client.Client<R, E, A>,
345
354
  f: (a: ClientRequest.ClientRequest) => ClientRequest.ClientRequest
346
355
  ) => Client.Client<R, E, A>
347
- >(2, (self, f) => make((request) => self(f(request))))
356
+ >(2, (self, f) => setProto((request) => self(f(request))))
348
357
 
349
358
  /** @internal */
350
359
  export const mapRequestEffect = dual<
@@ -355,7 +364,7 @@ export const mapRequestEffect = dual<
355
364
  self: Client.Client<R, E, A>,
356
365
  f: (a: ClientRequest.ClientRequest) => Effect.Effect<R2, E2, ClientRequest.ClientRequest>
357
366
  ) => Client.Client<R | R2, E | E2, A>
358
- >(2, (self, f) => make((request) => Effect.flatMap(f(request), self)))
367
+ >(2, (self, f) => setProto((request) => Effect.flatMap(f(request), self)))
359
368
 
360
369
  /** @internal */
361
370
  export const retry: {
@@ -371,7 +380,7 @@ export const retry: {
371
380
  <R, E extends E0, E0, A, R1, B>(
372
381
  self: Client.Client<R, E, A>,
373
382
  policy: Schedule.Schedule<R1, E0, B>
374
- ): Client.Client<R | R1, E, A> => make((request) => Effect.retry(self(request), policy))
383
+ ): Client.Client<R | R1, E, A> => setProto((request) => Effect.retry(self(request), policy))
375
384
  )
376
385
 
377
386
  /** @internal */
@@ -405,7 +414,7 @@ export const schemaFunction = dual<
405
414
  (body) =>
406
415
  self(internalRequest.setBody(
407
416
  request,
408
- internalBody.bytes(body, "application/json")
417
+ internalBody.uint8Array(body, "application/json")
409
418
  ))
410
419
  )
411
420
  })
@@ -419,7 +428,7 @@ export const tap = dual<
419
428
  self: Client.Client<R, E, A>,
420
429
  f: (a: A) => Effect.Effect<R2, E2, _>
421
430
  ) => Client.Client<R | R2, E | E2, A>
422
- >(2, (self, f) => make((request) => Effect.tap(self(request), f)))
431
+ >(2, (self, f) => setProto((request) => Effect.tap(self(request), f)))
423
432
 
424
433
  /** @internal */
425
434
  export const tapRequest = dual<
@@ -430,4 +439,4 @@ export const tapRequest = dual<
430
439
  self: Client.Client<R, E, A>,
431
440
  f: (a: ClientRequest.ClientRequest) => Effect.Effect<R2, E2, _>
432
441
  ) => Client.Client<R | R2, E | E2, A>
433
- >(2, (self, f) => make((request) => Effect.zipRight(f(request), self(request))))
442
+ >(2, (self, f) => setProto((request) => Effect.zipRight(f(request), self(request))))
@@ -1,5 +1,8 @@
1
1
  import { dual } from "@effect/data/Function"
2
2
  import { pipeArguments } from "@effect/data/Pipeable"
3
+ import * as Effect from "@effect/io/Effect"
4
+ import type * as PlatformError from "@effect/platform/Error"
5
+ import type * as FileSystem from "@effect/platform/FileSystem"
3
6
  import type * as Body from "@effect/platform/Http/Body"
4
7
  import type * as Error from "@effect/platform/Http/ClientError"
5
8
  import type * as ClientRequest from "@effect/platform/Http/ClientRequest"
@@ -7,6 +10,7 @@ import * as Headers from "@effect/platform/Http/Headers"
7
10
  import type { Method } from "@effect/platform/Http/Method"
8
11
  import * as UrlParams from "@effect/platform/Http/UrlParams"
9
12
  import * as internalBody from "@effect/platform/internal/http/body"
13
+ import * as internalError from "@effect/platform/internal/http/clientError"
10
14
  import type * as Schema from "@effect/schema/Schema"
11
15
  import type * as Stream from "@effect/stream/Stream"
12
16
 
@@ -14,14 +18,16 @@ import type * as Stream from "@effect/stream/Stream"
14
18
  export const TypeId: ClientRequest.TypeId = Symbol.for("@effect/platform/Http/ClientRequest") as ClientRequest.TypeId
15
19
 
16
20
  class ClientRequestImpl implements ClientRequest.ClientRequest {
17
- readonly [TypeId]: ClientRequest.TypeId = TypeId
21
+ readonly [TypeId]: ClientRequest.TypeId
18
22
  constructor(
19
23
  readonly method: Method,
20
24
  readonly url: string,
21
25
  readonly urlParams: UrlParams.UrlParams,
22
26
  readonly headers: Headers.Headers,
23
27
  readonly body: Body.Body
24
- ) {}
28
+ ) {
29
+ this[TypeId] = TypeId
30
+ }
25
31
  pipe() {
26
32
  return pipeArguments(this, arguments)
27
33
  }
@@ -293,12 +299,26 @@ export const setBody = dual<
293
299
  })
294
300
 
295
301
  /** @internal */
296
- export const binaryBody = dual<
302
+ export const uint8ArrayBody = dual<
297
303
  (body: Uint8Array, contentType?: string) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
298
304
  (self: ClientRequest.ClientRequest, body: Uint8Array, contentType?: string) => ClientRequest.ClientRequest
299
305
  >(
300
306
  (args) => isClientRequest(args[0]),
301
- (self, body, contentType = "application/octet-stream") => setBody(self, internalBody.bytes(body, contentType))
307
+ (self, body, contentType = "application/octet-stream") => setBody(self, internalBody.uint8Array(body, contentType))
308
+ )
309
+
310
+ /** @internal */
311
+ export const effectBody = dual<
312
+ (
313
+ body: Effect.Effect<never, unknown, Body.NonEffect>
314
+ ) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
315
+ (
316
+ self: ClientRequest.ClientRequest,
317
+ body: Effect.Effect<never, unknown, Body.NonEffect>
318
+ ) => ClientRequest.ClientRequest
319
+ >(
320
+ 2,
321
+ (self, body) => setBody(self, internalBody.effect(body))
302
322
  )
303
323
 
304
324
  /** @internal */
@@ -316,6 +336,30 @@ export const jsonBody = dual<
316
336
  (self: ClientRequest.ClientRequest, body: unknown) => ClientRequest.ClientRequest
317
337
  >(2, (self, body) => setBody(self, internalBody.json(body)))
318
338
 
339
+ /** @internal */
340
+ export const unsafeJsonBody = dual<
341
+ (body: unknown) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
342
+ (self: ClientRequest.ClientRequest, body: unknown) => ClientRequest.ClientRequest
343
+ >(2, (self, body) => setBody(self, internalBody.unsafeJson(body)))
344
+
345
+ /** @internal */
346
+ export const fileBody = dual<
347
+ (
348
+ path: string,
349
+ options?: FileSystem.StreamOptions & { readonly contentType?: string }
350
+ ) => (
351
+ self: ClientRequest.ClientRequest
352
+ ) => Effect.Effect<FileSystem.FileSystem, PlatformError.PlatformError, ClientRequest.ClientRequest>,
353
+ (
354
+ self: ClientRequest.ClientRequest,
355
+ path: string,
356
+ options?: FileSystem.StreamOptions & { readonly contentType?: string }
357
+ ) => Effect.Effect<FileSystem.FileSystem, PlatformError.PlatformError, ClientRequest.ClientRequest>
358
+ >(
359
+ (args) => isClientRequest(args[0]),
360
+ (self, path, options) => Effect.map(internalBody.file(path, options), (body) => setBody(self, body))
361
+ )
362
+
319
363
  /** @internal */
320
364
  export const schemaBody = <I, A>(schema: Schema.Schema<I, A>): {
321
365
  (body: A): (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest
@@ -369,3 +413,19 @@ export const streamBody = dual<
369
413
  (self, body, { contentLength, contentType = "application/octet-stream" } = {}) =>
370
414
  setBody(self, internalBody.stream(body, contentType, contentLength))
371
415
  )
416
+
417
+ /** @internal */
418
+ export const resolveBody = (
419
+ self: ClientRequest.ClientRequest
420
+ ): Effect.Effect<never, Error.RequestError, ClientRequest.ClientRequest.NonEffectBody> =>
421
+ self.body._tag === "Effect"
422
+ ? Effect.map(
423
+ Effect.mapError(self.body.effect, (error) =>
424
+ internalError.requestError({
425
+ reason: "Encode",
426
+ request: self,
427
+ error
428
+ })),
429
+ (body) => setBody(self, body) as ClientRequest.ClientRequest.NonEffectBody
430
+ )
431
+ : Effect.succeed(self as ClientRequest.ClientRequest.NonEffectBody)
@@ -2,9 +2,9 @@ import * as Effect from "@effect/io/Effect"
2
2
  import type * as Error from "@effect/platform/Http/ClientError"
3
3
  import type * as ClientRequest from "@effect/platform/Http/ClientRequest"
4
4
  import type * as ClientResponse from "@effect/platform/Http/ClientResponse"
5
- import type * as FormData from "@effect/platform/Http/FormData"
6
5
  import * as Headers from "@effect/platform/Http/Headers"
7
6
  import * as IncomingMessage from "@effect/platform/Http/IncomingMessage"
7
+ import * as UrlParams from "@effect/platform/Http/UrlParams"
8
8
  import * as internalError from "@effect/platform/internal/http/clientError"
9
9
  import * as Stream from "@effect/stream/Stream"
10
10
 
@@ -18,13 +18,16 @@ export const fromWeb = (
18
18
  ): ClientResponse.ClientResponse => new ClientResponseImpl(request, source)
19
19
 
20
20
  class ClientResponseImpl implements ClientResponse.ClientResponse {
21
- readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId = IncomingMessage.TypeId
22
- readonly [TypeId]: ClientResponse.TypeId = TypeId
21
+ readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId
22
+ readonly [TypeId]: ClientResponse.TypeId
23
23
 
24
24
  constructor(
25
25
  private readonly request: ClientRequest.ClientRequest,
26
26
  private readonly source: globalThis.Response
27
- ) {}
27
+ ) {
28
+ this[IncomingMessage.TypeId] = IncomingMessage.TypeId
29
+ this[TypeId] = TypeId
30
+ }
28
31
 
29
32
  get status(): number {
30
33
  return this.source.status
@@ -77,6 +80,20 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
77
80
  })
78
81
  }
79
82
 
83
+ get urlParams(): Effect.Effect<never, Error.ResponseError, UrlParams.UrlParams> {
84
+ return Effect.flatMap(this.text, (_) =>
85
+ Effect.try({
86
+ try: () => UrlParams.fromInput(new URLSearchParams(_)),
87
+ catch: (_) =>
88
+ internalError.responseError({
89
+ request: this.request,
90
+ response: this,
91
+ reason: "Decode",
92
+ error: _
93
+ })
94
+ }))
95
+ }
96
+
80
97
  get formData(): Effect.Effect<never, Error.ResponseError, FormData> {
81
98
  return Effect.tryPromise({
82
99
  try: () => this.source.formData(),
@@ -90,15 +107,6 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
90
107
  })
91
108
  }
92
109
 
93
- get formDataStream(): Stream.Stream<never, Error.ResponseError, FormData.Part> {
94
- return Stream.fail(internalError.responseError({
95
- request: this.request,
96
- response: this,
97
- reason: "Decode",
98
- error: "not implemented"
99
- }))
100
- }
101
-
102
110
  get arrayBuffer(): Effect.Effect<never, Error.ResponseError, ArrayBuffer> {
103
111
  return Effect.tryPromise({
104
112
  try: () => this.source.arrayBuffer(),
@@ -0,0 +1,140 @@
1
+ import * as Chunk from "@effect/data/Chunk"
2
+ import * as Data from "@effect/data/Data"
3
+ import { dual, pipe } from "@effect/data/Function"
4
+ import { globalValue } from "@effect/data/Global"
5
+ import * as Option from "@effect/data/Option"
6
+ import * as Predicate from "@effect/data/Predicate"
7
+ import * as ReadonlyArray from "@effect/data/ReadonlyArray"
8
+ import * as Effect from "@effect/io/Effect"
9
+ import * as FiberRef from "@effect/io/FiberRef"
10
+ import * as FileSystem from "@effect/platform/FileSystem"
11
+ import type * as FormData from "@effect/platform/Http/FormData"
12
+ import type * as ParseResult from "@effect/schema/ParseResult"
13
+ import * as Schema from "@effect/schema/Schema"
14
+
15
+ /** @internal */
16
+ export const TypeId: FormData.TypeId = Symbol.for("@effect/platform/Http/FormData") as FormData.TypeId
17
+
18
+ /** @internal */
19
+ export const ErrorTypeId: FormData.ErrorTypeId = Symbol.for(
20
+ "@effect/platform/Http/FormData/FormDataError"
21
+ ) as FormData.ErrorTypeId
22
+
23
+ /** @internal */
24
+ export const FormDataError = (reason: FormData.FormDataError["reason"], error: unknown): FormData.FormDataError =>
25
+ Data.struct({
26
+ [ErrorTypeId]: ErrorTypeId,
27
+ _tag: "FormDataError",
28
+ reason,
29
+ error
30
+ })
31
+
32
+ /** @internal */
33
+ export const maxFieldSize: FiberRef.FiberRef<FileSystem.Size> = globalValue(
34
+ "@effect/platform/Http/FormData/maxFieldSize",
35
+ () => FiberRef.unsafeMake(FileSystem.Size(1024 * 1024))
36
+ )
37
+
38
+ /** @internal */
39
+ export const withMaxFieldSize = dual<
40
+ (size: FileSystem.SizeInput) => <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A>,
41
+ <R, E, A>(effect: Effect.Effect<R, E, A>, size: FileSystem.SizeInput) => Effect.Effect<R, E, A>
42
+ >(2, (effect, size) => Effect.locally(effect, maxFieldSize, FileSystem.Size(size)))
43
+
44
+ /** @internal */
45
+ export const maxFileSize: FiberRef.FiberRef<Option.Option<FileSystem.Size>> = globalValue(
46
+ "@effect/platform/Http/FormData/maxFileSize",
47
+ () => FiberRef.unsafeMake(Option.none<FileSystem.Size>())
48
+ )
49
+
50
+ /** @internal */
51
+ export const withMaxFileSize = dual<
52
+ (size: Option.Option<FileSystem.SizeInput>) => <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A>,
53
+ <R, E, A>(effect: Effect.Effect<R, E, A>, size: Option.Option<FileSystem.SizeInput>) => Effect.Effect<R, E, A>
54
+ >(2, (effect, size) => Effect.locally(effect, maxFileSize, Option.map(size, FileSystem.Size)))
55
+
56
+ /** @internal */
57
+ export const fieldMimeTypes: FiberRef.FiberRef<Chunk.Chunk<string>> = globalValue(
58
+ "@effect/platform/Http/FormData/fieldMimeTypes",
59
+ () => FiberRef.unsafeMake(Chunk.make("application/json"))
60
+ )
61
+
62
+ /**
63
+ * @since 1.0.0
64
+ * @category fiber refs
65
+ */
66
+ export const withFieldMimeTypes = dual<
67
+ (mimeTypes: ReadonlyArray<string>) => <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A>,
68
+ <R, E, A>(effect: Effect.Effect<R, E, A>, mimeTypes: ReadonlyArray<string>) => Effect.Effect<R, E, A>
69
+ >(2, (effect, mimeTypes) => Effect.locally(effect, fieldMimeTypes, Chunk.fromIterable(mimeTypes)))
70
+
71
+ /** @internal */
72
+ export const toRecord = (formData: globalThis.FormData): Record<string, Array<globalThis.File> | string> =>
73
+ ReadonlyArray.reduce(
74
+ formData.entries(),
75
+ {} as Record<string, Array<globalThis.File> | string>,
76
+ (acc, [key, value]) => {
77
+ if (Predicate.isString(value)) {
78
+ acc[key] = value
79
+ } else {
80
+ const existing = acc[key]
81
+ if (Array.isArray(existing)) {
82
+ existing.push(value)
83
+ } else {
84
+ acc[key] = [value]
85
+ }
86
+ }
87
+ return acc
88
+ }
89
+ )
90
+ /** @internal */
91
+ export const filesSchema: Schema.Schema<ReadonlyArray<File>, ReadonlyArray<File>> = Schema.array(
92
+ pipe(
93
+ Schema.instanceOf(Blob),
94
+ Schema.filter(
95
+ (blob): blob is File => "name" in blob
96
+ )
97
+ ) as any as Schema.Schema<File, File>
98
+ )
99
+
100
+ /** @internal */
101
+ export const schemaRecord = <I extends Readonly<Record<string, string | ReadonlyArray<globalThis.File>>>, A>(
102
+ schema: Schema.Schema<I, A>
103
+ ) => {
104
+ const parse = Schema.parse(schema)
105
+ return (formData: globalThis.FormData) => parse(toRecord(formData))
106
+ }
107
+
108
+ /** @internal */
109
+ export const schemaJson = <I, A>(schema: Schema.Schema<I, A>): {
110
+ (
111
+ field: string
112
+ ): (formData: globalThis.FormData) => Effect.Effect<never, FormData.FormDataError | ParseResult.ParseError, A>
113
+ (
114
+ formData: globalThis.FormData,
115
+ field: string
116
+ ): Effect.Effect<never, FormData.FormDataError | ParseResult.ParseError, A>
117
+ } => {
118
+ const parse = Schema.parse(schema)
119
+ return dual<
120
+ (
121
+ field: string
122
+ ) => (formData: globalThis.FormData) => Effect.Effect<never, FormData.FormDataError | ParseResult.ParseError, A>,
123
+ (
124
+ formData: globalThis.FormData,
125
+ field: string
126
+ ) => Effect.Effect<never, FormData.FormDataError | ParseResult.ParseError, A>
127
+ >(2, (formData, field) =>
128
+ pipe(
129
+ Effect.succeed(formData.get(field)),
130
+ Effect.filterOrFail(
131
+ (field) => Predicate.isString(field),
132
+ () => FormDataError("Parse", `schemaJson: field was not a string`)
133
+ ),
134
+ Effect.tryMap({
135
+ try: (field) => JSON.parse(field as string),
136
+ catch: (error) => FormDataError("Parse", `schemaJson: field was not valid json: ${error}`)
137
+ }),
138
+ Effect.flatMap(parse)
139
+ ))
140
+ }
@@ -0,0 +1,72 @@
1
+ import { dual } from "@effect/data/Function"
2
+ import * as Effect from "@effect/io/Effect"
3
+ import type * as App from "@effect/platform/Http/App"
4
+ import * as Headers from "@effect/platform/Http/Headers"
5
+ import type * as Middleware from "@effect/platform/Http/Middleware"
6
+ import * as ServerRequest from "@effect/platform/Http/ServerRequest"
7
+
8
+ /** @internal */
9
+ export const make = <M extends Middleware.Middleware>(middleware: M): M => middleware
10
+
11
+ /** @internal */
12
+ export const logger = make((httpApp) =>
13
+ 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), {
21
+ "http.method": request.method,
22
+ "http.url": request.url,
23
+ "http.status": exit._tag === "Success" ? exit.value.status : 500
24
+ })
25
+ )
26
+ ),
27
+ "http.span"
28
+ )
29
+ )
30
+
31
+ /** @internal */
32
+ export const tracer = make((httpApp) =>
33
+ Effect.flatMap(
34
+ ServerRequest.ServerRequest,
35
+ (request) =>
36
+ Effect.withSpan(
37
+ Effect.tap(
38
+ httpApp,
39
+ (response) => Effect.annotateCurrentSpan("http.status", response.status)
40
+ ),
41
+ `http ${request.method}`,
42
+ { attributes: { "http.method": request.method, "http.url": request.url } }
43
+ )
44
+ )
45
+ )
46
+
47
+ /** @internal */
48
+ 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
+ })
56
+ )
57
+
58
+ /** @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)