effect 4.0.0-beta.10 → 4.0.0-beta.12

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 (189) hide show
  1. package/dist/Channel.d.ts +7 -7
  2. package/dist/Config.d.ts +157 -0
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js +56 -1
  5. package/dist/Config.js.map +1 -1
  6. package/dist/Effect.d.ts +296 -8
  7. package/dist/Effect.d.ts.map +1 -1
  8. package/dist/Effect.js +72 -0
  9. package/dist/Effect.js.map +1 -1
  10. package/dist/ErrorReporter.d.ts +376 -0
  11. package/dist/ErrorReporter.d.ts.map +1 -0
  12. package/dist/ErrorReporter.js +246 -0
  13. package/dist/ErrorReporter.js.map +1 -0
  14. package/dist/Fiber.d.ts +2 -2
  15. package/dist/Fiber.d.ts.map +1 -1
  16. package/dist/Fiber.js.map +1 -1
  17. package/dist/Graph.d.ts.map +1 -1
  18. package/dist/Graph.js +3 -6
  19. package/dist/Graph.js.map +1 -1
  20. package/dist/LogLevel.d.ts +5 -0
  21. package/dist/LogLevel.d.ts.map +1 -1
  22. package/dist/LogLevel.js.map +1 -1
  23. package/dist/Logger.d.ts +25 -91
  24. package/dist/Logger.d.ts.map +1 -1
  25. package/dist/Logger.js +2 -3
  26. package/dist/Logger.js.map +1 -1
  27. package/dist/Queue.d.ts.map +1 -1
  28. package/dist/Queue.js +0 -1
  29. package/dist/Queue.js.map +1 -1
  30. package/dist/Random.d.ts +17 -0
  31. package/dist/Random.d.ts.map +1 -1
  32. package/dist/Random.js +17 -0
  33. package/dist/Random.js.map +1 -1
  34. package/dist/References.d.ts +3 -3
  35. package/dist/References.d.ts.map +1 -1
  36. package/dist/Schema.d.ts +3 -1
  37. package/dist/Schema.d.ts.map +1 -1
  38. package/dist/SchemaAST.d.ts.map +1 -1
  39. package/dist/SchemaAST.js +2 -1
  40. package/dist/SchemaAST.js.map +1 -1
  41. package/dist/Stream.d.ts +5 -5
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/internal/effect.js +98 -33
  47. package/dist/internal/effect.js.map +1 -1
  48. package/dist/internal/hashMap.js +2 -2
  49. package/dist/internal/hashMap.js.map +1 -1
  50. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  51. package/dist/unstable/ai/LanguageModel.js +86 -14
  52. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  53. package/dist/unstable/ai/McpSchema.d.ts +112 -36
  54. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  55. package/dist/unstable/ai/McpSchema.js +47 -10
  56. package/dist/unstable/ai/McpSchema.js.map +1 -1
  57. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  58. package/dist/unstable/ai/McpServer.js +33 -6
  59. package/dist/unstable/ai/McpServer.js.map +1 -1
  60. package/dist/unstable/ai/Tool.d.ts +16 -0
  61. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  62. package/dist/unstable/ai/Tool.js +14 -0
  63. package/dist/unstable/ai/Tool.js.map +1 -1
  64. package/dist/unstable/cli/CliOutput.js +37 -6
  65. package/dist/unstable/cli/CliOutput.js.map +1 -1
  66. package/dist/unstable/cli/Command.d.ts +199 -7
  67. package/dist/unstable/cli/Command.d.ts.map +1 -1
  68. package/dist/unstable/cli/Command.js +116 -6
  69. package/dist/unstable/cli/Command.js.map +1 -1
  70. package/dist/unstable/cli/HelpDoc.d.ts +60 -2
  71. package/dist/unstable/cli/HelpDoc.d.ts.map +1 -1
  72. package/dist/unstable/cli/internal/command.d.ts +11 -1
  73. package/dist/unstable/cli/internal/command.d.ts.map +1 -1
  74. package/dist/unstable/cli/internal/command.js +33 -8
  75. package/dist/unstable/cli/internal/command.js.map +1 -1
  76. package/dist/unstable/cli/internal/completions/CommandDescriptor.js +7 -2
  77. package/dist/unstable/cli/internal/completions/CommandDescriptor.js.map +1 -1
  78. package/dist/unstable/cli/internal/parser.js +10 -2
  79. package/dist/unstable/cli/internal/parser.js.map +1 -1
  80. package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  81. package/dist/unstable/cluster/ClusterWorkflowEngine.js +2 -2
  82. package/dist/unstable/cluster/ClusterWorkflowEngine.js.map +1 -1
  83. package/dist/unstable/http/Headers.d.ts.map +1 -1
  84. package/dist/unstable/http/Headers.js +27 -10
  85. package/dist/unstable/http/Headers.js.map +1 -1
  86. package/dist/unstable/http/HttpClient.d.ts +28 -4
  87. package/dist/unstable/http/HttpClient.d.ts.map +1 -1
  88. package/dist/unstable/http/HttpClient.js.map +1 -1
  89. package/dist/unstable/http/HttpEffect.d.ts +3 -8
  90. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  91. package/dist/unstable/http/HttpEffect.js +25 -31
  92. package/dist/unstable/http/HttpEffect.js.map +1 -1
  93. package/dist/unstable/http/HttpMiddleware.d.ts.map +1 -1
  94. package/dist/unstable/http/HttpMiddleware.js +4 -8
  95. package/dist/unstable/http/HttpMiddleware.js.map +1 -1
  96. package/dist/unstable/http/HttpServerError.d.ts +14 -27
  97. package/dist/unstable/http/HttpServerError.d.ts.map +1 -1
  98. package/dist/unstable/http/HttpServerError.js +37 -44
  99. package/dist/unstable/http/HttpServerError.js.map +1 -1
  100. package/dist/unstable/http/HttpServerRespondable.d.ts +2 -2
  101. package/dist/unstable/http/HttpServerRespondable.d.ts.map +1 -1
  102. package/dist/unstable/http/HttpServerRespondable.js +5 -5
  103. package/dist/unstable/http/HttpServerRespondable.js.map +1 -1
  104. package/dist/unstable/http/HttpServerResponse.d.ts +2 -1
  105. package/dist/unstable/http/HttpServerResponse.d.ts.map +1 -1
  106. package/dist/unstable/http/HttpServerResponse.js +2 -0
  107. package/dist/unstable/http/HttpServerResponse.js.map +1 -1
  108. package/dist/unstable/http/internal/preResponseHandler.d.ts +2 -0
  109. package/dist/unstable/http/internal/preResponseHandler.d.ts.map +1 -0
  110. package/dist/unstable/http/internal/preResponseHandler.js +10 -0
  111. package/dist/unstable/http/internal/preResponseHandler.js.map +1 -0
  112. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  113. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  114. package/dist/unstable/httpapi/HttpApiBuilder.js +1 -1
  115. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  116. package/dist/unstable/httpapi/HttpApiError.d.ts +11 -0
  117. package/dist/unstable/httpapi/HttpApiError.d.ts.map +1 -1
  118. package/dist/unstable/httpapi/HttpApiError.js +29 -9
  119. package/dist/unstable/httpapi/HttpApiError.js.map +1 -1
  120. package/dist/unstable/observability/OtlpLogger.d.ts.map +1 -1
  121. package/dist/unstable/observability/OtlpLogger.js +7 -4
  122. package/dist/unstable/observability/OtlpLogger.js.map +1 -1
  123. package/dist/unstable/reactivity/Atom.js +1 -1
  124. package/dist/unstable/reactivity/Atom.js.map +1 -1
  125. package/dist/unstable/reactivity/AtomRegistry.d.ts +6 -0
  126. package/dist/unstable/reactivity/AtomRegistry.d.ts.map +1 -1
  127. package/dist/unstable/reactivity/AtomRegistry.js +22 -1
  128. package/dist/unstable/reactivity/AtomRegistry.js.map +1 -1
  129. package/dist/unstable/rpc/RpcSchema.d.ts +13 -0
  130. package/dist/unstable/rpc/RpcSchema.d.ts.map +1 -1
  131. package/dist/unstable/rpc/RpcSchema.js +14 -0
  132. package/dist/unstable/rpc/RpcSchema.js.map +1 -1
  133. package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
  134. package/dist/unstable/rpc/RpcSerialization.js +34 -9
  135. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  136. package/dist/unstable/rpc/RpcServer.d.ts +0 -7
  137. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  138. package/dist/unstable/rpc/RpcServer.js +9 -10
  139. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  140. package/dist/unstable/workflow/WorkflowEngine.d.ts +6 -0
  141. package/dist/unstable/workflow/WorkflowEngine.d.ts.map +1 -1
  142. package/dist/unstable/workflow/WorkflowEngine.js +131 -0
  143. package/dist/unstable/workflow/WorkflowEngine.js.map +1 -1
  144. package/package.json +1 -1
  145. package/src/Channel.ts +9 -9
  146. package/src/Config.ts +171 -9
  147. package/src/Effect.ts +315 -8
  148. package/src/ErrorReporter.ts +459 -0
  149. package/src/Fiber.ts +9 -2
  150. package/src/Graph.ts +16 -6
  151. package/src/LogLevel.ts +6 -0
  152. package/src/Logger.ts +28 -95
  153. package/src/Queue.ts +0 -1
  154. package/src/Random.ts +18 -0
  155. package/src/References.ts +4 -4
  156. package/src/Schema.ts +1 -1
  157. package/src/SchemaAST.ts +2 -1
  158. package/src/Stream.ts +7 -7
  159. package/src/index.ts +5 -0
  160. package/src/internal/effect.ts +205 -49
  161. package/src/internal/hashMap.ts +2 -2
  162. package/src/unstable/ai/LanguageModel.ts +117 -16
  163. package/src/unstable/ai/McpSchema.ts +57 -11
  164. package/src/unstable/ai/McpServer.ts +44 -6
  165. package/src/unstable/ai/Tool.ts +15 -0
  166. package/src/unstable/cli/CliOutput.ts +45 -6
  167. package/src/unstable/cli/Command.ts +298 -11
  168. package/src/unstable/cli/HelpDoc.ts +68 -2
  169. package/src/unstable/cli/internal/command.ts +47 -11
  170. package/src/unstable/cli/internal/completions/CommandDescriptor.ts +7 -2
  171. package/src/unstable/cli/internal/parser.ts +11 -3
  172. package/src/unstable/cluster/ClusterWorkflowEngine.ts +2 -2
  173. package/src/unstable/http/Headers.ts +28 -13
  174. package/src/unstable/http/HttpClient.ts +45 -10
  175. package/src/unstable/http/HttpEffect.ts +30 -44
  176. package/src/unstable/http/HttpMiddleware.ts +4 -14
  177. package/src/unstable/http/HttpServerError.ts +42 -45
  178. package/src/unstable/http/HttpServerRespondable.ts +6 -6
  179. package/src/unstable/http/HttpServerResponse.ts +3 -1
  180. package/src/unstable/http/internal/preResponseHandler.ts +15 -0
  181. package/src/unstable/httpapi/HttpApiBuilder.ts +2 -1
  182. package/src/unstable/httpapi/HttpApiError.ts +30 -9
  183. package/src/unstable/observability/OtlpLogger.ts +9 -5
  184. package/src/unstable/reactivity/Atom.ts +1 -1
  185. package/src/unstable/reactivity/AtomRegistry.ts +29 -1
  186. package/src/unstable/rpc/RpcSchema.ts +17 -0
  187. package/src/unstable/rpc/RpcSerialization.ts +44 -9
  188. package/src/unstable/rpc/RpcServer.ts +14 -19
  189. package/src/unstable/workflow/WorkflowEngine.ts +178 -0
