@effect/platform 0.31.0 → 0.31.2

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 (44) hide show
  1. package/dist/cjs/Http/App.js +43 -1
  2. package/dist/cjs/Http/App.js.map +1 -1
  3. package/dist/cjs/Http/Middleware.js +11 -1
  4. package/dist/cjs/Http/Middleware.js.map +1 -1
  5. package/dist/cjs/Http/ServerRequest.js +6 -1
  6. package/dist/cjs/Http/ServerRequest.js.map +1 -1
  7. package/dist/cjs/Http/ServerResponse.js +6 -1
  8. package/dist/cjs/Http/ServerResponse.js.map +1 -1
  9. package/dist/cjs/internal/http/middleware.js +7 -2
  10. package/dist/cjs/internal/http/middleware.js.map +1 -1
  11. package/dist/cjs/internal/http/serverRequest.js +112 -1
  12. package/dist/cjs/internal/http/serverRequest.js.map +1 -1
  13. package/dist/cjs/internal/http/serverResponse.js +49 -1
  14. package/dist/cjs/internal/http/serverResponse.js.map +1 -1
  15. package/dist/dts/Http/App.d.ts +22 -2
  16. package/dist/dts/Http/App.d.ts.map +1 -1
  17. package/dist/dts/Http/Middleware.d.ts +15 -0
  18. package/dist/dts/Http/Middleware.d.ts.map +1 -1
  19. package/dist/dts/Http/ServerRequest.d.ts +5 -0
  20. package/dist/dts/Http/ServerRequest.d.ts.map +1 -1
  21. package/dist/dts/Http/ServerResponse.d.ts +5 -0
  22. package/dist/dts/Http/ServerResponse.d.ts.map +1 -1
  23. package/dist/esm/Http/App.js +40 -0
  24. package/dist/esm/Http/App.js.map +1 -1
  25. package/dist/esm/Http/Middleware.js +10 -0
  26. package/dist/esm/Http/Middleware.js.map +1 -1
  27. package/dist/esm/Http/ServerRequest.js +5 -0
  28. package/dist/esm/Http/ServerRequest.js.map +1 -1
  29. package/dist/esm/Http/ServerResponse.js +5 -0
  30. package/dist/esm/Http/ServerResponse.js.map +1 -1
  31. package/dist/esm/internal/http/middleware.js +6 -1
  32. package/dist/esm/internal/http/middleware.js.map +1 -1
  33. package/dist/esm/internal/http/serverRequest.js +110 -0
  34. package/dist/esm/internal/http/serverRequest.js.map +1 -1
  35. package/dist/esm/internal/http/serverResponse.js +47 -0
  36. package/dist/esm/internal/http/serverResponse.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/Http/App.ts +50 -2
  39. package/src/Http/Middleware.ts +23 -0
  40. package/src/Http/ServerRequest.ts +6 -0
  41. package/src/Http/ServerResponse.ts +6 -0
  42. package/src/internal/http/middleware.ts +28 -7
  43. package/src/internal/http/serverRequest.ts +159 -0
  44. package/src/internal/http/serverResponse.ts +44 -1
@@ -3,8 +3,10 @@
3
3
  */
4
4
  import type * as Effect from "effect/Effect"
5
5
  import type * as FiberRef from "effect/FiberRef"
6
+ import type * as Predicate from "effect/Predicate"
6
7
  import * as internal from "../internal/http/middleware.js"
7
8
  import type * as App from "./App.js"
9
+ import type * as ServerRequest from "./ServerRequest.js"
8
10
 
