effect 4.0.0-beta.11 → 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 (95) hide show
  1. package/dist/Config.d.ts +157 -0
  2. package/dist/Config.d.ts.map +1 -1
  3. package/dist/Config.js +56 -1
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Effect.d.ts +79 -0
  6. package/dist/Effect.d.ts.map +1 -1
  7. package/dist/Effect.js +26 -0
  8. package/dist/Effect.js.map +1 -1
  9. package/dist/Fiber.d.ts +2 -2
  10. package/dist/Fiber.d.ts.map +1 -1
  11. package/dist/Fiber.js.map +1 -1
  12. package/dist/Graph.d.ts.map +1 -1
  13. package/dist/Graph.js +3 -6
  14. package/dist/Graph.js.map +1 -1
  15. package/dist/Random.d.ts +17 -0
  16. package/dist/Random.d.ts.map +1 -1
  17. package/dist/Random.js +17 -0
  18. package/dist/Random.js.map +1 -1
  19. package/dist/Schema.d.ts +3 -1
  20. package/dist/Schema.d.ts.map +1 -1
  21. package/dist/internal/effect.js +59 -44
  22. package/dist/internal/effect.js.map +1 -1
  23. package/dist/unstable/ai/LanguageModel.d.ts.map +1 -1
  24. package/dist/unstable/ai/LanguageModel.js +49 -18
  25. package/dist/unstable/ai/LanguageModel.js.map +1 -1
  26. package/dist/unstable/ai/McpSchema.d.ts +112 -36
  27. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  28. package/dist/unstable/ai/McpSchema.js +47 -10
  29. package/dist/unstable/ai/McpSchema.js.map +1 -1
  30. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  31. package/dist/unstable/ai/McpServer.js +33 -6
  32. package/dist/unstable/ai/McpServer.js.map +1 -1
  33. package/dist/unstable/ai/Tool.d.ts +16 -0
  34. package/dist/unstable/ai/Tool.d.ts.map +1 -1
  35. package/dist/unstable/ai/Tool.js +14 -0
  36. package/dist/unstable/ai/Tool.js.map +1 -1
  37. package/dist/unstable/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  38. package/dist/unstable/cluster/ClusterWorkflowEngine.js +2 -2
  39. package/dist/unstable/cluster/ClusterWorkflowEngine.js.map +1 -1
  40. package/dist/unstable/http/HttpClient.d.ts +28 -4
  41. package/dist/unstable/http/HttpClient.d.ts.map +1 -1
  42. package/dist/unstable/http/HttpClient.js.map +1 -1
  43. package/dist/unstable/http/HttpEffect.d.ts +3 -8
  44. package/dist/unstable/http/HttpEffect.d.ts.map +1 -1
  45. package/dist/unstable/http/HttpEffect.js +13 -24
  46. package/dist/unstable/http/HttpEffect.js.map +1 -1
  47. package/dist/unstable/http/HttpMiddleware.d.ts.map +1 -1
  48. package/dist/unstable/http/HttpMiddleware.js +4 -8
  49. package/dist/unstable/http/HttpMiddleware.js.map +1 -1
  50. package/dist/unstable/http/HttpServerError.d.ts +6 -1
  51. package/dist/unstable/http/HttpServerError.d.ts.map +1 -1
  52. package/dist/unstable/http/HttpServerError.js +26 -17
  53. package/dist/unstable/http/HttpServerError.js.map +1 -1
  54. package/dist/unstable/http/internal/preResponseHandler.d.ts +2 -0
  55. package/dist/unstable/http/internal/preResponseHandler.d.ts.map +1 -0
  56. package/dist/unstable/http/internal/preResponseHandler.js +10 -0
  57. package/dist/unstable/http/internal/preResponseHandler.js.map +1 -0
  58. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  59. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  60. package/dist/unstable/reactivity/Atom.js +1 -1
  61. package/dist/unstable/reactivity/Atom.js.map +1 -1
  62. package/dist/unstable/rpc/RpcSchema.d.ts +13 -0
  63. package/dist/unstable/rpc/RpcSchema.d.ts.map +1 -1
  64. package/dist/unstable/rpc/RpcSchema.js +14 -0
  65. package/dist/unstable/rpc/RpcSchema.js.map +1 -1
  66. package/dist/unstable/rpc/RpcSerialization.d.ts.map +1 -1
  67. package/dist/unstable/rpc/RpcSerialization.js +34 -9
  68. package/dist/unstable/rpc/RpcSerialization.js.map +1 -1
  69. package/dist/unstable/rpc/RpcServer.d.ts +0 -7
  70. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  71. package/dist/unstable/rpc/RpcServer.js +5 -10
  72. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  73. package/package.json +1 -1
  74. package/src/Config.ts +171 -9
  75. package/src/Effect.ts +80 -0
  76. package/src/Fiber.ts +9 -2
  77. package/src/Graph.ts +16 -6
  78. package/src/Random.ts +18 -0
  79. package/src/Schema.ts +1 -1
  80. package/src/internal/effect.ts +82 -49
  81. package/src/unstable/ai/LanguageModel.ts +54 -24
  82. package/src/unstable/ai/McpSchema.ts +57 -11
  83. package/src/unstable/ai/McpServer.ts +44 -6
  84. package/src/unstable/ai/Tool.ts +15 -0
  85. package/src/unstable/cluster/ClusterWorkflowEngine.ts +2 -2
  86. package/src/unstable/http/HttpClient.ts +45 -10
  87. package/src/unstable/http/HttpEffect.ts +20 -39
  88. package/src/unstable/http/HttpMiddleware.ts +4 -14
  89. package/src/unstable/http/HttpServerError.ts +29 -18
  90. package/src/unstable/http/internal/preResponseHandler.ts +15 -0
  91. package/src/unstable/httpapi/HttpApiBuilder.ts +1 -1
  92. package/src/unstable/reactivity/Atom.ts +1 -1
  93. package/src/unstable/rpc/RpcSchema.ts +17 -0
  94. package/src/unstable/rpc/RpcSerialization.ts +44 -9
  95. package/src/unstable/rpc/RpcServer.ts +10 -19
