effect 4.0.0-beta.104 → 4.0.0-beta.106

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 (300) hide show
  1. package/AGENTS.md +381 -0
  2. package/CLAUDE.md +381 -0
  3. package/ai-docs/README.md +44 -0
  4. package/ai-docs/package.json +36 -0
  5. package/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
  6. package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +39 -0
  7. package/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
  8. package/ai-docs/src/01_effect/01_basics/index.md +5 -0
  9. package/ai-docs/src/01_effect/02_schema/10_schema-basics.ts +43 -0
  10. package/ai-docs/src/01_effect/02_schema/index.md +7 -0
  11. package/ai-docs/src/01_effect/03_services/01_service.ts +45 -0
  12. package/ai-docs/src/01_effect/03_services/10_reference.ts +10 -0
  13. package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +70 -0
  14. package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +66 -0
  15. package/ai-docs/src/01_effect/03_services/index.md +5 -0
  16. package/ai-docs/src/01_effect/04_errors/01_error-handling.ts +30 -0
  17. package/ai-docs/src/01_effect/04_errors/10_catch-tags.ts +24 -0
  18. package/ai-docs/src/01_effect/04_errors/20_reason-errors.ts +64 -0
  19. package/ai-docs/src/01_effect/04_errors/index.md +1 -0
  20. package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +105 -0
  21. package/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts +31 -0
  22. package/ai-docs/src/01_effect/05_resources/30_layer-map.ts +86 -0
  23. package/ai-docs/src/01_effect/05_resources/index.md +3 -0
  24. package/ai-docs/src/01_effect/06_running/10_run-main.ts +30 -0
  25. package/ai-docs/src/01_effect/06_running/20_layer-launch.ts +27 -0
  26. package/ai-docs/src/01_effect/06_running/index.md +1 -0
  27. package/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts +56 -0
  28. package/ai-docs/src/01_effect/07_pubsub/index.md +3 -0
  29. package/ai-docs/src/03_stream/10_creating-streams.ts +103 -0
  30. package/ai-docs/src/03_stream/20_consuming-streams.ts +137 -0
  31. package/ai-docs/src/03_stream/30_encoding.ts +165 -0
  32. package/ai-docs/src/03_stream/index.md +4 -0
  33. package/ai-docs/src/04_integration/10_managed-runtime.ts +129 -0
  34. package/ai-docs/src/04_integration/index.md +5 -0
  35. package/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
  36. package/ai-docs/src/05_batching/index.md +3 -0
  37. package/ai-docs/src/06_schedule/10_schedules.ts +110 -0
  38. package/ai-docs/src/06_schedule/index.md +3 -0
  39. package/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
  40. package/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
  41. package/ai-docs/src/07_datetime/index.md +5 -0
  42. package/ai-docs/src/08_observability/10_logging.ts +66 -0
  43. package/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
  44. package/ai-docs/src/08_observability/index.md +7 -0
  45. package/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
  46. package/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
  47. package/ai-docs/src/09_testing/index.md +1 -0
  48. package/ai-docs/src/10_predicate/01_basics.ts +14 -0
  49. package/ai-docs/src/10_predicate/index.md +9 -0
  50. package/ai-docs/src/50_http-client/10_basics.ts +102 -0
  51. package/ai-docs/src/50_http-client/index.md +3 -0
  52. package/ai-docs/src/51_http-server/10_basics.ts +116 -0
  53. package/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
  54. package/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
  55. package/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
  56. package/ai-docs/src/51_http-server/fixtures/api/Users.ts +91 -0
  57. package/ai-docs/src/51_http-server/fixtures/domain/User.ts +12 -0
  58. package/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
  59. package/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +36 -0
  60. package/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +71 -0
  61. package/ai-docs/src/51_http-server/fixtures/server/Users.ts +62 -0
  62. package/ai-docs/src/51_http-server/index.md +3 -0
  63. package/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
  64. package/ai-docs/src/60_child-process/index.md +3 -0
  65. package/ai-docs/src/70_cli/10_basics.ts +136 -0
  66. package/ai-docs/src/70_cli/index.md +5 -0
  67. package/ai-docs/src/71_ai/10_language-model.ts +156 -0
  68. package/ai-docs/src/71_ai/20_tools.ts +226 -0
  69. package/ai-docs/src/71_ai/30_chat.ts +158 -0
  70. package/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
  71. package/ai-docs/src/71_ai/index.md +5 -0
  72. package/ai-docs/src/80_cluster/10_entities.ts +97 -0
  73. package/ai-docs/src/80_cluster/index.md +4 -0
  74. package/ai-docs/src/index.md +10 -0
  75. package/ai-docs/tsconfig.json +24 -0
  76. package/dist/Brand.d.ts +3 -3
  77. package/dist/Brand.d.ts.map +1 -1
  78. package/dist/Brand.js +4 -3
  79. package/dist/Brand.js.map +1 -1
  80. package/dist/Config.d.ts.map +1 -1
  81. package/dist/Config.js +5 -4
  82. package/dist/Config.js.map +1 -1
  83. package/dist/ConfigProvider.d.ts +35 -3
  84. package/dist/ConfigProvider.d.ts.map +1 -1
  85. package/dist/ConfigProvider.js +41 -8
  86. package/dist/ConfigProvider.js.map +1 -1
  87. package/dist/Context.d.ts +0 -1
  88. package/dist/Context.d.ts.map +1 -1
  89. package/dist/Context.js +1 -22
  90. package/dist/Context.js.map +1 -1
  91. package/dist/Cron.d.ts +31 -0
  92. package/dist/Cron.d.ts.map +1 -1
  93. package/dist/Cron.js +58 -2
  94. package/dist/Cron.js.map +1 -1
  95. package/dist/Fiber.d.ts +1 -1
  96. package/dist/Fiber.d.ts.map +1 -1
  97. package/dist/Function.d.ts +26 -0
  98. package/dist/Function.d.ts.map +1 -1
  99. package/dist/Function.js +36 -0
  100. package/dist/Function.js.map +1 -1
  101. package/dist/JsonSchema.d.ts.map +1 -1
  102. package/dist/JsonSchema.js +34 -17
  103. package/dist/JsonSchema.js.map +1 -1
  104. package/dist/Optic.d.ts +35 -21
  105. package/dist/Optic.d.ts.map +1 -1
  106. package/dist/Optic.js +33 -34
  107. package/dist/Optic.js.map +1 -1
  108. package/dist/Schema.d.ts +105 -109
  109. package/dist/Schema.d.ts.map +1 -1
  110. package/dist/Schema.js +141 -104
  111. package/dist/Schema.js.map +1 -1
  112. package/dist/SchemaAST.d.ts +32 -17
  113. package/dist/SchemaAST.d.ts.map +1 -1
  114. package/dist/SchemaAST.js +79 -60
  115. package/dist/SchemaAST.js.map +1 -1
  116. package/dist/SchemaError.d.ts +8 -8
  117. package/dist/SchemaError.d.ts.map +1 -1
  118. package/dist/SchemaError.js +7 -6
  119. package/dist/SchemaError.js.map +1 -1
  120. package/dist/SchemaGetter.d.ts +8 -5
  121. package/dist/SchemaGetter.d.ts.map +1 -1
  122. package/dist/SchemaGetter.js +41 -37
  123. package/dist/SchemaGetter.js.map +1 -1
  124. package/dist/SchemaIssue.d.ts +195 -38
  125. package/dist/SchemaIssue.d.ts.map +1 -1
  126. package/dist/SchemaIssue.js +243 -68
  127. package/dist/SchemaIssue.js.map +1 -1
  128. package/dist/SchemaParser.d.ts +30 -0
  129. package/dist/SchemaParser.d.ts.map +1 -1
  130. package/dist/SchemaParser.js +39 -9
  131. package/dist/SchemaParser.js.map +1 -1
  132. package/dist/SchemaRepresentation.d.ts +15 -3
  133. package/dist/SchemaRepresentation.d.ts.map +1 -1
  134. package/dist/SchemaRepresentation.js +8 -2
  135. package/dist/SchemaRepresentation.js.map +1 -1
  136. package/dist/SchemaTransformation.d.ts +2 -2
  137. package/dist/SchemaTransformation.d.ts.map +1 -1
  138. package/dist/SchemaTransformation.js +26 -26
  139. package/dist/SchemaTransformation.js.map +1 -1
  140. package/dist/ScopedRef.d.ts.map +1 -1
  141. package/dist/ScopedRef.js +1 -1
  142. package/dist/ScopedRef.js.map +1 -1
  143. package/dist/Stdio.d.ts +17 -4
  144. package/dist/Stdio.d.ts.map +1 -1
  145. package/dist/Stdio.js +6 -3
  146. package/dist/Stdio.js.map +1 -1
  147. package/dist/TxQueue.js +1 -1
  148. package/dist/TxQueue.js.map +1 -1
  149. package/dist/internal/effect.js +2 -4
  150. package/dist/internal/effect.js.map +1 -1
  151. package/dist/internal/executionPlan.js +3 -2
  152. package/dist/internal/executionPlan.js.map +1 -1
  153. package/dist/internal/rcRef.js +18 -13
  154. package/dist/internal/rcRef.js.map +1 -1
  155. package/dist/internal/schema/fromJsonSchemaDocument.js +10 -6
  156. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  157. package/dist/internal/schema/schema.js +1 -9
  158. package/dist/internal/schema/schema.js.map +1 -1
  159. package/dist/internal/schema/toArbitrary.d.ts +1 -4
  160. package/dist/internal/schema/toArbitrary.d.ts.map +1 -1
  161. package/dist/internal/schema/toArbitrary.js +0 -74
  162. package/dist/internal/schema/toArbitrary.js.map +1 -1
  163. package/dist/internal/schema/toJsonSchemaDocument.js +18 -4
  164. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  165. package/dist/internal/schema/toRepresentation.js +45 -43
  166. package/dist/internal/schema/toRepresentation.js.map +1 -1
  167. package/dist/testing/TestSchema.d.ts +1 -1
  168. package/dist/testing/TestSchema.d.ts.map +1 -1
  169. package/dist/testing/TestSchema.js +10 -9
  170. package/dist/testing/TestSchema.js.map +1 -1
  171. package/dist/unstable/ai/McpSchema.d.ts +110 -110
  172. package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
  173. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  174. package/dist/unstable/ai/McpServer.js +23 -11
  175. package/dist/unstable/ai/McpServer.js.map +1 -1
  176. package/dist/unstable/ai/Prompt.d.ts +4 -3
  177. package/dist/unstable/ai/Prompt.d.ts.map +1 -1
  178. package/dist/unstable/ai/Prompt.js +42 -17
  179. package/dist/unstable/ai/Prompt.js.map +1 -1
  180. package/dist/unstable/cluster/Entity.d.ts +2 -2
  181. package/dist/unstable/cluster/Entity.d.ts.map +1 -1
  182. package/dist/unstable/cluster/Entity.js.map +1 -1
  183. package/dist/unstable/cluster/EntityProxy.d.ts +3 -3
  184. package/dist/unstable/cluster/EntityProxy.d.ts.map +1 -1
  185. package/dist/unstable/cluster/EntityProxy.js +4 -3
  186. package/dist/unstable/cluster/EntityProxy.js.map +1 -1
  187. package/dist/unstable/cluster/Reply.js +4 -4
  188. package/dist/unstable/cluster/Reply.js.map +1 -1
  189. package/dist/unstable/cluster/Sharding.d.ts +2 -2
  190. package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
  191. package/dist/unstable/cluster/Sharding.js +14 -5
  192. package/dist/unstable/cluster/Sharding.js.map +1 -1
  193. package/dist/unstable/encoding/Msgpack.d.ts.map +1 -1
  194. package/dist/unstable/encoding/Msgpack.js +6 -6
  195. package/dist/unstable/encoding/Msgpack.js.map +1 -1
  196. package/dist/unstable/eventlog/EventLogEncryption.d.ts +6 -5
  197. package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
  198. package/dist/unstable/eventlog/EventLogEncryption.js +14 -12
  199. package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
  200. package/dist/unstable/eventlog/EventLogMessage.d.ts +3 -3
  201. package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
  202. package/dist/unstable/eventlog/EventLogMessage.js +2 -3
  203. package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
  204. package/dist/unstable/eventlog/EventLogRemote.js +4 -4
  205. package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
  206. package/dist/unstable/eventlog/EventLogServerEncrypted.js +3 -2
  207. package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -1
  208. package/dist/unstable/http/HttpClient.d.ts +59 -3
  209. package/dist/unstable/http/HttpClient.d.ts.map +1 -1
  210. package/dist/unstable/http/HttpClient.js +33 -19
  211. package/dist/unstable/http/HttpClient.js.map +1 -1
  212. package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
  213. package/dist/unstable/http/HttpServerRequest.js +7 -1
  214. package/dist/unstable/http/HttpServerRequest.js.map +1 -1
  215. package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
  216. package/dist/unstable/http/MultipartParser/internal/multipart.js +18 -6
  217. package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
  218. package/dist/unstable/httpapi/HttpApiBuilder.js +10 -10
  219. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  220. package/dist/unstable/httpapi/HttpApiClient.js +9 -9
  221. package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
  222. package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
  223. package/dist/unstable/httpapi/OpenApi.js +1 -1
  224. package/dist/unstable/httpapi/OpenApi.js.map +1 -1
  225. package/dist/unstable/reactivity/AsyncResult.d.ts.map +1 -1
  226. package/dist/unstable/reactivity/AsyncResult.js +4 -4
  227. package/dist/unstable/reactivity/AsyncResult.js.map +1 -1
  228. package/dist/unstable/reactivity/Atom.d.ts +26 -11
  229. package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
  230. package/dist/unstable/reactivity/Atom.js +9 -22
  231. package/dist/unstable/reactivity/Atom.js.map +1 -1
  232. package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
  233. package/dist/unstable/rpc/RpcClient.js +8 -1
  234. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  235. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  236. package/dist/unstable/rpc/RpcServer.js +3 -2
  237. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  238. package/dist/unstable/sql/SqlResolver.d.ts.map +1 -1
  239. package/dist/unstable/sql/SqlResolver.js +20 -16
  240. package/dist/unstable/sql/SqlResolver.js.map +1 -1
  241. package/dist/unstable/workers/Worker.d.ts.map +1 -1
  242. package/dist/unstable/workers/Worker.js +11 -12
  243. package/dist/unstable/workers/Worker.js.map +1 -1
  244. package/dist/unstable/workflow/Workflow.d.ts.map +1 -1
  245. package/dist/unstable/workflow/Workflow.js +2 -2
  246. package/dist/unstable/workflow/Workflow.js.map +1 -1
  247. package/package.json +5 -2
  248. package/src/Brand.ts +4 -4
  249. package/src/Config.ts +6 -4
  250. package/src/ConfigProvider.ts +43 -8
  251. package/src/Context.ts +1 -23
  252. package/src/Cron.ts +65 -2
  253. package/src/Fiber.ts +1 -1
  254. package/src/Function.ts +37 -0
  255. package/src/JsonSchema.ts +43 -42
  256. package/src/Optic.ts +59 -58
  257. package/src/Schema.ts +213 -217
  258. package/src/SchemaAST.ts +135 -78
  259. package/src/SchemaError.ts +9 -9
  260. package/src/SchemaGetter.ts +89 -30
  261. package/src/SchemaIssue.ts +285 -80
  262. package/src/SchemaParser.ts +51 -17
  263. package/src/SchemaRepresentation.ts +15 -3
  264. package/src/SchemaTransformation.ts +66 -18
  265. package/src/ScopedRef.ts +3 -1
  266. package/src/Stdio.ts +23 -4
  267. package/src/TxQueue.ts +1 -1
  268. package/src/internal/effect.ts +3 -6
  269. package/src/internal/executionPlan.ts +3 -2
  270. package/src/internal/rcRef.ts +24 -16
  271. package/src/internal/schema/fromJsonSchemaDocument.ts +19 -14
  272. package/src/internal/schema/schema.ts +1 -17
  273. package/src/internal/schema/toArbitrary.ts +0 -72
  274. package/src/internal/schema/toJsonSchemaDocument.ts +24 -4
  275. package/src/internal/schema/toRepresentation.ts +66 -48
  276. package/src/testing/TestSchema.ts +10 -10
  277. package/src/unstable/ai/McpServer.ts +23 -11
  278. package/src/unstable/ai/Prompt.ts +51 -19
  279. package/src/unstable/cluster/Entity.ts +7 -2
  280. package/src/unstable/cluster/EntityProxy.ts +12 -5
  281. package/src/unstable/cluster/Reply.ts +4 -4
  282. package/src/unstable/cluster/Sharding.ts +29 -17
  283. package/src/unstable/encoding/Msgpack.ts +12 -8
  284. package/src/unstable/eventlog/EventLogEncryption.ts +15 -16
  285. package/src/unstable/eventlog/EventLogMessage.ts +2 -3
  286. package/src/unstable/eventlog/EventLogRemote.ts +4 -4
  287. package/src/unstable/eventlog/EventLogServerEncrypted.ts +2 -2
  288. package/src/unstable/http/HttpClient.ts +132 -23
  289. package/src/unstable/http/HttpServerRequest.ts +8 -3
  290. package/src/unstable/http/MultipartParser/internal/multipart.ts +19 -6
  291. package/src/unstable/httpapi/HttpApiBuilder.ts +32 -13
  292. package/src/unstable/httpapi/HttpApiClient.ts +26 -8
  293. package/src/unstable/httpapi/OpenApi.ts +4 -1
  294. package/src/unstable/reactivity/AsyncResult.ts +11 -6
  295. package/src/unstable/reactivity/Atom.ts +44 -25
  296. package/src/unstable/rpc/RpcClient.ts +9 -1
  297. package/src/unstable/rpc/RpcServer.ts +4 -2
  298. package/src/unstable/sql/SqlResolver.ts +28 -20
  299. package/src/unstable/workers/Worker.ts +13 -14
  300. package/src/unstable/workflow/Workflow.ts +2 -5
