@effect/platform 0.15.2 → 0.16.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 (49) hide show
  1. package/Http/Client.d.ts +6 -0
  2. package/Http/Client.d.ts.map +1 -1
  3. package/Http/Client.js +8 -2
  4. package/Http/Client.js.map +1 -1
  5. package/Http/IncomingMessage.d.ts +8 -1
  6. package/Http/IncomingMessage.d.ts.map +1 -1
  7. package/Http/IncomingMessage.js +44 -2
  8. package/Http/IncomingMessage.js.map +1 -1
  9. package/Http/Middleware.d.ts +5 -0
  10. package/Http/Middleware.d.ts.map +1 -1
  11. package/Http/Middleware.js +7 -1
  12. package/Http/Middleware.js.map +1 -1
  13. package/Http/ServerRequest.d.ts +5 -2
  14. package/Http/ServerRequest.d.ts.map +1 -1
  15. package/Http/ServerRequest.js.map +1 -1
  16. package/internal/http/client.d.ts.map +1 -1
  17. package/internal/http/client.js +12 -1
  18. package/internal/http/client.js.map +1 -1
  19. package/internal/http/clientResponse.js +4 -0
  20. package/internal/http/clientResponse.js.map +1 -1
  21. package/internal/http/middleware.js +14 -5
  22. package/internal/http/middleware.js.map +1 -1
  23. package/internal/http/router.d.ts.map +1 -1
  24. package/internal/http/router.js +17 -1
  25. package/internal/http/router.js.map +1 -1
  26. package/mjs/Http/Client.mjs +5 -0
  27. package/mjs/Http/Client.mjs.map +1 -1
  28. package/mjs/Http/IncomingMessage.mjs +42 -1
  29. package/mjs/Http/IncomingMessage.mjs.map +1 -1
  30. package/mjs/Http/Middleware.mjs +5 -0
  31. package/mjs/Http/Middleware.mjs.map +1 -1
  32. package/mjs/Http/ServerRequest.mjs.map +1 -1
  33. package/mjs/internal/http/client.mjs +10 -0
  34. package/mjs/internal/http/client.mjs.map +1 -1
  35. package/mjs/internal/http/clientResponse.mjs +4 -0
  36. package/mjs/internal/http/clientResponse.mjs.map +1 -1
  37. package/mjs/internal/http/middleware.mjs +12 -4
  38. package/mjs/internal/http/middleware.mjs.map +1 -1
  39. package/mjs/internal/http/router.mjs +17 -1
  40. package/mjs/internal/http/router.mjs.map +1 -1
  41. package/package.json +4 -4
  42. package/src/Http/Client.ts +8 -0
  43. package/src/Http/IncomingMessage.ts +62 -2
  44. package/src/Http/Middleware.ts +6 -0
  45. package/src/Http/ServerRequest.ts +7 -2
  46. package/src/internal/http/client.ts +33 -1
  47. package/src/internal/http/clientResponse.ts +5 -0
  48. package/src/internal/http/middleware.ts +39 -12
  49. package/src/internal/http/router.ts +15 -1
@@ -1,15 +1,17 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import { dual } from "@effect/data/Function"
4
+ import * as Context from "@effect/data/Context"
5
+ import { dual, flow } from "@effect/data/Function"
5
6
  import * as Global from "@effect/data/GlobalValue"
6
7
  import * as Option from "@effect/data/Option"
7
8
  import * as Effect from "@effect/io/Effect"
8
9
  import * as FiberRef from "@effect/io/FiberRef"
10
+ import type { ExternalSpan } from "@effect/io/Tracer"
9
11
  import * as FileSystem from "@effect/platform/FileSystem"
10
12
  import type * as Headers from "@effect/platform/Http/Headers"
11
13
  import type * as UrlParams from "@effect/platform/Http/UrlParams"
12
- import type * as ParseResult from "@effect/schema/ParseResult"
14
+ import * as ParseResult from "@effect/schema/ParseResult"
13
15
  import * as Schema from "@effect/schema/Schema"
14
16
  import type * as Stream from "@effect/stream/Stream"
15
17
 