9
11
  /**
10
12
  * @since 1.0.0
@@ -51,6 +53,27 @@ export const loggerDisabled: FiberRef.FiberRef<boolean> = internal.loggerDisable
51
53
  export const withLoggerDisabled: <R, E, A>(self: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A> =
52
54
  internal.withLoggerDisabled
53
55
 
56
+ /**
57
+ * @since 1.0.0
58
+ * @category fiber refs
59
+ */
60
+ export const currentTracerDisabledWhen: FiberRef.FiberRef<Predicate.Predicate<ServerRequest.ServerRequest>> =
61
+ internal.currentTracerDisabledWhen
62
+
63
+ /**
64
+ * @since 1.0.0
65
+ * @category fiber refs
66
+ */
67
+ export const withTracerDisabledWhen: {
68
+ (
69
+ predicate: Predicate.Predicate<ServerRequest.ServerRequest>
70
+ ): <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A>
71
+ <R, E, A>(
72
+ effect: Effect.Effect<R, E, A>,
73
+ predicate: Predicate.Predicate<ServerRequest.ServerRequest>
74
+ ): Effect.Effect<R, E, A>
75
+ } = internal.withTracerDisabledWhen
76
+
54
77
  /**
55
78
  * @since 1.0.0
56
79
  * @category constructors
@@ -127,3 +127,9 @@ export const schemaFormDataJson: <I, A>(
127
127
  Error.RequestError | FormData.FormDataError | ParseResult.ParseError,
128
128
  A
129
129
  > = internal.schemaFormDataJson
130
+
131
+ /**
132
+ * @since 1.0.0
133
+ * @category conversions
134
+ */
135
+ export const fromWeb: (request: Request) => ServerRequest = internal.fromWeb
@@ -190,3 +190,9 @@ export const setStatus: {
190
190
  (status: number, statusText?: string | undefined): (self: ServerResponse) => ServerResponse
191
191
  (self: ServerResponse, status: number, statusText?: string | undefined): ServerResponse
192
192
  } = internal.setStatus
193
+
194
+ /**
195
+ * @since 1.0.0
196
+ * @category conversions
197
+ */
198
+ export const toWeb: (response: ServerResponse, withoutBody?: boolean) => Response = internal.toWeb
@@ -1,6 +1,8 @@
1
1
  import * as Effect from "effect/Effect"
2
2
  import * as FiberRef from "effect/FiberRef"
3
+ import * as Function from "effect/Function"
3
4
  import { globalValue } from "effect/GlobalValue"
5
+ import type * as Predicate from "effect/Predicate"
4
6
  import * as Headers from "../../Http/Headers.js"
5
7
  import * as IncomingMessage from "../../Http/IncomingMessage.js"
6
8
  import type * as Middleware from "../../Http/Middleware.js"
@@ -22,6 +24,23 @@ export const withLoggerDisabled = <R, E, A>(self: Effect.Effect<R, E, A>): Effec
22
24
  self
23
25
  )
24
26
 
27
+ /** @internal */
28
+ export const currentTracerDisabledWhen = globalValue(
29
+ Symbol.for("@effect/platform/Http/Middleware/tracerDisabledWhen"),
30
+ () => FiberRef.unsafeMake<Predicate.Predicate<ServerRequest.ServerRequest>>(Function.constFalse)
31
+ )
32
+
33
+ /** @internal */
34
+ export const withTracerDisabledWhen = Function.dual<
35
+ (
36
+ predicate: Predicate.Predicate<ServerRequest.ServerRequest>
37
+ ) => <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<R, E, A>,
38
+ <R, E, A>(
39
+ effect: Effect.Effect<R, E, A>,
40
+ predicate: Predicate.Predicate<ServerRequest.ServerRequest>
41
+ ) => Effect.Effect<R, E, A>
42
+ >(2, (self, pred) => Effect.locally(self, currentTracerDisabledWhen, pred))
43
+
25
44
  /** @internal */
26
45
  export const logger = make((httpApp) => {
27
46
  let counter = 0
@@ -61,18 +80,20 @@ export const tracer = make((httpApp) => {
61
80
  (response) => Effect.annotateCurrentSpan("http.status", response.status)
62
81
  )
63
82
  return Effect.flatMap(
64
- ServerRequest.ServerRequest,
65
- (request) =>
83
+ Effect.zip(ServerRequest.ServerRequest, FiberRef.get(currentTracerDisabledWhen)),
84
+ ([request, disabledWhen]) =>
66
85
  Effect.flatMap(
67
86
  request.headers["x-b3-traceid"] || request.headers["b3"] ?
68
87
  Effect.orElseSucceed(IncomingMessage.schemaExternalSpan(request), () => undefined) :
69
88
  Effect.succeed(undefined),
70
89
  (parent) =>
71
- Effect.withSpan(
72
- appWithStatus,
73
- `http ${request.method}`,
74
- { attributes: { "http.method": request.method, "http.url": request.url }, parent }
75
- )
90
+ disabledWhen(request) ?
91
+ httpApp :
92
+ Effect.withSpan(
93
+ appWithStatus,
94
+ `http ${request.method}`,
95
+ { attributes: { "http.method": request.method, "http.url": request.url }, parent }
96
+ )
76
97
  )
77
98
  )
78
99
  })
@@ -1,10 +1,18 @@
1
1
  import type * as Schema from "@effect/schema/Schema"
2
2
  import * as Context from "effect/Context"
3
3
  import * as Effect from "effect/Effect"
4
+ import * as Option from "effect/Option"
5
+ import type * as Scope from "effect/Scope"
6
+ import * as Stream from "effect/Stream"
7
+ import type * as FileSystem from "../../FileSystem.js"
4
8
  import * as FormData from "../../Http/FormData.js"
9
+ import * as Headers from "../../Http/Headers.js"
5
10
  import * as IncomingMessage from "../../Http/IncomingMessage.js"
11
+ import type { Method } from "../../Http/Method.js"
6
12
  import * as Error from "../../Http/ServerError.js"
7
13
  import type * as ServerRequest from "../../Http/ServerRequest.js"
14
+ import * as UrlParams from "../../Http/UrlParams.js"
15
+ import type * as Path from "../../Path.js"
8
16
 
9
17
  /** @internal */
10
18
  export const TypeId: ServerRequest.TypeId = Symbol.for("@effect/platform/Http/ServerRequest") as ServerRequest.TypeId
@@ -63,3 +71,154 @@ export const schemaFormDataJson = <I, A>(schema: Schema.Schema<I, A>) => {
63
71
  )
64
72
  ))