@@ -14,7 +14,7 @@ import * as HttpApiEndpoint from "../httpapi/HttpApiEndpoint.ts"
14
14
  import * as HttpApiGroup from "../httpapi/HttpApiGroup.ts"
15
15
  import * as Rpc from "../rpc/Rpc.ts"
16
16
  import * as RpcGroup from "../rpc/RpcGroup.ts"
17
- import { AlreadyProcessingMessage, MailboxFull, PersistenceError } from "./ClusterError.ts"
17
+ import { AlreadyProcessingMessage, EntityNotAssignedToRunner, MailboxFull, PersistenceError } from "./ClusterError.ts"
18
18
  import type * as Entity from "./Entity.ts"
19
19
  import type { EntityId } from "./EntityId.ts"
20
20
 
@@ -24,6 +24,11 @@ const clientErrors = [
24
24
  PersistenceError
25
25
  ] as const
26
26
 
27
+ const requestErrors = [
28
+ ...clientErrors,
29
+ EntityNotAssignedToRunner
30
+ ] as const
31
+
27
32
  /**
28
33
  * Derives an `RpcGroup` from an `Entity`.
29
34
  *
@@ -75,7 +80,7 @@ export const toRpcGroup = <Type extends string, Rpcs extends Rpc.Any>(
75
80
  }
76
81
  const rpc = Rpc.make(`${entity.type}.${parentRpc._tag}`, {
77
82
  payload: payloadSchema,
78
- error: Schema.Union([parentRpc.errorSchema, ...clientErrors]),
83
+ error: Schema.Union([parentRpc.errorSchema, ...requestErrors]),
79
84
  success: parentRpc.successSchema
80
85
  }).annotateMerge(parentRpc.annotations)
81
86
  const rpcDiscard = Rpc.make(`${entity.type}.${parentRpc._tag}Discard`, {
@@ -115,11 +120,12 @@ export type ConvertRpcs<Rpcs extends Rpc.Any, Prefix extends string> = Rpcs exte
115
120
  }>,
116
121
  _Success,
117
122
  Schema.Codec<
118
- _Error["Type"] | MailboxFull | AlreadyProcessingMessage | PersistenceError,
123
+ _Error["Type"] | MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner,
119
124
  | _Error["Encoded"]
120
125
  | typeof MailboxFull["Encoded"]
121
126
  | typeof AlreadyProcessingMessage["Encoded"]
122
- | typeof PersistenceError["Encoded"],
127
+ | typeof PersistenceError["Encoded"]
128
+ | typeof EntityNotAssignedToRunner["Encoded"],
123
129
  _Error["DecodingServices"],
124
130
  _Error["EncodingServices"]
125
131
  >
@@ -193,7 +199,7 @@ export const toHttpApiGroup = <const Name extends string, Type extends string, R
193
199
  params: entityIdPath,
194
200
  payload: parentRpc.payloadSchema,
195
201
  success: parentRpc.successSchema,
196
- error: [parentRpc.errorSchema, ...clientErrors]
202
+ error: [parentRpc.errorSchema, ...requestErrors]
197
203
  }).annotateMerge(parentRpc.annotations)
198
204
  const endpointDiscard = HttpApiEndpoint.post(
199
205
  `${parentRpc._tag}Discard`,
@@ -250,6 +256,7 @@ export type ConvertHttpApi<Rpcs extends Rpc.Any> = Rpcs extends Rpc.Rpc<
250
256
  | typeof MailboxFull
251
257
  | typeof AlreadyProcessingMessage
252
258
  | typeof PersistenceError
259
+ | typeof EntityNotAssignedToRunner
253
260
  >
254
261
  | HttpApiEndpoint.HttpApiEndpoint<
255
262
  `${_Tag}Discard`,
@@ -242,10 +242,10 @@ export class Chunk<R extends Rpc.Any> extends Data.TaggedClass("Chunk")<{
242
242
  [success],
243
243
  ([success]) => (input, ast, options) => {
244
244
  if (!isReply(input) || input._tag !== "Chunk") {
245
- return Effect.fail(new SchemaIssue.InvalidType(ast))
245
+ return Effect.fail(new SchemaIssue.InvalidType(ast, input, options))
246
246
  }
247
247
  return Effect.mapBothEager(SchemaParser.decodeEffect(Schema.NonEmptyArray(success))(input.values, options), {
248
- onFailure: (issue) => new SchemaIssue.Composite(ast, [new SchemaIssue.Pointer(["values"], issue)]),
248
+ onFailure: (issue) => SchemaIssue.makeCompositeAtKey(ast, "values", issue, input, options),
249
249
  onSuccess: (values) => new Chunk({ ...input, values } as any)
250
250
  })
251
251
  },
@@ -351,10 +351,10 @@ export class WithExit<R extends Rpc.Any> extends Data.TaggedClass("WithExit")<{
351
351
  [exitSchema],
352
352
  ([exit]) => (input, ast, options) => {
353
353
  if (!isReply(input) || input._tag !== "WithExit") {
354
- return Effect.fail(new SchemaIssue.InvalidType(ast))
354
+ return Effect.fail(new SchemaIssue.InvalidType(ast, input, options))
355
355
  }
356
356
  return Effect.mapBothEager(SchemaParser.decodeEffect(exit)(input.exit, options), {
357
- onFailure: (issue) => new SchemaIssue.Composite(ast, [new SchemaIssue.Pointer(["exit"], issue)]),
357
+ onFailure: (issue) => SchemaIssue.makeCompositeAtKey(ast, "exit", issue, input, options),
358
358
  onSuccess: (exit) => new WithExit({ ...input, exit: exit as any })
359
359
  })
360
360
  },
@@ -124,7 +124,7 @@ export class Sharding extends Context.Service<Sharding, {
124
124
  entityId: string
125
125
  ) => RpcClient.RpcClient.From<
126
126
  Rpcs,
127
- MailboxFull | AlreadyProcessingMessage | PersistenceError
127
+ MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner
128
128
  >
129
129
  >
130
130
 
@@ -178,7 +178,7 @@ export class Sharding extends Context.Service<Sharding, {
178
178
  discard: boolean
179
179
  ) => Effect.Effect<
180
180
  void,
181
- MailboxFull | AlreadyProcessingMessage | PersistenceError
181
+ MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner
182
182
  >
183
183
 
184
184
  /**
@@ -967,18 +967,40 @@ const make = Effect.gen(function*() {
967
967
  retries?: number
968
968
  ): Effect.Effect<
969
969
  void,
970
- MailboxFull | AlreadyProcessingMessage | PersistenceError
970
+ MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner
971
971
  > {
972
972
  const isPersisted = Context.get(
973
973
  message._tag === "OutgoingRequest" ? message.annotations : message.rpc.annotations,
974
974
  Persisted
975
975
  )
976
+ const shouldFail = !discard &&
977
+ (message._tag === "OutgoingRequest" || message.envelope._tag === "AckChunk")
978
+ const abandon = (error: EntityNotAssignedToRunner) => {
979
+ if (!isPersisted) {
980
+ return shouldFail
981
+ ? Effect.fail(error)
982
+ : Effect.logDebug("Abandoning outgoing message during shutdown", message.envelope.address)
983
+ }
984
+ const persist = message._tag === "OutgoingRequest"
985
+ ? storage.saveRequest(message)
986
+ : storage.saveEnvelope(message)
987
+ return Effect.catchTag(persist, "MalformedMessage", Effect.die).pipe(
988
+ Effect.andThen(
989
+ shouldFail
990
+ ? Effect.fail(error)
991
+ : Effect.logWarning("Persisting outgoing message abandoned during shutdown", message.envelope.address)
992
+ )
993
+ )
994
+ }
976
995
  return Effect.catchFilter(
977
996
  Effect.suspend(() => {
978
997
  const address = message.envelope.address
979
998
  if (isPersisted && !storageEnabled) {
980
999
  return Effect.die("Sharding.sendOutgoing: Persisted messages require MessageStorage")
981
1000
  }
1001
+ if (shouldFail && MutableRef.get(isShutdown)) {
1002
+ return Effect.fail(new EntityNotAssignedToRunner({ address }))
1003
+ }
982
1004
  const maybeRunner = MutableHashMap.get(shardAssignments, address.shardId)
983
1005
  const runnerIsLocal = Option.isSome(maybeRunner) && isLocalRunner(maybeRunner.value)
984
1006
  if (isPersisted) {
@@ -1003,17 +1025,7 @@ const make = Effect.gen(function*() {
1003
1025
  const cannotRecover = MutableRef.get(isShutdown) ||
1004
1026
  (targetManager !== undefined && targetManager.status !== "alive")
1005
1027
  if (cannotRecover) {
1006
- if (!isPersisted) {
1007
- return Effect.logDebug("Abandoning outgoing message during shutdown", message.envelope.address)
1008
- }
1009
- const persist = message._tag === "OutgoingRequest"
1010
- ? storage.saveRequest(message)
1011
- : storage.saveEnvelope(message)
1012
- return Effect.catchTag(persist, "MalformedMessage", Effect.die).pipe(
1013
- Effect.andThen(
1014
- Effect.logWarning("Persisting outgoing message abandoned during shutdown", message.envelope.address)
1015
- )
1016
- )
1028
+ return abandon(error)
1017
1029
  }
1018
1030
  }
1019
1031
  if (retries === 0) {
@@ -1183,7 +1195,7 @@ const make = Effect.gen(function*() {
1183
1195
  Entity<any, any>,
1184
1196
  (entityId: string) => RpcClient.RpcClient<
1185
1197
  any,
1186
- MailboxFull | AlreadyProcessingMessage
1198
+ MailboxFull | AlreadyProcessingMessage | EntityNotAssignedToRunner
1187
1199
  >,
1188
1200
  never
1189
1201
  > = yield* ResourceMap.make(
@@ -1196,7 +1208,7 @@ const make = Effect.gen(function*() {
1196
1208
  flatten: true,
1197
1209
  onFromClient(options): Effect.Effect<
1198
1210
  void,
1199
- MailboxFull | AlreadyProcessingMessage | PersistenceError
1211
+ MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner
1200
1212
  > {
1201
1213
  const address = Context.getUnsafe(options.context, ClientAddressTag)
1202
1214
  switch (options.message._tag) {
@@ -1337,7 +1349,7 @@ const make = Effect.gen(function*() {
1337
1349
  const makeClient = <Type extends string, Rpcs extends Rpc.Any>(entity: Entity<Type, Rpcs>): Effect.Effect<
1338
1350
  (
1339
1351
  entityId: string
1340
- ) => RpcClient.RpcClient.From<Rpcs, MailboxFull | AlreadyProcessingMessage>
1352
+ ) => RpcClient.RpcClient.From<Rpcs, MailboxFull | AlreadyProcessingMessage | EntityNotAssignedToRunner>
1341
1353
  > => clients.get(entity) as any
1342
1354
 
1343
1355
  const clientRespondDiscard = (_reply: Reply.Reply<any>) => Effect.void
@@ -387,25 +387,29 @@ export const transformation: SchemaTransformation.Transformation<
387
387
  unknown,
388
388
  Uint8Array<ArrayBuffer>
389
389
  > = SchemaTransformation.transformOrFail({
390
- decode(e, _options) {
390
+ decode(e, options) {
391
391
  try {
392
392
  return Effect.succeed(Msgpackr.decode(e))
393
393
  } catch {
394
394
  return Effect.fail(
395
- new SchemaIssue.InvalidValue({
396
- message: "Expected valid MessagePack bytes"
397
- })
395
+ new SchemaIssue.InvalidValue(
396
+ { expected: "valid MessagePack bytes" },
397
+ e,
398
+ options
399
+ )
398
400
  )
399
401
  }
400
402
  },
401
- encode(t, _options) {
403
+ encode(t, options) {
402
404
  try {
403
405
  return Effect.succeed(Msgpackr.encode(t) as Uint8Array<ArrayBuffer>)
404
406
  } catch {
405
407
  return Effect.fail(
406
- new SchemaIssue.InvalidValue({
407
- message: "Expected a MessagePack-serializable value"
408
- })
408
+ new SchemaIssue.InvalidValue(
409
+ { expected: "a MessagePack-serializable value" },
410
+ t,
411
+ options
412
+ )
409
413
  )
410
414
  }
411
415
  }
@@ -19,14 +19,15 @@ import type { Identity } from "./EventLog.ts"
19
19
  import { makeGetIdentityRootSecretMaterial } from "./internal/identityRootSecretDerivation.ts"
20
20
 
21
21
  /**
22
- * Schema for an encrypted journal entry paired with the id of the original
23
- * entry.
22
+ * Schema for an encrypted journal entry paired with its initialization vector
23
+ * and the id of the original entry.
24
24
  *
25
25
  * @category models
26
26
  * @since 4.0.0
27
27
  */