@@ -32,6 +34,7 @@ export type TypeId = typeof TypeId
32
34
  export interface IncomingMessage<E> {
33
35
  readonly [TypeId]: TypeId
34
36
  readonly headers: Headers.Headers
37
+ readonly remoteAddress: Option.Option<string>
35
38
  readonly json: Effect.Effect<never, E, unknown>
36
39
  readonly text: Effect.Effect<never, E, string>
37
40
  readonly urlParamsBody: Effect.Effect<never, E, UrlParams.UrlParams>
@@ -68,6 +71,63 @@ export const schemaHeaders = <I extends Readonly<Record<string, string>>, A>(sch
68
71
  return <E>(self: IncomingMessage<E>): Effect.Effect<never, ParseResult.ParseError, A> => parse(self.headers)
69
72
  }
70
73
 
74
+ const SpanSchema = Schema.struct({
75
+ traceId: Schema.string,
76
+ spanId: Schema.string,
77
+ parentSpanId: Schema.union(Schema.string, Schema.undefined)
78
+ })
79
+
80
+ /**
81
+ * @since 1.0.0
82
+ * @category schema
83
+ */
84
+ export const schemaExternalSpan = flow(
85
+ schemaHeaders(Schema.union(
86
+ Schema.transformOrFail(
87
+ Schema.struct({
88
+ b3: Schema.NonEmpty
89
+ }),
90
+ SpanSchema,
91
+ (_) => {
92
+ const parts = _.b3.split("-")
93
+ if (parts.length >= 2) {
94
+ return ParseResult.success({
95
+ traceId: parts[0],
96
+ spanId: parts[1],
97
+ parentSpanId: parts[3]
98
+ })
99
+ }
100
+ return ParseResult.failure(ParseResult.missing)
101
+ },
102
+ (_) => ParseResult.success("")
103
+ ),
104
+ Schema.transform(
105
+ Schema.struct({
106
+ "x-b3-traceid": Schema.NonEmpty,
107
+ "x-b3-spanid": Schema.NonEmpty,
108
+ "x-b3-parentspanid": Schema.optional(Schema.NonEmpty)
109
+ }),
110
+ SpanSchema,
111
+ (_) => ({
112
+ traceId: _["x-b3-traceid"],
113
+ spanId: _["x-b3-spanid"],
114
+ parentSpanId: _["x-b3-parentspanid"]
115
+ }),
116
+ (_) => ({
117
+ "x-b3-traceid": _.traceId,
118
+ "x-b3-spanid": _.spanId,
119
+ "x-b3-parentspanid": _.parentSpanId
120
+ })
121
+ )
122
+ )),
123
+ Effect.map((_): ExternalSpan => ({
124
+ _tag: "ExternalSpan",
125
+ traceId: _.traceId,
126
+ spanId: _.spanId,
127
+ context: Context.empty()
128
+ }))
129
+ )
130
+
71
131
  /**
72
132
  * @since 1.0.0
73
133
  * @category fiber refs
@@ -30,6 +30,12 @@ export declare namespace Middleware {
30
30
  */
31
31
  export const make: <M extends Middleware>(middleware: M) => M = internal.make
32
32
 
33
+ /**
34
+ * @since 1.0.0
35
+ * @category constructors
36
+ */
37
+ export const b3Response: <R, E>(httpApp: App.Default<R, E>) => App.Default<R, E> = internal.b3Response
38
+
33
39
  /**
34
40
  * @since 1.0.0
35
41
  * @category constructors
@@ -49,8 +49,13 @@ export interface ServerRequest extends IncomingMessage.IncomingMessage<Error.Req
49
49
  readonly formData: Effect.Effect<Scope.Scope | FileSystem.FileSystem | Path.Path, FormData.FormDataError, FormData>
50
50
  readonly formDataStream: Stream.Stream<never, FormData.FormDataError, FormData.Part>
51
51
 
52
- readonly setUrl: (url: string) => ServerRequest
53
- readonly replaceHeaders: (headers: Headers.Headers) => ServerRequest
52
+ readonly modify: (
53
+ options: {
54
+ readonly url?: string
55
+ readonly headers?: Headers.Headers
56
+ readonly remoteAddress?: string
57
+ }
58
+ ) => ServerRequest
54
59
  }
55
60
 
56
61
  /**
@@ -1,15 +1,18 @@
1
1
  import * as Context from "@effect/data/Context"
2
- import { dual } from "@effect/data/Function"
2
+ import { dual, pipe } from "@effect/data/Function"
3
+ import * as Option from "@effect/data/Option"
3
4
  import { pipeArguments } from "@effect/data/Pipeable"
4
5
  import type * as Predicate from "@effect/data/Predicate"
5
6
  import * as Effect from "@effect/io/Effect"
6
7
  import * as Layer from "@effect/io/Layer"
7
8
  import type * as Schedule from "@effect/io/Schedule"
9
+ import type * as Scope from "@effect/io/Scope"
8
10
  import type * as Body from "@effect/platform/Http/Body"
9
11
  import type * as Client from "@effect/platform/Http/Client"
10
12
  import type * as Error from "@effect/platform/Http/ClientError"
11
13
  import type * as ClientRequest from "@effect/platform/Http/ClientRequest"
12
14
  import type * as ClientResponse from "@effect/platform/Http/ClientResponse"
15
+ import * as IncomingMessage from "@effect/platform/Http/IncomingMessage"
13
16
  import * as Method from "@effect/platform/Http/Method"
14
17
  import * as UrlParams from "@effect/platform/Http/UrlParams"
15
18
  import * as internalBody from "@effect/platform/internal/http/body"
@@ -359,6 +362,35 @@ export const mapRequestEffect = dual<
359
362
  ) => Client.Client<R | R2, E | E2, A>
360
363
  >(2, (self, f) => setProto((request) => Effect.flatMap(f(request), self)))
361
364
 
365
+ /** @internal */
366
+ export const withB3Propagation = <R, E>(
367
+ self: Client.Client.WithResponse<R, E>
368
+ ): Client.Client.WithResponse<R | Scope.Scope, E> =>
369
+ setProto((req) =>
370
+ pipe(
371
+ Effect.map(
372
+ Effect.currentSpan,
373
+ Option.match({
374
+ onNone: () => req,
375
+ onSome: (span) => {
376
+ const parentId = span.parent._tag === "Some" ? `-${span.parent.value.spanId}` : ""
377
+ return internalRequest.setHeader(
378
+ req,
379
+ "b3",
380
+ `${span.traceId}-${span.spanId}-1${parentId}`
381
+ )
382
+ }
383
+ })
384
+ ),
385
+ Effect.flatMap(self),
386
+ Effect.tap((res) =>
387
+ Effect.ignore(
388
+ Effect.flatMap(IncomingMessage.schemaExternalSpan(res), Effect.withParentSpanScoped)
389
+ )
390
+ )
391
+ )
392
+ )
393
+
362
394
  /** @internal */
363
395
  export const retry: {
364
396
  <R1, E extends E0, E0, B>(policy: Schedule.Schedule<R1, E0, B>): <R, A>(
@@ -1,3 +1,4 @@
1
+ import * as Option from "@effect/data/Option"
1
2
  import * as Effect from "@effect/io/Effect"
2
3
  import type * as Error from "@effect/platform/Http/ClientError"
3
4
  import type * as ClientRequest from "@effect/platform/Http/ClientRequest"
@@ -39,6 +40,10 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
39
40
  return Headers.fromInput(this.source.headers)
40
41
  }
41
42
 
43
+ get remoteAddress(): Option.Option<string> {
44
+ return Option.none()
45
+ }
46
+
42
47
  get stream(): Stream.Stream<never, Error.ResponseError, Uint8Array> {
43
48
  return this.source.body
44
49
  ? Stream.fromReadableStream(() => this.source.body!, (_) =>
@@ -1,8 +1,10 @@
1
1
  import { flow } from "@effect/data/Function"
2
2
  import * as Effect from "@effect/io/Effect"
3
3
  import * as Headers from "@effect/platform/Http/Headers"
4
+ import * as IncomingMessage from "@effect/platform/Http/IncomingMessage"
4
5
  import type * as Middleware from "@effect/platform/Http/Middleware"
5
6
  import * as ServerRequest from "@effect/platform/Http/ServerRequest"
7
+ import * as ServerResponse from "@effect/platform/Http/ServerResponse"
6
8
 
7
9
  /** @internal */
8
10
  export const make = <M extends Middleware.Middleware>(middleware: M): M => middleware
@@ -36,26 +38,51 @@ export const tracer = make((httpApp) =>
36
38
  Effect.flatMap(
37
39
  ServerRequest.ServerRequest,
38
40
  (request) =>
39
- Effect.withSpan(
40
- Effect.tap(
41
- httpApp,
42
- (response) => Effect.annotateCurrentSpan("http.status", response.status)
43
- ),
44
- `http ${request.method}`,
45
- { attributes: { "http.method": request.method, "http.url": request.url } }
41
+ Effect.flatMap(
42
+ Effect.orElseSucceed(IncomingMessage.schemaExternalSpan(request), () => undefined),
43
+ (parent) =>
44
+ Effect.withSpan(
45
+ Effect.tap(
46
+ httpApp,
47
+ (response) => Effect.annotateCurrentSpan("http.status", response.status)
48
+ ),
49
+ `http ${request.method}`,
50
+ { attributes: { "http.method": request.method, "http.url": request.url }, parent }
51
+ )
46
52
  )
47
53
  )
48
54
  )
49
55
 
56
+ /** @internal */
57
+ export const b3Response = make((httpApp) =>
58
+ Effect.flatMap(
59
+ Effect.currentSpan,
60
+ (span) =>
61
+ span._tag === "Some"
62
+ ? Effect.map(httpApp, (res) =>
63
+ ServerResponse.setHeader(
64
+ res,
65
+ "b3",
66
+ `${span.value.traceId}-${span.value.spanId}-1${
67
+ span.value.parent._tag === "Some" ? `-${span.value.parent.value.spanId}` : ""
68
+ }`
69
+ ))
70
+ : httpApp
71
+ )
72
+ )
73
+
50
74
  /** @internal */
51
75
  export const xForwardedHeaders = make((httpApp) =>
52
76
  Effect.updateService(httpApp, ServerRequest.ServerRequest, (request) =>
53
77
  request.headers["x-forwarded-host"]
54
- ? request.replaceHeaders(Headers.set(
55
- request.headers,
56
- "host",
57
- request.headers["x-forwarded-host"]
58
- ))
78
+ ? request.modify({
79
+ headers: Headers.set(
80
+ request.headers,
81
+ "host",
82
+ request.headers["x-forwarded-host"]
83
+ ),
84
+ remoteAddress: request.headers["x-forwarded-for"]?.split(",")[0].trim()
85
+ })
59
86
  : request)
60
87
  )
61
88
 
@@ -3,6 +3,7 @@ import * as Context from "@effect/data/Context"
3
3
  import * as Equal from "@effect/data/Equal"
4
4
  import { dual } from "@effect/data/Function"
5
5
  import * as Hash from "@effect/data/Hash"
6
+ import * as Inspectable from "@effect/data/Inspectable"
6
7
  import * as Option from "@effect/data/Option"
7
8
  import { pipeArguments } from "@effect/data/Pipeable"
8
9
  import type * as Cause from "@effect/io/Cause"
@@ -91,6 +92,19 @@ class RouterImpl<R, E> implements Router.Router<R, E> {
91
92
  [Hash.symbol](this: RouterImpl<R, E>): number {
92
93
  return Hash.random(this)
93
94
  }
95
+ toJSON() {
96
+ return {
97
+ _id: "Router",
98
+ routes: this.routes.toJSON(),
99
+ mounts: this.mounts.toJSON()
100
+ }
101
+ }
102
+ toString() {
103
+ return Inspectable.toString(this)
104
+ }
105
+ [Inspectable.NodeInspectSymbol]() {
106
+ return this.toJSON()
107
+ }
94
108
  }
95
109
 
96
110
  const toHttpApp = <R, E>(
@@ -151,7 +165,7 @@ const toHttpApp = <R, E>(
151
165
 
152
166
  function sliceRequestUrl(request: ServerRequest.ServerRequest, prefix: string) {
153
167
  const prefexLen = prefix.length
154
- return request.setUrl(request.url.length <= prefexLen ? "/" : request.url.slice(prefexLen))
168
+ return request.modify({ url: request.url.length <= prefexLen ? "/" : request.url.slice(prefexLen) })
155
169
  }
156
170
 
157
171
  class RouteImpl<R, E> implements Router.Route<R, E> {