65
73
  }
74
+
75
+ /** @internal */
76
+ export const fromWeb = (request: globalThis.Request): ServerRequest.ServerRequest =>
77
+ new ServerRequestImpl(request, request.url)
78
+
79
+ class ServerRequestImpl implements ServerRequest.ServerRequest {
80
+ readonly [TypeId]: ServerRequest.TypeId
81
+ readonly [IncomingMessage.TypeId]: IncomingMessage.TypeId
82
+ constructor(
83
+ readonly source: Request,
84
+ readonly url: string,
85
+ public headersOverride?: Headers.Headers,
86
+ private remoteAddressOverride?: string
87
+ ) {
88
+ this[TypeId] = TypeId
89
+ this[IncomingMessage.TypeId] = IncomingMessage.TypeId
90
+ }
91
+ modify(
92
+ options: {
93
+ readonly url?: string | undefined
94
+ readonly headers?: Headers.Headers | undefined
95
+ readonly remoteAddress?: string | undefined
96
+ }
97
+ ) {
98
+ return new ServerRequestImpl(
99
+ this.source,
100
+ options.url ?? this.url,
101
+ options.headers ?? this.headersOverride,
102
+ options.remoteAddress ?? this.remoteAddressOverride
103
+ )
104
+ }
105
+ get method(): Method {
106
+ return this.source.method.toUpperCase() as Method
107
+ }
108
+ get originalUrl() {
109
+ return this.source.url
110
+ }
111
+ get remoteAddress(): Option.Option<string> {
112
+ return this.remoteAddressOverride ? Option.some(this.remoteAddressOverride) : Option.none()
113
+ }
114
+ get headers(): Headers.Headers {
115
+ this.headersOverride ??= Headers.fromInput(this.source.headers)
116
+ return this.headersOverride
117
+ }
118
+
119
+ get stream(): Stream.Stream<never, Error.RequestError, Uint8Array> {
120
+ return this.source.body
121
+ ? Stream.fromReadableStream(() => this.source.body as any, (_) =>
122
+ Error.RequestError({
123
+ request: this,
124
+ reason: "Decode",
125
+ error: _
126
+ }))
127
+ : Stream.fail(Error.RequestError({
128
+ request: this,
129
+ reason: "Decode",
130
+ error: "can not create stream from empty body"
131
+ }))
132
+ }
133
+
134
+ private textEffect: Effect.Effect<never, Error.RequestError, string> | undefined
135
+ get text(): Effect.Effect<never, Error.RequestError, string> {
136
+ if (this.textEffect) {
137
+ return this.textEffect
138
+ }
139
+ this.textEffect = Effect.runSync(Effect.cached(
140
+ Effect.tryPromise({
141
+ try: () => this.source.text(),
142
+ catch: (error) =>
143
+ Error.RequestError({
144
+ request: this,
145
+ reason: "Decode",
146
+ error
147
+ })
148
+ })
149
+ ))
150
+ return this.textEffect
151
+ }
152
+
153
+ get json(): Effect.Effect<never, Error.RequestError, unknown> {
154
+ return Effect.tryMap(this.text, {
155
+ try: (_) => JSON.parse(_) as unknown,
156
+ catch: (error) =>
157
+ Error.RequestError({
158
+ request: this,
159
+ reason: "Decode",
160
+ error
161
+ })
162
+ })
163
+ }
164
+
165
+ get urlParamsBody(): Effect.Effect<never, Error.RequestError, UrlParams.UrlParams> {
166
+ return Effect.flatMap(this.text, (_) =>
167
+ Effect.try({
168
+ try: () => UrlParams.fromInput(new URLSearchParams(_)),
169
+ catch: (error) =>
170
+ Error.RequestError({
171
+ request: this,
172
+ reason: "Decode",
173
+ error
174
+ })
175
+ }))
176
+ }
177
+
178
+ private formDataEffect:
179
+ | Effect.Effect<
180
+ Scope.Scope | FileSystem.FileSystem | Path.Path,
181
+ FormData.FormDataError,
182
+ FormData.PersistedFormData
183
+ >
184
+ | undefined
185
+ get formData(): Effect.Effect<
186
+ Scope.Scope | FileSystem.FileSystem | Path.Path,
187
+ FormData.FormDataError,
188
+ FormData.PersistedFormData
189
+ > {
190
+ if (this.formDataEffect) {
191
+ return this.formDataEffect
192
+ }
193
+ this.formDataEffect = Effect.runSync(Effect.cached(
194
+ FormData.formData(this.formDataStream)
195
+ ))
196
+ return this.formDataEffect
197
+ }
198
+
199
+ get formDataStream(): Stream.Stream<never, FormData.FormDataError, FormData.Part> {
200
+ return Stream.pipeThroughChannel(
201
+ Stream.mapError(this.stream, (error) => FormData.FormDataError("InternalError", error)),
202
+ FormData.makeChannel(this.headers)
203
+ )
204
+ }
205
+
206
+ private arrayBufferEffect: Effect.Effect<never, Error.RequestError, ArrayBuffer> | undefined
207
+ get arrayBuffer(): Effect.Effect<never, Error.RequestError, ArrayBuffer> {
208
+ if (this.arrayBuffer) {
209
+ return this.arrayBuffer
210
+ }
211
+ this.arrayBufferEffect = Effect.runSync(Effect.cached(
212
+ Effect.tryPromise({
213
+ try: () => this.source.arrayBuffer(),
214
+ catch: (error) =>
215
+ Error.RequestError({
216
+ request: this,
217
+ reason: "Decode",
218
+ error
219
+ })
220
+ })
221
+ ))
222
+ return this.arrayBufferEffect
223
+ }
224
+ }
@@ -2,7 +2,7 @@ import type * as Schema from "@effect/schema/Schema"
2
2
  import * as Effect from "effect/Effect"