@@ -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,7 +1,7 @@
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"
@@ -11,14 +11,14 @@ import * as Layer from "../../Layer.ts"
11
11
  import * as Scope from "../../Scope.ts"
12
12
  import * as ServiceMap from "../../ServiceMap.ts"
13
13
  import * as Stream from "../../Stream.ts"
14
- import * as UndefinedOr from "../../UndefinedOr.ts"
15
14
  import * as HttpBody from "./HttpBody.ts"
16
15
  import { type HttpMiddleware, tracer } from "./HttpMiddleware.ts"
17
- import { causeResponse, clientAbortFiberId, HttpServerError, InternalError } from "./HttpServerError.ts"
16
+ import { causeResponse, ClientAbort, HttpServerError, InternalError } from "./HttpServerError.ts"
18
17
  import { HttpServerRequest } from "./HttpServerRequest.ts"
19
18
  import * as Request from "./HttpServerRequest.ts"
20
19
  import type { HttpServerResponse } from "./HttpServerResponse.ts"
21
20
  import * as Response from "./HttpServerResponse.ts"
21
+ import { appendPreResponseHandlerUnsafe, requestPreResponseHandlers } from "./internal/preResponseHandler.ts"
22
22
 
23
23
  /**
24
24
  * @since 4.0.0
@@ -32,12 +32,12 @@ export const toHandled = <E, R, EH, RH>(
32
32
  ) => Effect.Effect<unknown, EH, RH>,
33
33
  middleware?: HttpMiddleware | undefined
34
34
  ): Effect.Effect<void, never, Exclude<R | RH | HttpServerRequest, Scope.Scope>> => {
35
- const handleCause = (cause: Cause<E | EH | HttpServerError>) =>
35
+ const handleCause = (cause: Cause.Cause<E | EH | HttpServerError>) =>
36
36
  Effect.flatMapEager(causeResponse(cause), ([response, cause]) => {
37
37
  const fiber = Fiber.getCurrent()!
38
38
  reportCauseUnsafe(fiber, cause)
39
39
  const request = ServiceMap.getUnsafe(fiber.services, HttpServerRequest)
40
- const handler = fiber.getRef(PreResponseHandlers)
40
+ const handler = requestPreResponseHandlers.get(request)
41
41
  const cont = cause.reasons.length === 0 ? Effect.succeed(response) : Effect.failCause(cause)
42
42
  if (handler === undefined) {
43
43
  ;(request as any)[handledSymbol] = true
@@ -60,7 +60,7 @@ export const toHandled = <E, R, EH, RH>(
60
60
  onSuccess: (response) => {
61
61
  const fiber = Fiber.getCurrent()!
62
62
  const request = ServiceMap.getUnsafe(fiber.services, HttpServerRequest)
63
- const handler = fiber.getRef(PreResponseHandlers)
63
+ const handler = requestPreResponseHandlers.get(request)
64
64
  if (handler === undefined) {
65
65
  ;(request as any)[handledSymbol] = true
66
66
  return Effect.mapEager(handleResponse(request, response), () => response)
@@ -145,10 +145,10 @@ const scopeEjected = Symbol.for("effect/http/HttpEffect/scopeEjected")
145
145
  const scoped = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
146
146
  Effect.withFiber((fiber) => {
147
147
  const scope = Scope.makeUnsafe()
148
- const prev = ServiceMap.getOption(fiber.services, Scope.Scope)
148
+ const prevServices = fiber.services
149
149
  fiber.setServices(ServiceMap.add(fiber.services, Scope.Scope, scope))
150
150
  return Effect.onExitPrimitive(effect, (exit) => {
151
- fiber.setServices(ServiceMap.addOrOmit(fiber.services, Scope.Scope, prev))
151
+ fiber.setServices(prevServices)
152
152
  if (scopeEjected in scope) return undefined
153
153
  return Scope.closeUnsafe(scope, exit)
154
154
  }, true)
@@ -163,27 +163,13 @@ export type PreResponseHandler = (
163
163
  response: HttpServerResponse
164
164
  ) => Effect.Effect<HttpServerResponse, HttpServerError>
165
165
 
166
- /**
167
- * @since 4.0.0
168
- * @category Pre-response handlers
169
- */
170
- export const PreResponseHandlers = ServiceMap.Reference<PreResponseHandler | undefined>(
171
- "effect/http/HttpEffect/PreResponseHandlers",
172
- { defaultValue: () => undefined }
173
- )
174
-
175
166
  /**
176
167
  * @since 4.0.0
177
168
  * @category fiber refs
178
169
  */