28
28
  export const EncryptedEntry = Schema.Struct({
29
29
  entryId: EntryId,
30
+ iv: Transferable.Uint8Array,
30
31
  encryptedEntry: Transferable.Uint8Array
31
32
  })
32
33
 
@@ -76,10 +77,12 @@ export class EventLogEncryption extends Context.Service<EventLogEncryption, {
76
77
  readonly encrypt: (
77
78
  identity: Identity["Service"],
78
79
  entries: ReadonlyArray<Entry>
79
- ) => Effect.Effect<{
80
- readonly iv: Uint8Array<ArrayBuffer>
81
- readonly encryptedEntries: ReadonlyArray<Uint8Array<ArrayBuffer>>
82
- }>
80
+ ) => Effect.Effect<
81
+ ReadonlyArray<{
82
+ readonly iv: Uint8Array<ArrayBuffer>
83
+ readonly encryptedEntry: Uint8Array<ArrayBuffer>
84
+ }>
85
+ >
83
86
  readonly decrypt: (
84
87
  identity: Identity["Service"],
85
88
  entries: ReadonlyArray<EncryptedRemoteEntry>
@@ -104,22 +107,18 @@ export const makeEncryptionSubtle = (crypto: Crypto): Effect.Effect<EventLogEncr
104
107
  encrypt: Effect.fnUntraced(function*(identity, entries) {
105
108
  const data = yield* Effect.orDie(Entry.encodeArray(entries))
106
109
  const key = (yield* getIdentityRootSecretMaterial(identity)).encryptionKey
107
- const iv = crypto.getRandomValues(new Uint8Array(12))
108
- const encryptedEntries = yield* Effect.promise(() =>
110
+ return yield* Effect.promise(() =>
109
111
  Promise.all(
110
- data.map((entry) =>
111
- crypto.subtle.encrypt(
112
+ data.map((entry) => {
113
+ const iv = crypto.getRandomValues(new Uint8Array(12))
114
+ return crypto.subtle.encrypt(
112
115
  { name: "AES-GCM", iv: toBufferSource(iv), tagLength: 128 },
113
116
  key,
114
117
  toBufferSource(entry)
115
- )
116
- )
118
+ ).then((encryptedEntry) => ({ iv, encryptedEntry: new Uint8Array(encryptedEntry) }))
119
+ })
117
120
  )
118
121
  )
119
- return {
120
- iv,
121
- encryptedEntries: encryptedEntries.map((entry) => new Uint8Array(entry))
122
- }
123
122
  }),
124
123
  decrypt: Effect.fnUntraced(function*(identity, entries) {
125
124
  const key = (yield* getIdentityRootSecretMaterial(identity)).encryptionKey
@@ -261,8 +261,8 @@ export class WriteChunkedRpc extends Rpc.make("EventLog.WriteChunked", {
261
261
  *
262
262
  * **Details**
263
263
  *
264
- * It includes the client public key, target store id, AES-GCM initialization
265
- * vector, and encrypted entries.
264
+ * It includes the client public key, target store id, and encrypted entries
265
+ * with their AES-GCM initialization vectors.
266
266
  *
267
267
  * @category protocols
268
268
  * @since 4.0.0
@@ -270,7 +270,6 @@ export class WriteChunkedRpc extends Rpc.make("EventLog.WriteChunked", {
270
270
  export class WriteEntries extends Schema.Class<WriteEntries>("effect/eventlog/EventLogRemote/WriteEntries")({
271
271
  publicKey: Schema.String,
272
272
  storeId: StoreId,
273
- iv: Transferable.Uint8Array,
274
273
  encryptedEntries: Schema.Array(EncryptedEntry)
275
274
  }) {
276
275
  static FromMsgpack = Msgpack.schema(WriteEntries)
@@ -307,14 +307,14 @@ export const makeEncrypted = Effect.gen(function*(): Effect.fn.Return<
307
307
  return yield* makeWith({
308
308
  encodeWrite: (options) =>
309
309
  encryption.encrypt(options.identity, options.entries).pipe(
310
- Effect.flatMap((msg) =>
310
+ Effect.flatMap((encryptedEntries) =>
311
311
  new WriteEntries({
312
312
  publicKey: options.identity.publicKey,
313
313
  storeId: options.storeId,
314
- iv: msg.iv,
315
- encryptedEntries: msg.encryptedEntries.map((entry, i) => ({
314
+ encryptedEntries: encryptedEntries.map((entry, i) => ({
316
315
  entryId: options.entries[i].id,
317
- encryptedEntry: entry
316
+ iv: entry.iv,
317
+ encryptedEntry: entry.encryptedEntry
318
318
  }))
319
319
  }).encoded
320
320
  )
@@ -67,10 +67,10 @@ export const layerRpcHandlers = Layer.unwrap(Effect.gen(function*() {
67
67
  })
68
68
  }
69
69
  if (request.encryptedEntries.length === 0) return
70
- const entries = request.encryptedEntries.map(({ encryptedEntry, entryId }) =>
70
+ const entries = request.encryptedEntries.map(({ encryptedEntry, entryId, iv }) =>
71
71
  new PersistedEntry({
72
72
  entryId,
73
- iv: request.iv,
73
+ iv,
74
74
  encryptedEntry
75
75
  })
76
76
  )
@@ -1267,7 +1267,8 @@ export declare namespace WithRateLimiter {
1267
1267
  *
1268
1268
  * **Details**
1269
1269
  *
1270
- * They define the backing limiter, initial limit window, keying strategy, algorithm, token cost, and whether response headers update future limits.
1270
+ * They define the backing limiter, initial limit window, keying strategy,
1271
+ * algorithm, token cost, retry count, and response header inspection.
1271
1272
  *
1272
1273
  * @category rate limiting
1273
1274
  * @since 4.0.0
@@ -1300,11 +1301,48 @@ export declare namespace WithRateLimiter {
1300
1301
  */
1301
1302
  readonly tokens?: number | ((request: HttpClientRequest.HttpClientRequest) => number) | undefined
1302
1303
  /**
1303
- * Disable automatic limits updates from response headers.
1304
+ * The maximum number of automatic retries after an HTTP `429` response.
1305
+ * Use a non-negative integer. Defaults to an unlimited number of retries.
1306
+ * Set to `0` to disable automatic retries.
1307
+ */
1308
+ readonly times?: number | undefined
1309
+ /**
1310
+ * Overrides the response header names used for rate limit inspection.
1311
+ * Header names are matched case-insensitively. An omitted field continues
1312
+ * using the built-in names, while a configured field replaces them for
1313
+ * that value. Header value formats are unchanged.
1314
+ */
1315
+ readonly responseHeaders?: {
1316
+ /**
1317
+ * Header containing the maximum number of requests in a window.
1318
+ */
1319
+ readonly limit?: string | undefined
1320
+ /**
1321
+ * Header containing the number of requests remaining in a window.
1322
+ */
1323
+ readonly remaining?: string | undefined
1324
+ /**
1325
+ * Header containing the rate limit reset time.
1326
+ */
1327
+ readonly reset?: string | undefined
1328
+ /**
1329
+ * Header containing the number of seconds until the rate limit resets.
1330
+ */
1331
+ readonly resetAfter?: string | undefined
1332
+ /**
1333
+ * Header containing the retry delay for an HTTP `429` response.
1334
+ */
1335
+ readonly retryAfter?: string | undefined
1336
+ } | undefined
1337
+ /**
1338
+ * Disables automatic limit updates, `Retry-After` delays, and adaptive
1339
+ * feedback from response headers. This does not disable automatic HTTP
1340
+ * `429` retries. Set `times` to `0` to disable them.
1304
1341
  */
1305
1342
  readonly disableResponseInspection?: boolean | undefined
1306
1343
  /**
1307
- * Disable adaptive learning from `Retry-After` responses.
1344
+ * Disables adaptive learning from `Retry-After` responses. Response
1345
+ * inspection and direct `Retry-After` delays remain enabled.
1308
1346
  */
1309
1347
  readonly disableAdaptiveLearning?: boolean | undefined
1310
1348
  }
@@ -1319,6 +1357,12 @@ export declare namespace WithRateLimiter {
1319
1357
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1320
1358
  * wrapping a `429` response) by forcing the retry back through the limiter.
1321
1359
  *
1360
+ * **Gotchas**
1361
+ *
1362
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1363
+ * Disabling response inspection does not disable retries; set `times` to `0`
1364
+ * to return or fail with the first `429`.
1365
+ *
1322
1366
  * @category rate limiting
1323
1367
  * @since 4.0.0
1324
1368
  */
@@ -1332,6 +1376,12 @@ export const withRateLimiter: {
1332
1376
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1333
1377
  * wrapping a `429` response) by forcing the retry back through the limiter.
1334
1378
  *
1379
+ * **Gotchas**
1380
+ *
1381
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1382
+ * Disabling response inspection does not disable retries; set `times` to `0`
1383
+ * to return or fail with the first `429`.
1384
+ *
1335
1385
  * @category rate limiting
1336
1386
  * @since 4.0.0
1337
1387
  */
@@ -1347,6 +1397,12 @@ export const withRateLimiter: {
1347
1397
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1348
1398
  * wrapping a `429` response) by forcing the retry back through the limiter.
1349
1399
  *
1400
+ * **Gotchas**
1401
+ *
1402
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1403
+ * Disabling response inspection does not disable retries; set `times` to `0`
1404
+ * to return or fail with the first `429`.
1405
+ *
1350
1406
  * @category rate limiting
1351
1407
  * @since 4.0.0
1352
1408
  */
@@ -1371,6 +1427,7 @@ export const withRateLimiter: {
1371
1427
  ? tokensOption
1372
1428
  : constant(tokensOption ?? 1)
1373
1429
  const adaptiveLearningEnabled = !options.disableAdaptiveLearning
1430
+ const headerNames = resolveRateLimiterHeaderNames(options.responseHeaders)
1374
1431
 
1375
1432
  const getState = (key: string): RateLimiterState => {
1376
1433
  const current = states.get(key)
@@ -1385,13 +1442,13 @@ export const withRateLimiter: {
1385
1442
  ? undefined
1386
1443
  : (clock: Clock, key: string, headers: Headers.Headers, tokens: number) => {
1387
1444
  const current = getState(key)
1388
- const next = parseRateLimiterState(current, clock, headers, tokens)
1445
+ const next = parseRateLimiterState(current, clock, headers, tokens, headerNames)
1389
1446
  if (next.limit !== current.limit || !Duration.equals(next.window, current.window)) {
1390
1447
  states.set(key, next)
1391
1448
  }
1392
1449
  }
1393
1450
 
1394
- return transform(self, function loop(effect, request): Effect.Effect<
1451
+ return transform(self, function loop(effect, request, retries = 0): Effect.Effect<
1395
1452
  HttpClientResponse.HttpClientResponse,
1396
1453
  E | RateLimiter.RateLimiterError,
1397
1454
  R
@@ -1401,11 +1458,11 @@ export const withRateLimiter: {
1401
1458
  const key = resolveKey(request)
1402
1459
  const tokens = Math.max(resolveTokens(request), 1)
1403
1460
  const current = getState(key)
1461
+ const canRetry = options.times === undefined || retries < options.times
1404
1462
  function retry(retryAfter: Duration.Duration | undefined) {
1405
- if (options.disableResponseInspection) return loop(effect, request)
1406
1463
  return retryAfter
1407
- ? Effect.flatMap(Effect.sleep(retryAfter), () => loop(effect, request))
1408
- : loop(effect, request)
1464
+ ? Effect.flatMap(Effect.sleep(retryAfter), () => loop(effect, request, retries + 1))
1465
+ : loop(effect, request, retries + 1)
1409
1466
  }
1410
1467
  const inspectResponse = (
1411
1468
  response: HttpClientResponse.HttpClientResponse,
@@ -1422,12 +1479,18 @@ export const withRateLimiter: {
1422
1479
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1423
1480
  * wrapping a `429` response) by forcing the retry back through the limiter.
1424
1481
  *
1482
+ * **Gotchas**
1483
+ *
1484
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1485
+ * Disabling response inspection does not disable retries; set `times` to `0`
1486
+ * to return or fail with the first `429`.
1487
+ *
1425
1488
  * @category rate limiting
1426
1489
  * @since 4.0.0
1427
1490
  */
1428
1491
  Duration.Duration | undefined>(undefined);
1429
1492
  }
1430
- const retryAfter = parseRetryAfter(clock, getHeader(response.headers, "retry-after"))
1493
+ const retryAfter = parseRetryAfter(clock, getHeader(response.headers, ...headerNames.retryAfter))
1431
1494
  if (retryAfter === undefined) {
1432
1495
  return Effect.succeed</**
1433
1496
  * Applies request rate limiting using the `RateLimiter` service.
@@ -1438,12 +1501,18 @@ export const withRateLimiter: {
1438
1501
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1439
1502
  * wrapping a `429` response) by forcing the retry back through the limiter.
1440
1503
  *
1504
+ * **Gotchas**
1505
+ *
1506
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1507
+ * Disabling response inspection does not disable retries; set `times` to `0`
1508
+ * to return or fail with the first `429`.
1509
+ *
1441
1510
  * @category rate limiting
1442
1511
  * @since 4.0.0
1443
1512
  */
1444
1513
  Duration.Duration | undefined>(undefined);
1445
1514
  }
1446
- const delay = parseRateLimitWindow(clock, response.headers) ?? retryAfter
1515
+ const delay = parseRateLimitWindow(clock, response.headers, headerNames) ?? retryAfter
1447
1516
  if (adaptive === undefined) {
1448
1517
  return Effect.succeed</**
1449
1518
  * Applies request rate limiting using the `RateLimiter` service.
@@ -1454,6 +1523,12 @@ export const withRateLimiter: {
1454
1523
  * automatically retries HTTP `429` responses (or `HttpClientError` values
1455
1524
  * wrapping a `429` response) by forcing the retry back through the limiter.
1456
1525
  *
1526
+ * **Gotchas**
1527
+ *
1528
+ * Automatic HTTP `429` retries are unlimited unless `times` is specified.
1529
+ * Disabling response inspection does not disable retries; set `times` to `0`
1530
+ * to return or fail with the first `429`.
1531
+ *
1457
1532
  * @category rate limiting
1458
1533
  * @since 4.0.0
1459
1534
  */
@@ -1489,7 +1564,7 @@ export const withRateLimiter: {
1489
1564
  const request = Effect.matchEffect(effect, {
1490
1565
  onSuccess(response) {
1491
1566
  return Effect.flatMap(inspectResponse(response, adaptive), (retryAfter) => {
1492
- if (response.status !== 429) return Effect.succeed(response)
1567
+ if (response.status !== 429 || !canRetry) return Effect.succeed(response)
1493
1568
  return retry(retryAfter)
1494
1569
  })
1495
1570
  },
@@ -1497,7 +1572,7 @@ export const withRateLimiter: {
1497
1572
  if (isTooManyRequestsHttpClientError(error)) {
1498
1573
  return Effect.flatMap(
1499
1574
  inspectResponse(error.reason.response, adaptive),
1500
- (retryAfter) => retry(retryAfter)
1575
+ (retryAfter) => canRetry ? retry(retryAfter) : Effect.fail(error)
1501
1576
  )
1502
1577
  }
1503
1578
  return Effect.fail(error)
@@ -1531,6 +1606,34 @@ export const withRateLimiter: {
1531
1606
  });
1532
1607
  })
1533
1608
 
1609
+ interface RateLimiterHeaderNames {
1610
+ readonly limit: ReadonlyArray<string>
1611
+ readonly remaining: ReadonlyArray<string>
1612
+ readonly reset: ReadonlyArray<string>
1613
+ readonly resetAfter: ReadonlyArray<string>
1614
+ readonly retryAfter: ReadonlyArray<string>
1615
+ }
1616
+
1617
+ const resolveRateLimiterHeaderNames = (
1618
+ responseHeaders: WithRateLimiter.Options["responseHeaders"]
1619
+ ): RateLimiterHeaderNames => ({
1620
+ limit: responseHeaders?.limit === undefined
1621
+ ? ["ratelimit-limit", "x-ratelimit-limit"]
1622
+ : [responseHeaders.limit.toLowerCase()],
1623
+ remaining: responseHeaders?.remaining === undefined
1624
+ ? ["ratelimit-remaining", "x-ratelimit-remaining"]
1625
+ : [responseHeaders.remaining.toLowerCase()],
1626
+ reset: responseHeaders?.reset === undefined
1627
+ ? ["ratelimit-reset", "x-ratelimit-reset"]
1628
+ : [responseHeaders.reset.toLowerCase()],
1629
+ resetAfter: responseHeaders?.resetAfter === undefined
1630
+ ? ["ratelimit-reset-after", "x-ratelimit-reset-after"]
1631
+ : [responseHeaders.resetAfter.toLowerCase()],
1632
+ retryAfter: responseHeaders?.retryAfter === undefined
1633
+ ? ["retry-after"]
1634
+ : [responseHeaders.retryAfter.toLowerCase()]
1635
+ })
1636
+
1534
1637
  interface RateLimiterState {
1535
1638
  readonly limit: number
1536
1639
  readonly window: Duration.Duration
@@ -1541,10 +1644,11 @@ const parseRateLimiterState = (
1541
1644
  state: RateLimiterState,
1542
1645
  clock: Clock,
1543
1646
  headers: Headers.Headers,
1544
- tokens: number
1647
+ tokens: number,
1648
+ headerNames: RateLimiterHeaderNames
1545
1649
  ): RateLimiterState => {
1546
- const limit = parseRateLimitLimit(state, headers, tokens) ?? state.limit
1547
- const window = parseRateLimitWindow(clock, headers) ?? state.window
1650
+ const limit = parseRateLimitLimit(state, headers, tokens, headerNames) ?? state.limit
1651
+ const window = parseRateLimitWindow(clock, headers, headerNames) ?? state.window
1548
1652
  if (limit === state.limit && Duration.equals(window, state.window)) {
1549
1653
  return state
1550
1654
  }
@@ -1554,35 +1658,40 @@ const parseRateLimiterState = (
1554
1658
  const parseRateLimitLimit = (
1555
1659
  state: RateLimiterState,
1556
1660
  headers: Headers.Headers,
1557
- tokens: number
1661
+ tokens: number,
1662
+ headerNames: RateLimiterHeaderNames
1558
1663
  ): number | undefined => {
1559
- const raw = getHeader(headers, "ratelimit-limit", "x-ratelimit-limit")
1664
+ const raw = getHeader(headers, ...headerNames.limit)
1560
1665
  const value = parseNumberHeader(raw)
1561
1666
  if (value !== undefined && value > 0) {
1562
1667
  return value
1563
1668
  }
1564
- const remaining = parseRateLimitRemaining(headers)
1669
+ const remaining = parseRateLimitRemaining(headers, headerNames)
1565
1670
  if (remaining === undefined) {
1566
1671
  return undefined
1567
1672
  }
1568
1673
  return state.initial ? remaining + tokens : Math.max(remaining + tokens, state.limit)
1569
1674
  }
1570
1675
 
1571
- const parseRateLimitRemaining = (headers: Headers.Headers): number | undefined => {
1572
- const raw = getHeader(headers, "ratelimit-remaining", "x-ratelimit-remaining")
1676
+ const parseRateLimitRemaining = (
1677
+ headers: Headers.Headers,
1678
+ headerNames: RateLimiterHeaderNames
1679
+ ): number | undefined => {
1680
+ const raw = getHeader(headers, ...headerNames.remaining)
1573
1681
  const value = parseNumberHeader(raw)
1574
1682
  return value !== undefined && value >= 0 ? value : undefined
1575
1683
  }
1576
1684
 
1577
1685
  const parseRateLimitWindow = (
1578
1686
  clock: Clock,
1579
- headers: Headers.Headers
1687
+ headers: Headers.Headers,
1688
+ headerNames: RateLimiterHeaderNames
1580
1689
  ): Duration.Duration | undefined => {
1581
- const resetAfter = parseResetAfter(getHeader(headers, "ratelimit-reset-after", "x-ratelimit-reset-after"))
1690
+ const resetAfter = parseResetAfter(getHeader(headers, ...headerNames.resetAfter))
1582
1691
  if (resetAfter !== undefined) {
1583
1692
  return resetAfter
1584
1693
  }
1585
- return parseResetHeader(clock, getHeader(headers, "ratelimit-reset", "x-ratelimit-reset"))
1694
+ return parseResetHeader(clock, getHeader(headers, ...headerNames.reset))
1586
1695
  }
1587
1696
 
1588
1697
  const parseRetryAfter = (