@@ -157,11 +157,16 @@ export const fromCommand = (cmd: Command.Any): CommandDescriptor => {
157
157
  }
158
158
  }
159
159
 
160
- const subcommands: Array<CommandDescriptor> = cmd.subcommands.map(fromCommand)
160
+ const subcommands: Array<CommandDescriptor> = []
161
+ for (const group of cmd.subcommands) {
162
+ for (const subcommand of group.commands) {
163
+ subcommands.push(fromCommand(subcommand))
164
+ }
165
+ }
161
166
 
162
167
  return {
163
168
  name: cmd.name,
164
- description: cmd.description,
169
+ description: cmd.shortDescription ?? cmd.description,
165
170
  flags,
166
171
  arguments: args,
167
172
  subcommands
@@ -273,8 +273,16 @@ const createFlagRegistry = (params: ReadonlyArray<FlagParam>): FlagRegistry => {
273
273
  }
274
274
 
275
275
  const buildSubcommandIndex = (
276
- subcommands: ReadonlyArray<Command<string, unknown, unknown, unknown>>
277
- ): Map<string, Command<string, unknown, unknown, unknown>> => new Map(subcommands.map((sub) => [sub.name, sub]))
276
+ subcommands: Command.Any["subcommands"]
277
+ ): Map<string, Command<string, unknown, unknown, unknown>> => {
278
+ const index = new Map<string, Command<string, unknown, unknown, unknown>>()
279
+ for (const group of subcommands) {
280
+ for (const subcommand of group.commands) {
281
+ index.set(subcommand.name, subcommand)
282
+ }
283
+ }
284
+ return index
285
+ }
278
286
 