3
3
  import * as Effectable from "effect/Effectable"
4
4
  import { dual } from "effect/Function"
5
- import type * as Stream from "effect/Stream"
5
+ import * as Stream from "effect/Stream"
6
6
  import type * as PlatformError from "../../Error.js"
7
7
  import type * as FileSystem from "../../FileSystem.js"
8
8
  import type * as Body from "../../Http/Body.js"
@@ -259,3 +259,46 @@ export const setBody = dual<
259
259
  body
260
260
  )
261
261
  })
262
+
263
+ /** @internal */
264
+ export const toWeb = (response: ServerResponse.ServerResponse, withoutBody = false): Response => {
265
+ if (withoutBody) {
266
+ return new Response(undefined, {
267
+ status: response.status,
268
+ statusText: response.statusText,
269
+ headers: response.headers
270
+ })
271
+ }
272
+ const body = response.body
273
+ switch (body._tag) {
274
+ case "Empty": {
275
+ return new Response(undefined, {
276
+ status: response.status,
277
+ statusText: response.statusText,
278
+ headers: response.headers
279
+ })
280
+ }
281
+ case "Uint8Array":
282
+ case "Raw": {
283
+ return new Response(body.body as any, {
284
+ status: response.status,
285
+ statusText: response.statusText,
286
+ headers: response.headers
287
+ })
288
+ }
289
+ case "FormData": {
290
+ return new Response(body.formData as any, {
291
+ status: response.status,
292
+ statusText: response.statusText,
293
+ headers: response.headers
294
+ })
295
+ }
296
+ case "Stream": {
297
+ return new Response(Stream.toReadableStream(body.stream), {
298
+ status: response.status,
299
+ statusText: response.statusText,
300
+ headers: response.headers
301
+ })
302
+ }
303
+ }
304
+ }