179
- export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Effect<void> =>
180
- Effect.withFiber((fiber) => {
181
- const next = UndefinedOr.match(fiber.getRef(PreResponseHandlers), {
182
- onUndefined: () => handler,
183
- onDefined: (prev) => (request, response) =>
184
- Effect.flatMap(prev(request, response), (response) => handler(request, response))
185
- })
186
- 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)
187
173
  return Effect.void
188
174
  })
189
175
 
@@ -196,33 +182,28 @@ export const withPreResponseHandler: {
196
182
  * @since 4.0.0
197
183
  * @category fiber refs
198
184
  */
199
- (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>
200
186
  /**
201
187
  * @since 4.0.0
202
188
  * @category fiber refs
203
189
  */
204
- <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>
205
191
  } = dual<
206
192
  /**
207
193
  * @since 4.0.0
208
194
  * @category fiber refs
209
195
  */
210
- (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>,
211
197
  /**
212
198
  * @since 4.0.0
213
199
  * @category fiber refs
214
200
  */
215
- <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>
216
202
  >(2, (self, handler) =>
217
- Effect.updateService(
218
- self,
219
- PreResponseHandlers,
220
- UndefinedOr.match({
221
- onUndefined: () => handler,
222
- onDefined: (prev) => (request, response) =>
223
- Effect.flatMap(prev(request, response), (response) => handler(request, response))
224
- })
225
- ))
203
+ HttpServerRequest.use((request) => {
204
+ appendPreResponseHandlerUnsafe(request, handler)
205
+ return self
206
+ }))
226
207
 
227
208
  /**
228
209
  * @since 4.0.0
@@ -259,7 +240,7 @@ export const toWebHandlerWith = <Provided, R = never, ReqR = Exclude<R, Provided
259
240
  ;(httpServerRequest as any)[resolveSymbol] = resolve
260
241
  const fiber = Effect.runForkWith(ServiceMap.makeUnsafe(contextMap))(httpApp as any)
261
242
  request.signal?.addEventListener("abort", () => {
262
- fiber.interruptUnsafe(clientAbortFiberId)
243
+ fiber.interruptUnsafe(undefined, ClientAbort.annotation)
263
244
  }, { once: true })
264
245
  })
265
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
- )
@@ -6,7 +6,9 @@ import * as Data from "../../Data.ts"
6
6
  import * as Effect from "../../Effect.ts"
7
7
  import * as ErrorReporter from "../../ErrorReporter.ts"
8
8
  import type * as Exit from "../../Exit.ts"
9
+ import { constUndefined } from "../../Function.ts"
9
10
  import { hasProperty } from "../../Predicate.ts"
11
+ import * as ServiceMap from "../../ServiceMap.ts"
10
12
  import type * as Request from "./HttpServerRequest.ts"
11
13
  import * as Respondable from "./HttpServerRespondable.ts"
12
14
  import * as Response from "./HttpServerResponse.ts"
@@ -182,16 +184,25 @@ export class ServeError extends Data.TaggedError("ServeError")<{
182
184
  readonly cause: unknown
183
185
  }> {}
184
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
+
185
201
  const formatRequestMessage = (reason: string, description: string | undefined, info: string) => {
186
202
  const prefix = `${reason} (${info})`
187
203
  return description ? `${prefix}: ${description}` : prefix
188
204
  }
189
205
 
190
- /**
191
- * @since 4.0.0
192
- */
193
- export const clientAbortFiberId = -499
194
-
195
206
  /**
196
207
  * @since 4.0.0
197
208
  */
@@ -201,37 +212,37 @@ export const causeResponse = <E>(
201
212
  let response: Response.HttpServerResponse | undefined
202
213
  let effect = succeedInternalServerError
203
214
  const failures: Array<Cause.Reason<E>> = []
204
- let interrupt: Cause.Interrupt | undefined
215
+ let interrupts: Array<Cause.Interrupt> = []
205
216
  let isClientInterrupt = false
206
217
  for (let i = 0; i < cause.reasons.length; i++) {
207
- const f = cause.reasons[i]
208
- switch (f._tag) {
218
+ const reason = cause.reasons[i]
219
+ switch (reason._tag) {
209
220
  case "Fail": {
210
- effect = Respondable.toResponseOrElse(f.error, internalServerError)
211
- failures.push(f)
221
+ effect = Respondable.toResponseOrElse(reason.error, internalServerError)
222
+ failures.push(reason)
212
223
  break
213
224
  }
214
225
  case "Die": {
215
- if (Response.isHttpServerResponse(f.defect)) {
216
- response = f.defect
226
+ if (Response.isHttpServerResponse(reason.defect)) {
227
+ response = reason.defect
217
228
  } else {
218
- effect = Respondable.toResponseOrElseDefect(f.defect, internalServerError)
219
- failures.push(f)
229
+ effect = Respondable.toResponseOrElseDefect(reason.defect, internalServerError)
230
+ failures.push(reason)
220
231
  }
221
232
  break
222
233
  }
223
234
  case "Interrupt": {
224
- isClientInterrupt = isClientInterrupt || f.fiberId === clientAbortFiberId
235
+ isClientInterrupt = reason.annotations.has(ClientAbort.key)
225
236
  if (failures.length > 0) break
226
- interrupt = f
237
+ interrupts.push(reason)
227
238
  break
228
239
  }
229
240
  }
230
241
  }
231
242
  if (response) {
232
243
  return Effect.succeed([response, Cause.fromReasons(failures)] as const)
233
- } else if (interrupt && failures.length === 0) {
234
- failures.push(isClientInterrupt ? Cause.makeInterruptReason(clientAbortFiberId) : interrupt)
244
+ } else if (interrupts.length > 0 && failures.length === 0) {
245
+ failures.push(...interrupts)
235
246
  effect = isClientInterrupt ? clientAbortError : serverAbortError
236
247
  }
237
248
  return Effect.mapEager(effect, (response) => {
@@ -0,0 +1,15 @@
1
+ import * as Effect from "../../../Effect.ts"
2
+ import type { PreResponseHandler } from "../HttpEffect.ts"
3
+ import type { HttpServerRequest } from "../HttpServerRequest.ts"
4
+
5
+ /** @internal */
6
+ export const requestPreResponseHandlers = new WeakMap<HttpServerRequest, PreResponseHandler>()
7
+
8
+ /** @internal */
9
+ export const appendPreResponseHandlerUnsafe = (request: HttpServerRequest, handler: PreResponseHandler): void => {
10
+ const prev = requestPreResponseHandlers.get(request)
11
+ const next: PreResponseHandler = prev ?
12
+ (request, response) => Effect.flatMap(prev(request, response), (response) => handler(request, response))
13
+ : handler
14
+ requestPreResponseHandlers.set(request, next)
15
+ }
@@ -356,7 +356,7 @@ export const securitySetCookie = (
356
356
  self: HttpApiSecurity.ApiKey,
357
357
  value: string | Redacted.Redacted,
358
358
  options?: Cookie["options"]
359
- ): Effect.Effect<void> =>
359
+ ): Effect.Effect<void, never, HttpServerRequest> =>
360
360
  HttpEffect.appendPreResponseHandler((_req, response) =>
361
361
  Effect.orDie(
362
362
  Response.setCookie(response, self.key, stringOrRedacted(value), {
@@ -2070,7 +2070,7 @@ export const searchParam = <S extends Schema.Codec<any, string> = never>(name: s
2070
2070
  window.removeEventListener("pushstate", handleUpdate)
2071
2071
  })
2072
2072
  const value = new URLSearchParams(window.location.search).get(name) || ""
2073
- return decode ? Exit.findErrorOption(decode(value)) : value as any
2073
+ return decode ? Exit.getSuccess(decode(value)) : value as any
2074
2074
  },
2075
2075
  (ctx, value: any) => {
2076
2076
  if (typeof window === "undefined") {
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * @since 4.0.0
3
3
  */
4
+ import * as Cause from "../../Cause.ts"
5
+ import { constUndefined } from "../../Function.ts"
4
6
  import * as Predicate from "../../Predicate.ts"
5
7
  import * as Schema from "../../Schema.ts"
6
8
  import type * as AST from "../../SchemaAST.ts"
9
+ import * as ServiceMap from "../../ServiceMap.ts"
7
10
  import * as Stream_ from "../../Stream.ts"
8
11
 
9
12
  const StreamSchemaTypeId = "~effect/rpc/RpcSchema/StreamSchema"
@@ -58,3 +61,17 @@ const schema = Schema.declare(Stream_.isStream)
58
61
  export function Stream<A extends Schema.Top, E extends Schema.Top>(success: A, error: E): Stream<A, E> {
59
62
  return Schema.make(schema.ast, { [StreamSchemaTypeId]: StreamSchemaTypeId, success, error })
60
63
  }
64
+
65
+ /**
66
+ * @since 4.0.0
67
+ * @category Cause annotations
68
+ */
69
+ export class ClientAbort extends ServiceMap.Service<ClientAbort, true>()("effect/rpc/RpcSchema/ClientAbort") {
70
+ static annotation = this.serviceMap(true).pipe(
71
+ ServiceMap.add(Cause.StackTrace, {
72
+ name: "ClientAbort",
73
+ stack: constUndefined,
74
+ parent: undefined
75
+ })
76
+ )
77
+ }
@@ -107,11 +107,7 @@ export const jsonRpc = (options?: {
107
107
  return decodeJsonRpcRaw(decoded, batches)
108
108
  },
109
109
  encode: (response) => {
110
- if (Array.isArray(response)) {
111
- if (response.length === 0) return undefined
112
- return JSON.stringify(response.map(encodeJsonRpcMessage))
113
- }
114
- const encoded = encodeJsonRpcRaw(response as any, batches)
110
+ const encoded = encodeJsonRpcResponse(response as any, batches)
115
111
  return encoded && JSON.stringify(encoded)
116
112
  }
117
113
  }
@@ -146,10 +142,7 @@ export const ndJsonRpc = (options?: {
146
142
  return messages
147
143
  },
148
144
  encode: (response) => {
149
- if (Array.isArray(response)) {
150
- return parser.encode(response.map(encodeJsonRpcMessage))
151
- }
152
- const encoded = encodeJsonRpcRaw(response as any, batches)
145
+ const encoded = encodeJsonRpcResponse(response as any, batches)
153
146
  return encoded && parser.encode(encoded)
154
147
  }
155
148
  })
@@ -171,6 +164,7 @@ function decodeJsonRpcRaw(
171
164
  const messages: Array<RpcMessage.FromClientEncoded | RpcMessage.FromServerEncoded> = []
172
165
  for (let i = 0; i < decoded.length; i++) {
173
166
  const message = decodeJsonRpcMessage(decoded[i])
167
+ messages.push(message)
174
168
  if (message._tag === "Request") {
175
169
  batch.size++
176
170
  batches.set(message.id, batch)
@@ -263,6 +257,47 @@ function encodeJsonRpcRaw(
263
257
  return encodeJsonRpcMessage(response)
264
258
  }
265
259
 
260
+ function encodeJsonRpcResponse(
261
+ response:
262
+ | RpcMessage.FromServerEncoded
263
+ | RpcMessage.FromClientEncoded
264
+ | Array<RpcMessage.FromServerEncoded | RpcMessage.FromClientEncoded>,
265
+ batches: Map<string, {
266
+ readonly size: number
267
+ readonly responses: Map<string, RpcMessage.FromServerEncoded>
268
+ }>
269
+ ) {
270
+ if (Array.isArray(response) === false) {
271
+ return encodeJsonRpcRaw(response, batches)
272
+ }
273
+ if (response.length === 0) {
274
+ return undefined
275
+ }
276
+ const encoded: Array<JsonRpcMessage | Array<JsonRpcMessage>> = []
277
+ for (let i = 0; i < response.length; i++) {
278
+ const current = encodeJsonRpcRaw(response[i], batches)
279
+ if (current !== undefined) {
280
+ encoded.push(current)
281
+ }
282
+ }
283
+ if (encoded.length === 0) {
284
+ return undefined
285
+ }
286
+ if (encoded.length === 1) {
287
+ return encoded[0]
288
+ }
289
+ const messages: Array<JsonRpcMessage> = []
290
+ for (let i = 0; i < encoded.length; i++) {
291
+ const current = encoded[i]
292
+ if (Array.isArray(current)) {
293
+ messages.push(...current)
294
+ } else {
295
+ messages.push(current)
296
+ }
297
+ }
298
+ return messages
299
+ }
300
+
266
301
  function encodeJsonRpcMessage(response: RpcMessage.FromServerEncoded | RpcMessage.FromClientEncoded): JsonRpcMessage {
267
302
  switch (response._tag) {
268
303
  case "Request":
@@ -175,17 +175,16 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any>(
175
175
  }
176
176
  case "Interrupt": {
177
177
  const fiber = client.fibers.get(message.requestId)
178
- return fiber
179
- ? Effect.forkDetach(
180
- Fiber.interruptAs(fiber, fiberIdClientInterrupt),
181
- { startImmediately: true }
182
- )
183
- : options.onFromServer({
184
- _tag: "Exit",
185
- clientId,
186
- requestId: message.requestId,
187
- exit: Exit.interrupt()
188
- })
178
+ if (fiber) {
179
+ fiber.interruptUnsafe(requestFiber.id, RpcSchema.ClientAbort.annotation)
180
+ return Effect.void
181
+ }
182
+ return options.onFromServer({
183
+ _tag: "Exit",
184
+ clientId,
185
+ requestId: message.requestId,
186
+ exit: Exit.interrupt()
187
+ })
189
188
  }
190
189
  case "Eof": {
191
190
  client.ended = true
@@ -1282,14 +1281,6 @@ export const layerProtocolWorkerRunner: Layer.Layer<
1282
1281
  WorkerRunner.WorkerRunnerPlatform
1283
1282
  > = Layer.effect(Protocol)(makeProtocolWorkerRunner)
1284
1283
 
1285
- /**
1286
- * Fiber id used to indicate client induced interrupts
1287
- *
1288
- * @since 4.0.0
1289
- * @category Interruption
1290
- */
1291
- export const fiberIdClientInterrupt = -499
1292
-
1293
1284
  // internal
1294
1285
 
1295
1286
  const makeSocketProtocol: Effect.Effect<