279
287
  /* ========================================================================== */
280
288
  /* Flag Accumulator */
@@ -506,7 +514,7 @@ const resolveFirstValue = (
506
514
  // Not a subcommand. Check if this looks like a typo.
507
515
  const expectsArgs = toImpl(command).config.arguments.length > 0
508
516
  if (!expectsArgs && subIndex.size > 0) {
509
- const suggestions = suggest(value, command.subcommands.map((s) => s.name))
517
+ const suggestions = suggest(value, Array.from(subIndex.keys()))
510
518
  state.errors.push(
511
519
  new CliError.UnknownSubcommand({
512
520
  subcommand: value,
@@ -16,7 +16,7 @@ import * as Schema from "../../Schema.ts"
16
16
  import type * as Scope from "../../Scope.ts"
17
17
  import * as ServiceMap from "../../ServiceMap.ts"
18
18
  import * as Rpc from "../rpc/Rpc.ts"
19
- import * as RpcServer from "../rpc/RpcServer.ts"
19
+ import { ClientAbort } from "../rpc/RpcSchema.ts"
20
20
  import * as Activity from "../workflow/Activity.ts"
21
21
  import * as DurableClock from "../workflow/DurableClock.ts"
22
22
  import * as DurableDeferred from "../workflow/DurableDeferred.ts"
@@ -315,7 +315,7 @@ export const make = Effect.gen(function*() {
315
315
  // we only want to store interrupts as suspends when the
316
316
  // client requested it
317
317
  const suspend = cause.reasons.some((f) =>
318
- f._tag === "Interrupt" && f.fiberId === RpcServer.fiberIdClientInterrupt
318
+ f._tag === "Interrupt" && f.annotations.has(ClientAbort.key)
319
319
  )
320
320
  if (suspend) {
321
321
  interruptedActivities.add(activityId)
@@ -44,22 +44,37 @@ export interface Headers extends Redactable.Redactable {
44
44
  readonly [key: string]: string
45
45
  }
46
46
 
47
- const Proto = Object.assign(Object.create(null), Inspectable.BaseProto, {
48
- [TypeId]: TypeId,
49
- [Redactable.symbolRedactable](
50
- this: Headers,
51
- context: ServiceMap.ServiceMap<never>
52
- ): Record<string, string | Redacted.Redacted<string>> {
53
- return redact(this, ServiceMap.get(context, CurrentRedactedNames))
47
+ const Proto = Object.create(null)
48
+
49
+ Object.defineProperties(Proto, {
50
+ [TypeId]: {
51
+ value: TypeId
52
+ },
53
+ [Redactable.symbolRedactable]: {
54
+ value(this: Headers, context: ServiceMap.ServiceMap<never>): Record<string, string | Redacted.Redacted<string>> {
55
+ return redact(this, ServiceMap.get(context, CurrentRedactedNames))
56
+ }
57
+ },
58
+ toJSON: {
59
+ value(this: Headers) {
60
+ return Redactable.redact(this)
61
+ }
62
+ },
63
+ [Equal.symbol]: {
64
+ value(this: Headers, that: Headers): boolean {
65
+ return Equivalence(this, that)
66
+ }
54
67
  },
55
- toJSON() {
56
- return Redactable.redact(this)
68
+ [Hash.symbol]: {
69
+ value(this: Headers): number {
70
+ return Hash.structure(this)
71
+ }
57
72
  },
58
- [Equal.symbol](this: Headers, that: Headers): boolean {
59
- return Equivalence(this, that)
73
+ toString: {
74
+ value: Inspectable.BaseProto.toString
60
75
  },
61
- [Hash.symbol](this: Headers): number {
62
- return Hash.structure(this)
76
+ [Inspectable.NodeInspectSymbol]: {
77
+ value: Inspectable.BaseProto[Inspectable.NodeInspectSymbol]
63
78
  }
64
79
  })
65
80
 
@@ -949,13 +949,16 @@ export const retryTransient: {
949
949
  * @category error handling
950
950
  */
951
951
  <
952
- B,
953
952
  E,
953
+ B = never,
954
954
  ES = never,
955
955
  R1 = never,
956
- const RetryOn extends "errors-only" | "response-only" | "errors-and-responses" = never,
957
- Input = "errors-only" extends RetryOn ? E
958
- : "response-only" extends RetryOn ? HttpClientResponse.HttpClientResponse
956
+ const RetryOn extends "errors-only" | "response-only" | "errors-and-responses" =
957
+ | "errors-only"
958
+ | "response-only"
959
+ | "errors-and-responses",
960
+ Input = RetryOn extends "errors-only" ? E
961
+ : RetryOn extends "response-only" ? HttpClientResponse.HttpClientResponse
959
962
  : HttpClientResponse.HttpClientResponse | E
960
963
  >(
961
964
  options: {
@@ -963,7 +966,7 @@ export const retryTransient: {
963
966
  readonly while?: Predicate.Predicate<NoInfer<E | ES>>
964
967
  readonly schedule?: Schedule.Schedule<B, NoInfer<Input>, ES, R1>
965
968
  readonly times?: number
966
- } | Schedule.Schedule<B, NoInfer<Input>, ES, R1>
969
+ }
967
970
  ): <R>(self: HttpClient.With<E, R>) => HttpClient.With<E | ES, R1 | R>
968
971
  /**
969
972
  * Retries common transient errors, such as rate limiting, timeouts or network issues.
@@ -979,12 +982,15 @@ export const retryTransient: {
979
982
  <
980
983
  E,
981
984
  R,
982
- B,
985
+ B = never,
983
986
  ES = never,
984
987
  R1 = never,
985
- const RetryOn extends "errors-only" | "response-only" | "errors-and-responses" = never,
986
- Input = "errors-only" extends RetryOn ? E
987
- : "response-only" extends RetryOn ? HttpClientResponse.HttpClientResponse
988
+ const RetryOn extends "errors-only" | "response-only" | "errors-and-responses" =
989
+ | "errors-only"
990
+ | "response-only"
991
+ | "errors-and-responses",
992
+ Input = RetryOn extends "errors-only" ? E
993
+ : RetryOn extends "response-only" ? HttpClientResponse.HttpClientResponse
988
994
  : HttpClientResponse.HttpClientResponse | E
989
995
  >(
990
996
  self: HttpClient.With<E, R>,
@@ -993,7 +999,36 @@ export const retryTransient: {
993
999
  readonly while?: Predicate.Predicate<NoInfer<E | ES>>
994
1000
  readonly schedule?: Schedule.Schedule<B, NoInfer<Input>, ES, R1>
995
1001
  readonly times?: number
996
- } | Schedule.Schedule<B, NoInfer<Input>, ES, R1>
1002
+ }
1003
+ ): HttpClient.With<E | ES, R1 | R>
1004
+ /**
1005
+ * Retries common transient errors, such as rate limiting, timeouts or network issues.
1006
+ *
1007
+ * Use `retryOn` to focus on retrying errors, transient responses, or both.
1008
+ *
1009
+ * Specifying a `while` predicate allows you to consider other errors as
1010
+ * transient, and is ignored in "response-only" mode.
1011
+ *
1012
+ * @since 4.0.0
1013
+ * @category error handling
1014
+ */
1015
+ <B, E, ES = never, R1 = never>(
1016
+ options: Schedule.Schedule<B, NoInfer<HttpClientResponse.HttpClientResponse | E>, ES, R1>
1017
+ ): <R>(self: HttpClient.With<E, R>) => HttpClient.With<E | ES, R1 | R>
1018
+ /**
1019
+ * Retries common transient errors, such as rate limiting, timeouts or network issues.
1020
+ *
1021
+ * Use `retryOn` to focus on retrying errors, transient responses, or both.
1022
+ *
1023
+ * Specifying a `while` predicate allows you to consider other errors as
1024
+ * transient, and is ignored in "response-only" mode.
1025
+ *
1026
+ * @since 4.0.0
1027
+ * @category error handling
1028
+ */
1029
+ <E, R, B, ES = never, R1 = never>(
1030
+ self: HttpClient.With<E, R>,
1031
+ options: Schedule.Schedule<B, NoInfer<HttpClientResponse.HttpClientResponse | E>, ES, R1>
997
1032
  ): HttpClient.With<E | ES, R1 | R>
998
1033
  } = dual(
999
1034
  2,
@@ -1,23 +1,24 @@
1
1
  /**
2
2
  * @since 4.0.0
3
3
  */
4
- import type { Cause } from "../../Cause.ts"
4
+ import type * as Cause from "../../Cause.ts"
5
5
  import * as Effect from "../../Effect.ts"
6
6
  import * as Exit from "../../Exit.ts"
7
7
  import * as Fiber from "../../Fiber.ts"
8
8
  import { dual } from "../../Function.ts"
9
+ import { reportCauseUnsafe } from "../../internal/effect.ts"
9
10
  import * as Layer from "../../Layer.ts"
10
11
  import * as Scope from "../../Scope.ts"
11
12
  import * as ServiceMap from "../../ServiceMap.ts"
12
13
  import * as Stream from "../../Stream.ts"
13
- import * as UndefinedOr from "../../UndefinedOr.ts"
14
14
  import * as HttpBody from "./HttpBody.ts"
15
15
  import { type HttpMiddleware, tracer } from "./HttpMiddleware.ts"
16
- import { causeResponse, clientAbortFiberId, HttpServerError, InternalError } from "./HttpServerError.ts"
16
+ import { causeResponse, ClientAbort, HttpServerError, InternalError } from "./HttpServerError.ts"
17
17
  import { HttpServerRequest } from "./HttpServerRequest.ts"
18
18
  import * as Request from "./HttpServerRequest.ts"
19
19
  import type { HttpServerResponse } from "./HttpServerResponse.ts"
20
20
  import * as Response from "./HttpServerResponse.ts"
21
+ import { appendPreResponseHandlerUnsafe, requestPreResponseHandlers } from "./internal/preResponseHandler.ts"
21
22
 
22
23
  /**
23
24
  * @since 4.0.0
@@ -31,11 +32,12 @@ export const toHandled = <E, R, EH, RH>(
31
32
  ) => Effect.Effect<unknown, EH, RH>,
32
33
  middleware?: HttpMiddleware | undefined
33
34
  ): Effect.Effect<void, never, Exclude<R | RH | HttpServerRequest, Scope.Scope>> => {
34
- const handleCause = (cause: Cause<E | EH | HttpServerError>) =>
35
+ const handleCause = (cause: Cause.Cause<E | EH | HttpServerError>) =>
35
36
  Effect.flatMapEager(causeResponse(cause), ([response, cause]) => {
36
37
  const fiber = Fiber.getCurrent()!
38
+ reportCauseUnsafe(fiber, cause)
37
39
  const request = ServiceMap.getUnsafe(fiber.services, HttpServerRequest)
38
- const handler = fiber.getRef(PreResponseHandlers)
40
+ const handler = requestPreResponseHandlers.get(request)
39
41
  const cont = cause.reasons.length === 0 ? Effect.succeed(response) : Effect.failCause(cause)
40
42
  if (handler === undefined) {
41
43
  ;(request as any)[handledSymbol] = true
@@ -58,7 +60,7 @@ export const toHandled = <E, R, EH, RH>(
58
60
  onSuccess: (response) => {
59
61
  const fiber = Fiber.getCurrent()!
60
62
  const request = ServiceMap.getUnsafe(fiber.services, HttpServerRequest)
61
- const handler = fiber.getRef(PreResponseHandlers)
63
+ const handler = requestPreResponseHandlers.get(request)
62
64
  if (handler === undefined) {
63
65
  ;(request as any)[handledSymbol] = true
64
66
  return Effect.mapEager(handleResponse(request, response), () => response)
@@ -80,13 +82,16 @@ export const toHandled = <E, R, EH, RH>(
80
82
  Effect.matchCauseEffect(tracer(middleware(responded)), {
81
83
  onFailure(cause): Effect.Effect<void, EH, RH> {
82
84
  const fiber = Fiber.getCurrent()!
85
+ reportCauseUnsafe(fiber, cause)
83
86
  const request = ServiceMap.getUnsafe(fiber.services, HttpServerRequest)
84
- if (handledSymbol in request) {
85
- return Effect.void
86
- }
87
+ if (handledSymbol in request) return Effect.void
87
88
  return Effect.matchCauseEffectEager(causeResponse(cause), {
88
- onFailure: (_cause) => handleResponse(request, Response.empty({ status: 500 })),
89
- onSuccess: ([response]) => handleResponse(request, response)
89
+ onFailure(_) {
90
+ return handleResponse(request, Response.empty({ status: 500 }))
91
+ },
92
+ onSuccess([response]) {
93
+ return handleResponse(request, response)
94
+ }
90
95
  })
91
96
  },
92
97
  onSuccess(response): Effect.Effect<void, EH, RH> {
@@ -140,10 +145,10 @@ const scopeEjected = Symbol.for("effect/http/HttpEffect/scopeEjected")
140
145
  const scoped = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
141
146
  Effect.withFiber((fiber) => {
142
147
  const scope = Scope.makeUnsafe()
143
- const prev = ServiceMap.getOption(fiber.services, Scope.Scope)
148
+ const prevServices = fiber.services
144
149
  fiber.setServices(ServiceMap.add(fiber.services, Scope.Scope, scope))
145
150
  return Effect.onExitPrimitive(effect, (exit) => {
146
- fiber.setServices(ServiceMap.addOrOmit(fiber.services, Scope.Scope, prev))
151
+ fiber.setServices(prevServices)
147
152
  if (scopeEjected in scope) return undefined
148
153
  return Scope.closeUnsafe(scope, exit)
149
154
  }, true)
@@ -158,27 +163,13 @@ export type PreResponseHandler = (
158
163
  response: HttpServerResponse
159
164
  ) => Effect.Effect<HttpServerResponse, HttpServerError>
160
165
 
161
- /**
162
- * @since 4.0.0
163
- * @category Pre-response handlers
164
- */
165
- export const PreResponseHandlers = ServiceMap.Reference<PreResponseHandler | undefined>(
166
- "effect/http/HttpEffect/PreResponseHandlers",
167
- { defaultValue: () => undefined }
168
- )
169
-
170
166
  /**
171
167
  * @since 4.0.0
172
168
  * @category fiber refs
173
169
  */
174
- export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Effect<void> =>
175
- Effect.withFiber((fiber) => {
176
- const next = UndefinedOr.match(fiber.getRef(PreResponseHandlers), {
177
- onUndefined: () => handler,
178
- onDefined: (prev) => (request, response) =>
179
- Effect.flatMap(prev(request, response), (response) => handler(request, response))
180
- })
181
- fiber.setServices(ServiceMap.add(fiber.services, PreResponseHandlers, next))
170
+ export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Effect<void, never, HttpServerRequest> =>
171
+ HttpServerRequest.use((request) => {
172
+ appendPreResponseHandlerUnsafe(request, handler)
182
173
  return Effect.void
183
174
  })
184
175
 
@@ -191,33 +182,28 @@ export const withPreResponseHandler: {
191
182
  * @since 4.0.0
192
183
  * @category fiber refs
193
184
  */
194
- (handler: PreResponseHandler): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
185
+ (handler: PreResponseHandler): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | HttpServerRequest>
195
186
  /**
196
187
  * @since 4.0.0
197
188
  * @category fiber refs
198
189
  */
199
- <A, E, R>(self: Effect.Effect<A, E, R>, handler: PreResponseHandler): Effect.Effect<A, E, R>
190
+ <A, E, R>(self: Effect.Effect<A, E, R>, handler: PreResponseHandler): Effect.Effect<A, E, R | HttpServerRequest>
200
191
  } = dual<
201
192
  /**
202
193
  * @since 4.0.0
203
194
  * @category fiber refs
204
195
  */
205
- (handler: PreResponseHandler) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>,
196
+ (handler: PreResponseHandler) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | HttpServerRequest>,
206
197
  /**
207
198
  * @since 4.0.0
208
199
  * @category fiber refs
209
200
  */
210
- <A, E, R>(self: Effect.Effect<A, E, R>, handler: PreResponseHandler) => Effect.Effect<A, E, R>
201
+ <A, E, R>(self: Effect.Effect<A, E, R>, handler: PreResponseHandler) => Effect.Effect<A, E, R | HttpServerRequest>
211
202
  >(2, (self, handler) =>
212
- Effect.updateService(
213
- self,
214
- PreResponseHandlers,
215
- UndefinedOr.match({
216
- onUndefined: () => handler,
217
- onDefined: (prev) => (request, response) =>
218
- Effect.flatMap(prev(request, response), (response) => handler(request, response))
219
- })
220
- ))
203
+ HttpServerRequest.use((request) => {
204
+ appendPreResponseHandlerUnsafe(request, handler)
205
+ return self
206
+ }))
221
207
 
222
208
  /**
223
209
  * @since 4.0.0
@@ -254,7 +240,7 @@ export const toWebHandlerWith = <Provided, R = never, ReqR = Exclude<R, Provided
254
240
  ;(httpServerRequest as any)[resolveSymbol] = resolve
255
241
  const fiber = Effect.runForkWith(ServiceMap.makeUnsafe(contextMap))(httpApp as any)
256
242
  request.signal?.addEventListener("abort", () => {
257
- fiber.interruptUnsafe(clientAbortFiberId)
243
+ fiber.interruptUnsafe(undefined, ClientAbort.annotation)
258
244
  }, { once: true })
259
245
  })
260
246
  }
@@ -12,13 +12,13 @@ import { TracerEnabled } from "../../References.ts"
12
12
  import * as ServiceMap from "../../ServiceMap.ts"
13
13
  import { ParentSpan } from "../../Tracer.ts"
14
14
  import * as Headers from "./Headers.ts"
15
- import type { PreResponseHandler } from "./HttpEffect.ts"
16
15
  import { causeResponseStripped, exitResponse } from "./HttpServerError.ts"
17
16
  import { HttpServerRequest } from "./HttpServerRequest.ts"
18
17
  import * as Request from "./HttpServerRequest.ts"
19
18
  import * as Response from "./HttpServerResponse.ts"
20
19
  import type { HttpServerResponse } from "./HttpServerResponse.ts"
21
20
  import * as TraceContext from "./HttpTraceContext.ts"
21
+ import { appendPreResponseHandlerUnsafe } from "./internal/preResponseHandler.ts"
22
22
 
23
23
  /**
24
24
  * @since 4.0.0
@@ -142,10 +142,10 @@ export const tracer: <E, R>(
142
142
  parent: TraceContext.fromHeaders(request.headers),
143
143
  kind: "server"
144
144
  })
145
- const prevSpan = ServiceMap.getOption(fiber.services, ParentSpan)
145
+ const prevServices = fiber.services
146
146
  fiber.setServices(ServiceMap.add(fiber.services, ParentSpan, span))
147
147
  return Effect.onExitPrimitive(httpApp, (exit) => {
148
- fiber.setServices(ServiceMap.addOrOmit(fiber.services, ParentSpan, prevSpan))
148
+ fiber.setServices(prevServices)
149
149
  const endTime = fiber.getRef(Clock).currentTimeNanosUnsafe()
150
150
  fiber.currentScheduler.scheduleTask(() => {
151
151
  const url = Request.toURL(request)
@@ -338,17 +338,7 @@ export const cors = (options?: {
338
338
  headers: headersFromRequestOptions(request)
339
339
  }))
340
340
  }
341
- const prev = fiber.getRef(PreResponseHandlers)
342
- const next = prev
343
- ? ((req: HttpServerRequest, res: HttpServerResponse) =>
344
- Effect.flatMap(prev(req, res), (res) => preResponseHandler(req, res)))
345
- : preResponseHandler
346
- fiber.setServices(ServiceMap.add(fiber.services, PreResponseHandlers, next))
341
+ appendPreResponseHandlerUnsafe(request, preResponseHandler)
347
342
  return httpApp
348
343
  })
349
344
  }
350
-
351
- const PreResponseHandlers = ServiceMap.Reference<PreResponseHandler | undefined>(
352
- "effect/http/HttpEffect/PreResponseHandlers",
353
- { defaultValue: () => undefined }
354
- )
@@ -4,8 +4,11 @@
4
4
  import * as Cause from "../../Cause.ts"
5
5
  import * as Data from "../../Data.ts"
6
6
  import * as Effect from "../../Effect.ts"
7
+ import * as ErrorReporter from "../../ErrorReporter.ts"
7
8
  import type * as Exit from "../../Exit.ts"
9
+ import { constUndefined } from "../../Function.ts"
8
10
  import { hasProperty } from "../../Predicate.ts"
11
+ import * as ServiceMap from "../../ServiceMap.ts"
9
12
  import type * as Request from "./HttpServerRequest.ts"
10
13
  import * as Respondable from "./HttpServerRespondable.ts"
11
14
  import * as Response from "./HttpServerResponse.ts"
@@ -32,35 +35,24 @@ export class HttpServerError extends Data.TaggedError("HttpServerError")<{
32
35
  }
33
36
  }
34
37
 
35
- /**
36
- * @since 4.0.0
37
- */
38
38
  readonly [TypeId] = TypeId
39
39
 
40
- /**
41
- * @since 4.0.0
42
- */
43
40
  override stack = `${this.name}: ${this.message}`
44
41
 
45
- /**
46
- * @since 4.0.0
47
- */
48
42
  get request(): Request.HttpServerRequest {
49
43
  return this.reason.request
50
44
  }
51
45
 
52
- /**
53
- * @since 4.0.0
54
- */
55
46
  get response(): Response.HttpServerResponse | undefined {
56
47
  return "response" in this.reason ? this.reason.response : undefined
57
48
  }
58
49
 
59
- /**
60
- * @since 4.0.0
61
- */
62
- [Respondable.TypeId]() {
63
- return this.reason[Respondable.TypeId]()
50
+ [Respondable.symbol]() {
51
+ return this.reason[Respondable.symbol]()
52
+ }
53
+
54
+ override get [ErrorReporter.ignore](): boolean {
55
+ return this.reason[ErrorReporter.ignore] ?? false
64
56
  }
65
57
 
66
58
  override get message(): string {
@@ -80,7 +72,7 @@ export class RequestParseError extends Data.TaggedError("RequestParseError")<{
80
72
  /**
81
73
  * @since 4.0.0
82
74
  */
83
- [Respondable.TypeId]() {
75
+ [Respondable.symbol]() {
84
76
  return Effect.succeed(Response.empty({ status: 400 }))
85
77
  }
86
78
 
@@ -102,13 +94,12 @@ export class RouteNotFound extends Data.TaggedError("RouteNotFound")<{
102
94
  readonly description?: string
103
95
  readonly cause?: unknown
104
96
  }> implements Respondable.Respondable {
105
- /**
106
- * @since 4.0.0
107
- */
108
- [Respondable.TypeId]() {
97
+ [Respondable.symbol]() {
109
98
  return Effect.succeed(Response.empty({ status: 404 }))
110
99
  }
111
100
 
101
+ override readonly [ErrorReporter.ignore] = true
102
+
112
103
  get methodAndUrl() {
113
104
  return `${this.request.method} ${this.request.url}`
114
105
  }
@@ -130,7 +121,7 @@ export class InternalError extends Data.TaggedError("InternalError")<{
130
121
  /**
131
122
  * @since 4.0.0
132
123
  */
133
- [Respondable.TypeId]() {
124
+ [Respondable.symbol]() {
134
125
  return Effect.succeed(Response.empty({ status: 500 }))
135
126
  }
136
127
 
@@ -159,10 +150,7 @@ export class ResponseError extends Data.TaggedError("ResponseError")<{
159
150
  readonly description?: string
160
151
  readonly cause?: unknown
161
152
  }> implements Respondable.Respondable {
162
- /**
163
- * @since 4.0.0
164
- */
165
- [Respondable.TypeId]() {
153
+ [Respondable.symbol]() {
166
154
  return Effect.succeed(Response.empty({ status: 500 }))
167
155
  }
168
156
 
@@ -196,16 +184,25 @@ export class ServeError extends Data.TaggedError("ServeError")<{
196
184
  readonly cause: unknown
197
185
  }> {}
198
186
 
187
+ /**
188
+ * @since 4.0.0
189
+ * @category Annotations
190
+ */
191
+ export class ClientAbort extends ServiceMap.Service<ClientAbort, true>()("effect/http/HttpServerError/ClientAbort") {
192
+ static annotation = this.serviceMap(true).pipe(
193
+ ServiceMap.add(Cause.StackTrace, {
194
+ name: "ClientAbort",
195
+ stack: constUndefined,
196
+ parent: undefined
197
+ })
198
+ )
199
+ }
200
+
199
201
  const formatRequestMessage = (reason: string, description: string | undefined, info: string) => {
200
202
  const prefix = `${reason} (${info})`
201
203
  return description ? `${prefix}: ${description}` : prefix
202
204
  }
203
205
 
204
- /**
205
- * @since 4.0.0
206
- */
207
- export const clientAbortFiberId = -499
208
-
209
206
  /**
210
207
  * @since 4.0.0
211
208
  */
@@ -215,37 +212,37 @@ export const causeResponse = <E>(
215
212
  let response: Response.HttpServerResponse | undefined
216
213
  let effect = succeedInternalServerError
217
214
  const failures: Array<Cause.Reason<E>> = []
218
- let interrupt: Cause.Interrupt | undefined
215
+ let interrupts: Array<Cause.Interrupt> = []
219
216
  let isClientInterrupt = false
220
217
  for (let i = 0; i < cause.reasons.length; i++) {
221
- const f = cause.reasons[i]
222
- switch (f._tag) {
218
+ const reason = cause.reasons[i]
219
+ switch (reason._tag) {
223
220
  case "Fail": {
224
- effect = Respondable.toResponseOrElse(f.error, internalServerError)
225
- failures.push(f)
221
+ effect = Respondable.toResponseOrElse(reason.error, internalServerError)
222
+ failures.push(reason)
226
223
  break
227
224
  }
228
225
  case "Die": {
229
- if (Response.isHttpServerResponse(f.defect)) {
230
- response = f.defect
226
+ if (Response.isHttpServerResponse(reason.defect)) {
227
+ response = reason.defect
231
228
  } else {
232
- effect = Respondable.toResponseOrElseDefect(f.defect, internalServerError)
233
- failures.push(f)
229
+ effect = Respondable.toResponseOrElseDefect(reason.defect, internalServerError)
230
+ failures.push(reason)
234
231
  }
235
232
  break
236
233
  }
237
234
  case "Interrupt": {
238
- isClientInterrupt = isClientInterrupt || f.fiberId === clientAbortFiberId
235
+ isClientInterrupt = reason.annotations.has(ClientAbort.key)
239
236
  if (failures.length > 0) break
240
- interrupt = f
237
+ interrupts.push(reason)
241
238
  break
242
239
  }
243
240
  }
244
241
  }
245
242
  if (response) {
246
243
  return Effect.succeed([response, Cause.fromReasons(failures)] as const)
247
- } else if (interrupt && failures.length === 0) {
248
- failures.push(isClientInterrupt ? Cause.makeInterruptReason(clientAbortFiberId) : interrupt)
244
+ } else if (interrupts.length > 0 && failures.length === 0) {
245
+ failures.push(...interrupts)
249
246
  effect = isClientInterrupt ? clientAbortError : serverAbortError
250
247
  }
251
248
  return Effect.mapEager(effect, (response) => {
@@ -12,21 +12,21 @@ import * as Response from "./HttpServerResponse.ts"
12
12
  * @since 4.0.0
13
13
  * @category Type IDs
14
14
  */
15
- export const TypeId = "~effect/http/HttpServerRespondable"
15
+ export const symbol = "~effect/http/HttpServerRespondable"
16
16
 
17
17
  /**
18
18
  * @since 4.0.0
19
19
  * @category models
20
20
  */
21
21
  export interface Respondable {
22
- readonly [TypeId]: () => Effect.Effect<HttpServerResponse, unknown>
22
+ [symbol](): Effect.Effect<HttpServerResponse, unknown>
23
23
  }
24
24
 
25
25
  /**
26
26
  * @since 4.0.0
27
27
  * @category guards
28
28
  */
29
- export const isRespondable = (u: unknown): u is Respondable => hasProperty(u, TypeId)
29
+ export const isRespondable = (u: unknown): u is Respondable => hasProperty(u, symbol)
30
30
 
31
31
  const badRequest = Response.empty({ status: 400 })
32
32
  const notFound = Response.empty({ status: 404 })
@@ -39,7 +39,7 @@ export const toResponse = (self: Respondable): Effect.Effect<HttpServerResponse>
39
39
  if (Response.isHttpServerResponse(self)) {
40
40
  return Effect.succeed(self)
41
41
  }
42
- return Effect.orDie(self[TypeId]())
42
+ return Effect.orDie(self[symbol]())
43
43
  }
44
44
 
45
45
  /**
@@ -50,7 +50,7 @@ export const toResponseOrElse = (u: unknown, orElse: HttpServerResponse): Effect
50
50
  if (Response.isHttpServerResponse(u)) {
51
51
  return Effect.succeed(u)
52
52
  } else if (isRespondable(u)) {
53
- return Effect.catchCause(u[TypeId](), () => Effect.succeed(orElse))
53
+ return Effect.catchCause(u[symbol](), () => Effect.succeed(orElse))
54
54
  // add support for some commmon types
55
55
  } else if (Schema.isSchemaError(u)) {
56
56
  return Effect.succeed(badRequest)
@@ -68,7 +68,7 @@ export const toResponseOrElseDefect = (u: unknown, orElse: HttpServerResponse):
68
68
  if (Response.isHttpServerResponse(u)) {
69
69
  return Effect.succeed(u)
70
70
  } else if (isRespondable(u)) {
71
- return Effect.catchCause(u[TypeId](), () => Effect.succeed(orElse))
71
+ return Effect.catchCause(u[symbol](), () => Effect.succeed(orElse))
72
72
  }
73
73
  return Effect.succeed(orElse)
74
74
  }