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.
- package/AGENTS.md +381 -0
- package/CLAUDE.md +381 -0
- package/ai-docs/README.md +44 -0
- package/ai-docs/package.json +36 -0
- package/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
- package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +39 -0
- package/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
- package/ai-docs/src/01_effect/01_basics/index.md +5 -0
- package/ai-docs/src/01_effect/02_schema/10_schema-basics.ts +43 -0
- package/ai-docs/src/01_effect/02_schema/index.md +7 -0
- package/ai-docs/src/01_effect/03_services/01_service.ts +45 -0
- package/ai-docs/src/01_effect/03_services/10_reference.ts +10 -0
- package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +70 -0
- package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +66 -0
- package/ai-docs/src/01_effect/03_services/index.md +5 -0
- package/ai-docs/src/01_effect/04_errors/01_error-handling.ts +30 -0
- package/ai-docs/src/01_effect/04_errors/10_catch-tags.ts +24 -0
- package/ai-docs/src/01_effect/04_errors/20_reason-errors.ts +64 -0
- package/ai-docs/src/01_effect/04_errors/index.md +1 -0
- package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +105 -0
- package/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts +31 -0
- package/ai-docs/src/01_effect/05_resources/30_layer-map.ts +86 -0
- package/ai-docs/src/01_effect/05_resources/index.md +3 -0
- package/ai-docs/src/01_effect/06_running/10_run-main.ts +30 -0
- package/ai-docs/src/01_effect/06_running/20_layer-launch.ts +27 -0
- package/ai-docs/src/01_effect/06_running/index.md +1 -0
- package/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts +56 -0
- package/ai-docs/src/01_effect/07_pubsub/index.md +3 -0
- package/ai-docs/src/03_stream/10_creating-streams.ts +103 -0
- package/ai-docs/src/03_stream/20_consuming-streams.ts +137 -0
- package/ai-docs/src/03_stream/30_encoding.ts +165 -0
- package/ai-docs/src/03_stream/index.md +4 -0
- package/ai-docs/src/04_integration/10_managed-runtime.ts +129 -0
- package/ai-docs/src/04_integration/index.md +5 -0
- package/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
- package/ai-docs/src/05_batching/index.md +3 -0
- package/ai-docs/src/06_schedule/10_schedules.ts +110 -0
- package/ai-docs/src/06_schedule/index.md +3 -0
- package/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
- package/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
- package/ai-docs/src/07_datetime/index.md +5 -0
- package/ai-docs/src/08_observability/10_logging.ts +66 -0
- package/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
- package/ai-docs/src/08_observability/index.md +7 -0
- package/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
- package/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
- package/ai-docs/src/09_testing/index.md +1 -0
- package/ai-docs/src/10_predicate/01_basics.ts +14 -0
- package/ai-docs/src/10_predicate/index.md +9 -0
- package/ai-docs/src/50_http-client/10_basics.ts +102 -0
- package/ai-docs/src/50_http-client/index.md +3 -0
- package/ai-docs/src/51_http-server/10_basics.ts +116 -0
- package/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
- package/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
- package/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
- package/ai-docs/src/51_http-server/fixtures/api/Users.ts +91 -0
- package/ai-docs/src/51_http-server/fixtures/domain/User.ts +12 -0
- package/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
- package/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +36 -0
- package/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +71 -0
- package/ai-docs/src/51_http-server/fixtures/server/Users.ts +62 -0
- package/ai-docs/src/51_http-server/index.md +3 -0
- package/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
- package/ai-docs/src/60_child-process/index.md +3 -0
- package/ai-docs/src/70_cli/10_basics.ts +136 -0
- package/ai-docs/src/70_cli/index.md +5 -0
- package/ai-docs/src/71_ai/10_language-model.ts +156 -0
- package/ai-docs/src/71_ai/20_tools.ts +226 -0
- package/ai-docs/src/71_ai/30_chat.ts +158 -0
- package/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
- package/ai-docs/src/71_ai/index.md +5 -0
- package/ai-docs/src/80_cluster/10_entities.ts +97 -0
- package/ai-docs/src/80_cluster/index.md +4 -0
- package/ai-docs/src/index.md +10 -0
- package/ai-docs/tsconfig.json +24 -0
- package/dist/Brand.d.ts +3 -3
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js +4 -3
- package/dist/Brand.js.map +1 -1
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +5 -4
- package/dist/Config.js.map +1 -1
- package/dist/ConfigProvider.d.ts +35 -3
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +41 -8
- package/dist/ConfigProvider.js.map +1 -1
- package/dist/Context.d.ts +0 -1
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +1 -22
- package/dist/Context.js.map +1 -1
- package/dist/Cron.d.ts +31 -0
- package/dist/Cron.d.ts.map +1 -1
- package/dist/Cron.js +58 -2
- package/dist/Cron.js.map +1 -1
- package/dist/Fiber.d.ts +1 -1
- package/dist/Fiber.d.ts.map +1 -1
- package/dist/Function.d.ts +26 -0
- package/dist/Function.d.ts.map +1 -1
- package/dist/Function.js +36 -0
- package/dist/Function.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +34 -17
- package/dist/JsonSchema.js.map +1 -1
- package/dist/Optic.d.ts +35 -21
- package/dist/Optic.d.ts.map +1 -1
- package/dist/Optic.js +33 -34
- package/dist/Optic.js.map +1 -1
- package/dist/Schema.d.ts +105 -109
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +141 -104
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +32 -17
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +79 -60
- package/dist/SchemaAST.js.map +1 -1
- package/dist/SchemaError.d.ts +8 -8
- package/dist/SchemaError.d.ts.map +1 -1
- package/dist/SchemaError.js +7 -6
- package/dist/SchemaError.js.map +1 -1
- package/dist/SchemaGetter.d.ts +8 -5
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +41 -37
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/SchemaIssue.d.ts +195 -38
- package/dist/SchemaIssue.d.ts.map +1 -1
- package/dist/SchemaIssue.js +243 -68
- package/dist/SchemaIssue.js.map +1 -1
- package/dist/SchemaParser.d.ts +30 -0
- package/dist/SchemaParser.d.ts.map +1 -1
- package/dist/SchemaParser.js +39 -9
- package/dist/SchemaParser.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +15 -3
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +8 -2
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +2 -2
- package/dist/SchemaTransformation.d.ts.map +1 -1
- package/dist/SchemaTransformation.js +26 -26
- package/dist/SchemaTransformation.js.map +1 -1
- package/dist/ScopedRef.d.ts.map +1 -1
- package/dist/ScopedRef.js +1 -1
- package/dist/ScopedRef.js.map +1 -1
- package/dist/Stdio.d.ts +17 -4
- package/dist/Stdio.d.ts.map +1 -1
- package/dist/Stdio.js +6 -3
- package/dist/Stdio.js.map +1 -1
- package/dist/TxQueue.js +1 -1
- package/dist/TxQueue.js.map +1 -1
- package/dist/internal/effect.js +2 -4
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/executionPlan.js +3 -2
- package/dist/internal/executionPlan.js.map +1 -1
- package/dist/internal/rcRef.js +18 -13
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +10 -6
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/schema.js +1 -9
- package/dist/internal/schema/schema.js.map +1 -1
- package/dist/internal/schema/toArbitrary.d.ts +1 -4
- package/dist/internal/schema/toArbitrary.d.ts.map +1 -1
- package/dist/internal/schema/toArbitrary.js +0 -74
- package/dist/internal/schema/toArbitrary.js.map +1 -1
- package/dist/internal/schema/toJsonSchemaDocument.js +18 -4
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toRepresentation.js +45 -43
- package/dist/internal/schema/toRepresentation.js.map +1 -1
- package/dist/testing/TestSchema.d.ts +1 -1
- package/dist/testing/TestSchema.d.ts.map +1 -1
- package/dist/testing/TestSchema.js +10 -9
- package/dist/testing/TestSchema.js.map +1 -1
- package/dist/unstable/ai/McpSchema.d.ts +110 -110
- package/dist/unstable/ai/McpSchema.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.js +23 -11
- package/dist/unstable/ai/McpServer.js.map +1 -1
- package/dist/unstable/ai/Prompt.d.ts +4 -3
- package/dist/unstable/ai/Prompt.d.ts.map +1 -1
- package/dist/unstable/ai/Prompt.js +42 -17
- package/dist/unstable/ai/Prompt.js.map +1 -1
- package/dist/unstable/cluster/Entity.d.ts +2 -2
- package/dist/unstable/cluster/Entity.d.ts.map +1 -1
- package/dist/unstable/cluster/Entity.js.map +1 -1
- package/dist/unstable/cluster/EntityProxy.d.ts +3 -3
- package/dist/unstable/cluster/EntityProxy.d.ts.map +1 -1
- package/dist/unstable/cluster/EntityProxy.js +4 -3
- package/dist/unstable/cluster/EntityProxy.js.map +1 -1
- package/dist/unstable/cluster/Reply.js +4 -4
- package/dist/unstable/cluster/Reply.js.map +1 -1
- package/dist/unstable/cluster/Sharding.d.ts +2 -2
- package/dist/unstable/cluster/Sharding.d.ts.map +1 -1
- package/dist/unstable/cluster/Sharding.js +14 -5
- package/dist/unstable/cluster/Sharding.js.map +1 -1
- package/dist/unstable/encoding/Msgpack.d.ts.map +1 -1
- package/dist/unstable/encoding/Msgpack.js +6 -6
- package/dist/unstable/encoding/Msgpack.js.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.d.ts +6 -5
- package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.js +14 -12
- package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.d.ts +3 -3
- package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.js +2 -3
- package/dist/unstable/eventlog/EventLogMessage.js.map +1 -1
- package/dist/unstable/eventlog/EventLogRemote.js +4 -4
- package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
- package/dist/unstable/eventlog/EventLogServerEncrypted.js +3 -2
- package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/unstable/http/HttpClient.d.ts +59 -3
- package/dist/unstable/http/HttpClient.d.ts.map +1 -1
- package/dist/unstable/http/HttpClient.js +33 -19
- package/dist/unstable/http/HttpClient.js.map +1 -1
- package/dist/unstable/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/unstable/http/HttpServerRequest.js +7 -1
- package/dist/unstable/http/HttpServerRequest.js.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.d.ts.map +1 -1
- package/dist/unstable/http/MultipartParser/internal/multipart.js +18 -6
- package/dist/unstable/http/MultipartParser/internal/multipart.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiBuilder.js +10 -10
- package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
- package/dist/unstable/httpapi/HttpApiClient.js +9 -9
- package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
- package/dist/unstable/httpapi/OpenApi.d.ts.map +1 -1
- package/dist/unstable/httpapi/OpenApi.js +1 -1
- package/dist/unstable/httpapi/OpenApi.js.map +1 -1
- package/dist/unstable/reactivity/AsyncResult.d.ts.map +1 -1
- package/dist/unstable/reactivity/AsyncResult.js +4 -4
- package/dist/unstable/reactivity/AsyncResult.js.map +1 -1
- package/dist/unstable/reactivity/Atom.d.ts +26 -11
- package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
- package/dist/unstable/reactivity/Atom.js +9 -22
- package/dist/unstable/reactivity/Atom.js.map +1 -1
- package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcClient.js +8 -1
- package/dist/unstable/rpc/RpcClient.js.map +1 -1
- package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcServer.js +3 -2
- package/dist/unstable/rpc/RpcServer.js.map +1 -1
- package/dist/unstable/sql/SqlResolver.d.ts.map +1 -1
- package/dist/unstable/sql/SqlResolver.js +20 -16
- package/dist/unstable/sql/SqlResolver.js.map +1 -1
- package/dist/unstable/workers/Worker.d.ts.map +1 -1
- package/dist/unstable/workers/Worker.js +11 -12
- package/dist/unstable/workers/Worker.js.map +1 -1
- package/dist/unstable/workflow/Workflow.d.ts.map +1 -1
- package/dist/unstable/workflow/Workflow.js +2 -2
- package/dist/unstable/workflow/Workflow.js.map +1 -1
- package/package.json +5 -2
- package/src/Brand.ts +4 -4
- package/src/Config.ts +6 -4
- package/src/ConfigProvider.ts +43 -8
- package/src/Context.ts +1 -23
- package/src/Cron.ts +65 -2
- package/src/Fiber.ts +1 -1
- package/src/Function.ts +37 -0
- package/src/JsonSchema.ts +43 -42
- package/src/Optic.ts +59 -58
- package/src/Schema.ts +213 -217
- package/src/SchemaAST.ts +135 -78
- package/src/SchemaError.ts +9 -9
- package/src/SchemaGetter.ts +89 -30
- package/src/SchemaIssue.ts +285 -80
- package/src/SchemaParser.ts +51 -17
- package/src/SchemaRepresentation.ts +15 -3
- package/src/SchemaTransformation.ts +66 -18
- package/src/ScopedRef.ts +3 -1
- package/src/Stdio.ts +23 -4
- package/src/TxQueue.ts +1 -1
- package/src/internal/effect.ts +3 -6
- package/src/internal/executionPlan.ts +3 -2
- package/src/internal/rcRef.ts +24 -16
- package/src/internal/schema/fromJsonSchemaDocument.ts +19 -14
- package/src/internal/schema/schema.ts +1 -17
- package/src/internal/schema/toArbitrary.ts +0 -72
- package/src/internal/schema/toJsonSchemaDocument.ts +24 -4
- package/src/internal/schema/toRepresentation.ts +66 -48
- package/src/testing/TestSchema.ts +10 -10
- package/src/unstable/ai/McpServer.ts +23 -11
- package/src/unstable/ai/Prompt.ts +51 -19
- package/src/unstable/cluster/Entity.ts +7 -2
- package/src/unstable/cluster/EntityProxy.ts +12 -5
- package/src/unstable/cluster/Reply.ts +4 -4
- package/src/unstable/cluster/Sharding.ts +29 -17
- package/src/unstable/encoding/Msgpack.ts +12 -8
- package/src/unstable/eventlog/EventLogEncryption.ts +15 -16
- package/src/unstable/eventlog/EventLogMessage.ts +2 -3
- package/src/unstable/eventlog/EventLogRemote.ts +4 -4
- package/src/unstable/eventlog/EventLogServerEncrypted.ts +2 -2
- package/src/unstable/http/HttpClient.ts +132 -23
- package/src/unstable/http/HttpServerRequest.ts +8 -3
- package/src/unstable/http/MultipartParser/internal/multipart.ts +19 -6
- package/src/unstable/httpapi/HttpApiBuilder.ts +32 -13
- package/src/unstable/httpapi/HttpApiClient.ts +26 -8
- package/src/unstable/httpapi/OpenApi.ts +4 -1
- package/src/unstable/reactivity/AsyncResult.ts +11 -6
- package/src/unstable/reactivity/Atom.ts +44 -25
- package/src/unstable/rpc/RpcClient.ts +9 -1
- package/src/unstable/rpc/RpcServer.ts +4 -2
- package/src/unstable/sql/SqlResolver.ts +28 -20
- package/src/unstable/workers/Worker.ts +13 -14
- 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, ...
|
|
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, ...
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
397
|
-
|
|
395
|
+
new SchemaIssue.InvalidValue(
|
|
396
|
+
{ expected: "valid MessagePack bytes" },
|
|
397
|
+
e,
|
|
398
|
+
options
|
|
399
|
+
)
|
|
398
400
|
)
|
|
399
401
|
}
|
|
400
402
|
},
|
|
401
|
-
encode(t,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
108
|
-
const encryptedEntries = yield* Effect.promise(() =>
|
|
110
|
+
return yield* Effect.promise(() =>
|
|
109
111
|
Promise.all(
|
|
110
|
-
data.map((entry) =>
|
|
111
|
-
crypto.
|
|
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,
|
|
265
|
-
*
|
|
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((
|
|
310
|
+
Effect.flatMap((encryptedEntries) =>
|
|
311
311
|
new WriteEntries({
|
|
312
312
|
publicKey: options.identity.publicKey,
|
|
313
313
|
storeId: options.storeId,
|
|
314
|
-
|
|
315
|
-
encryptedEntries: msg.encryptedEntries.map((entry, i) => ({
|
|
314
|
+
encryptedEntries: encryptedEntries.map((entry, i) => ({
|
|
316
315
|
entryId: options.entries[i].id,
|
|
317
|
-
|
|
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
|
|
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,
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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,
|
|
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,
|
|
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 = (
|
|
1572
|
-
|
|
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,
|
|
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,
|
|
1694
|
+
return parseResetHeader(clock, getHeader(headers, ...headerNames.reset))
|
|
1586
1695
|
}
|
|
1587
1696
|
|
|
1588
1697
|
const parseRetryAfter = (
|