effect 4.0.2 → 4.0.4
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 +24 -0
- package/CLAUDE.md +24 -0
- package/ai-docs/package.json +2 -2
- package/ai-docs/src/52_graphql/10_basics.ts +111 -0
- package/ai-docs/src/52_graphql/20_testing.ts +45 -0
- package/ai-docs/src/52_graphql/30_batching.ts +116 -0
- package/ai-docs/src/52_graphql/40_aliased-batching.ts +122 -0
- package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
- package/ai-docs/src/52_graphql/index.md +6 -0
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +12 -3
- package/dist/Context.js.map +1 -1
- package/dist/Crypto.d.ts +33 -5
- package/dist/Crypto.d.ts.map +1 -1
- package/dist/Crypto.js +8 -2
- package/dist/Crypto.js.map +1 -1
- package/dist/DateTime.d.ts +9 -6
- package/dist/DateTime.d.ts.map +1 -1
- package/dist/DateTime.js +9 -6
- package/dist/DateTime.js.map +1 -1
- package/dist/Duration.d.ts +6 -6
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +27 -8
- package/dist/Duration.js.map +1 -1
- package/dist/FileSystem.d.ts +44 -2
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js.map +1 -1
- package/dist/RequestResolver.d.ts.map +1 -1
- package/dist/RequestResolver.js +4 -10
- package/dist/RequestResolver.js.map +1 -1
- package/dist/Scheduler.d.ts +9 -0
- package/dist/Scheduler.d.ts.map +1 -1
- package/dist/Scheduler.js +9 -0
- package/dist/Scheduler.js.map +1 -1
- package/dist/Schema.d.ts +481 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +362 -4
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaGetter.d.ts +4 -0
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +52 -33
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/Sink.d.ts.map +1 -1
- package/dist/Sink.js +38 -17
- package/dist/Sink.js.map +1 -1
- package/dist/Stdio.d.ts +7 -1
- package/dist/Stdio.d.ts.map +1 -1
- package/dist/Stdio.js +1 -0
- package/dist/Stdio.js.map +1 -1
- package/dist/Stream.d.ts +22 -11
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +21 -6
- package/dist/Stream.js.map +1 -1
- package/dist/Utils.js +8 -3
- package/dist/Utils.js.map +1 -1
- package/dist/ai/AnthropicStructuredOutput.js +0 -2
- package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
- package/dist/ai/Chat.d.ts +2 -2
- package/dist/ai/Chat.d.ts.map +1 -1
- package/dist/ai/LanguageModel.d.ts +38 -7
- package/dist/ai/LanguageModel.d.ts.map +1 -1
- package/dist/ai/LanguageModel.js +25 -7
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpServer.d.ts +61 -0
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +196 -44
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +28 -5
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Response.d.ts +4 -3
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +5 -0
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +1 -0
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/cli/CliOutput.d.ts +27 -0
- package/dist/cli/CliOutput.d.ts.map +1 -1
- package/dist/cli/CliOutput.js +33 -10
- package/dist/cli/CliOutput.js.map +1 -1
- package/dist/cli/Command.d.ts.map +1 -1
- package/dist/cli/Command.js +6 -3
- package/dist/cli/Command.js.map +1 -1
- package/dist/cli/Completions.d.ts +3 -3
- package/dist/cli/Completions.d.ts.map +1 -1
- package/dist/cli/Completions.js +8 -5
- package/dist/cli/Completions.js.map +1 -1
- package/dist/cli/GlobalFlag.d.ts +5 -4
- package/dist/cli/GlobalFlag.d.ts.map +1 -1
- package/dist/cli/GlobalFlag.js +3 -3
- package/dist/cli/GlobalFlag.js.map +1 -1
- package/dist/cli/Prompt.d.ts +17 -1
- package/dist/cli/Prompt.d.ts.map +1 -1
- package/dist/cli/Prompt.js +119 -114
- package/dist/cli/Prompt.js.map +1 -1
- package/dist/cli/internal/ansi.d.ts +0 -1
- package/dist/cli/internal/ansi.d.ts.map +1 -1
- package/dist/cli/internal/ansi.js +20 -3
- package/dist/cli/internal/ansi.js.map +1 -1
- package/dist/cli/internal/completions/powershell.d.ts +2 -0
- package/dist/cli/internal/completions/powershell.d.ts.map +1 -0
- package/dist/cli/internal/completions/powershell.js +273 -0
- package/dist/cli/internal/completions/powershell.js.map +1 -0
- package/dist/cli/internal/completions/zsh.js +3 -2
- package/dist/cli/internal/completions/zsh.js.map +1 -1
- package/dist/cli/internal/wizard.d.ts +3 -3
- package/dist/cli/internal/wizard.d.ts.map +1 -1
- package/dist/cli/internal/wizard.js +21 -18
- package/dist/cli/internal/wizard.js.map +1 -1
- package/dist/cluster/ClusterSchema.d.ts +28 -0
- package/dist/cluster/ClusterSchema.d.ts.map +1 -1
- package/dist/cluster/ClusterSchema.js +30 -0
- package/dist/cluster/ClusterSchema.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +56 -26
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +18 -3
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +31 -11
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +8 -2
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +187 -66
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/SqlMessageStorage.d.ts +59 -19
- package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/cluster/SqlMessageStorage.js +84 -55
- package/dist/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/cluster/SqlRunnerStorage.d.ts +68 -19
- package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/cluster/SqlRunnerStorage.js +191 -113
- package/dist/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +104 -37
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/encoding/Sse.d.ts +6 -0
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +62 -70
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/graphql/GraphQL.d.ts +349 -0
- package/dist/graphql/GraphQL.d.ts.map +1 -0
- package/dist/graphql/GraphQL.js +207 -0
- package/dist/graphql/GraphQL.js.map +1 -0
- package/dist/graphql/GraphQLClient.d.ts +562 -0
- package/dist/graphql/GraphQLClient.d.ts.map +1 -0
- package/dist/graphql/GraphQLClient.js +498 -0
- package/dist/graphql/GraphQLClient.js.map +1 -0
- package/dist/graphql/GraphQLClientError.d.ts +445 -0
- package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
- package/dist/graphql/GraphQLClientError.js +479 -0
- package/dist/graphql/GraphQLClientError.js.map +1 -0
- package/dist/graphql/GraphQLGroup.d.ts +118 -0
- package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
- package/dist/graphql/GraphQLGroup.js +109 -0
- package/dist/graphql/GraphQLGroup.js.map +1 -0
- package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
- package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
- package/dist/graphql/GraphQLMiddleware.js +130 -0
- package/dist/graphql/GraphQLMiddleware.js.map +1 -0
- package/dist/graphql/GraphQLProtocol.d.ts +731 -0
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
- package/dist/graphql/GraphQLProtocol.js +719 -0
- package/dist/graphql/GraphQLProtocol.js.map +1 -0
- package/dist/graphql/GraphQLResolver.d.ts +453 -0
- package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
- package/dist/graphql/GraphQLResolver.js +552 -0
- package/dist/graphql/GraphQLResolver.js.map +1 -0
- package/dist/graphql/index.d.ts +40 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +41 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/http/FindMyWay/internal/router.js +3 -6
- package/dist/http/FindMyWay/internal/router.js.map +1 -1
- package/dist/http/HttpClient.d.ts +21 -0
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +63 -12
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpEffect.d.ts.map +1 -1
- package/dist/http/HttpEffect.js +14 -7
- package/dist/http/HttpEffect.js.map +1 -1
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +31 -32
- package/dist/http/HttpMiddleware.js.map +1 -1
- package/dist/http/HttpRouter.d.ts +35 -0
- package/dist/http/HttpRouter.d.ts.map +1 -1
- package/dist/http/HttpRouter.js +35 -0
- package/dist/http/HttpRouter.js.map +1 -1
- package/dist/http/HttpServerResponse.d.ts +8 -6
- package/dist/http/HttpServerResponse.d.ts.map +1 -1
- package/dist/http/HttpServerResponse.js +27 -16
- package/dist/http/HttpServerResponse.js.map +1 -1
- package/dist/http/HttpTraceContext.d.ts.map +1 -1
- package/dist/http/HttpTraceContext.js +4 -2
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http/Multipart.d.ts.map +1 -1
- package/dist/http/Multipart.js +15 -1
- package/dist/http/Multipart.js.map +1 -1
- package/dist/http-api/HttpApiScalar.d.ts +5 -0
- package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
- package/dist/http-api/HttpApiScalar.js.map +1 -1
- package/dist/internal/dateTime.js +40 -35
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +19 -9
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/graphql.d.ts +2 -0
- package/dist/internal/graphql.d.ts.map +1 -0
- package/dist/internal/graphql.js +195 -0
- package/dist/internal/graphql.js.map +1 -0
- package/dist/internal/graphqlHeaders.d.ts +4 -0
- package/dist/internal/graphqlHeaders.d.ts.map +1 -0
- package/dist/internal/graphqlHeaders.js +11 -0
- package/dist/internal/graphqlHeaders.js.map +1 -0
- package/dist/internal/request.js +20 -1
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/schema/toCodeDocument.js +4 -1
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/net/AddressResolver.d.ts +145 -0
- package/dist/net/AddressResolver.d.ts.map +1 -0
- package/dist/net/AddressResolver.js +114 -0
- package/dist/net/AddressResolver.js.map +1 -0
- package/dist/net/Dns.d.ts +596 -0
- package/dist/net/Dns.d.ts.map +1 -0
- package/dist/net/Dns.js +549 -0
- package/dist/net/Dns.js.map +1 -0
- package/dist/net/Host.d.ts +369 -0
- package/dist/net/Host.d.ts.map +1 -0
- package/dist/net/Host.js +397 -0
- package/dist/net/Host.js.map +1 -0
- package/dist/net/IpInterface.d.ts +76 -3
- package/dist/net/IpInterface.d.ts.map +1 -1
- package/dist/net/IpInterface.js +53 -4
- package/dist/net/IpInterface.js.map +1 -1
- package/dist/net/IpNetwork.d.ts +82 -8
- package/dist/net/IpNetwork.d.ts.map +1 -1
- package/dist/net/IpNetwork.js +60 -4
- package/dist/net/IpNetwork.js.map +1 -1
- package/dist/net/NetAddress.d.ts +396 -35
- package/dist/net/NetAddress.d.ts.map +1 -1
- package/dist/net/NetAddress.js +358 -34
- package/dist/net/NetAddress.js.map +1 -1
- package/dist/net/index.d.ts +15 -0
- package/dist/net/index.d.ts.map +1 -1
- package/dist/net/index.js +15 -0
- package/dist/net/index.js.map +1 -1
- package/dist/observability/OtlpMetrics.d.ts.map +1 -1
- package/dist/observability/OtlpMetrics.js +15 -21
- package/dist/observability/OtlpMetrics.js.map +1 -1
- package/dist/observability/PrometheusMetrics.js +1 -0
- package/dist/observability/PrometheusMetrics.js.map +1 -1
- package/dist/persistence/PersistedQueue.d.ts +32 -3
- package/dist/persistence/PersistedQueue.d.ts.map +1 -1
- package/dist/persistence/PersistedQueue.js +75 -16
- package/dist/persistence/PersistedQueue.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +22 -6
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +12 -5
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts +6 -0
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +78 -29
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/socket/DatagramSocket.d.ts +456 -0
- package/dist/socket/DatagramSocket.d.ts.map +1 -0
- package/dist/socket/DatagramSocket.js +535 -0
- package/dist/socket/DatagramSocket.js.map +1 -0
- package/dist/socket/Socket.d.ts +20 -1
- package/dist/socket/Socket.d.ts.map +1 -1
- package/dist/socket/Socket.js +9 -1
- package/dist/socket/Socket.js.map +1 -1
- package/dist/socket/index.d.ts +5 -0
- package/dist/socket/index.d.ts.map +1 -1
- package/dist/socket/index.js +5 -0
- package/dist/socket/index.js.map +1 -1
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +7 -2
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +7 -1
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +31 -6
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/testing/TestCrypto.d.ts +73 -0
- package/dist/testing/TestCrypto.d.ts.map +1 -0
- package/dist/testing/TestCrypto.js +87 -0
- package/dist/testing/TestCrypto.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/workflow/DurableDeferred.d.ts +54 -0
- package/dist/workflow/DurableDeferred.d.ts.map +1 -1
- package/dist/workflow/DurableDeferred.js +108 -50
- package/dist/workflow/DurableDeferred.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts +24 -2
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +13 -3
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +6 -3
- package/src/Context.ts +12 -3
- package/src/Crypto.ts +43 -9
- package/src/DateTime.ts +9 -6
- package/src/Duration.ts +36 -16
- package/src/FileSystem.ts +48 -2
- package/src/RequestResolver.ts +4 -10
- package/src/Scheduler.ts +9 -0
- package/src/Schema.ts +677 -5
- package/src/SchemaGetter.ts +47 -35
- package/src/Sink.ts +45 -18
- package/src/Stdio.ts +9 -2
- package/src/Stream.ts +44 -15
- package/src/Utils.ts +8 -5
- package/src/ai/AnthropicStructuredOutput.ts +2 -2
- package/src/ai/Chat.ts +2 -2
- package/src/ai/LanguageModel.ts +96 -24
- package/src/ai/McpServer.ts +288 -94
- package/src/ai/Prompt.ts +24 -10
- package/src/ai/Response.ts +6 -3
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +5 -0
- package/src/ai/internal/mcpProtocol.ts +3 -0
- package/src/ai/internal/mcpRuntime.ts +2 -0
- package/src/cli/CliOutput.ts +33 -16
- package/src/cli/Command.ts +4 -3
- package/src/cli/Completions.ts +9 -6
- package/src/cli/GlobalFlag.ts +19 -11
- package/src/cli/Prompt.ts +159 -149
- package/src/cli/internal/ansi.ts +26 -3
- package/src/cli/internal/completions/powershell.ts +322 -0
- package/src/cli/internal/completions/zsh.ts +4 -2
- package/src/cli/internal/wizard.ts +34 -26
- package/src/cluster/ClusterSchema.ts +32 -0
- package/src/cluster/ClusterWorkflowEngine.ts +64 -36
- package/src/cluster/MessageStorage.ts +45 -14
- package/src/cluster/Runners.ts +8 -2
- package/src/cluster/Sharding.ts +219 -58
- package/src/cluster/SqlMessageStorage.ts +118 -84
- package/src/cluster/SqlRunnerStorage.ts +224 -128
- package/src/cluster/internal/entityManager.ts +151 -55
- package/src/encoding/Sse.ts +69 -79
- package/src/graphql/GraphQL.ts +494 -0
- package/src/graphql/GraphQLClient.ts +912 -0
- package/src/graphql/GraphQLClientError.ts +542 -0
- package/src/graphql/GraphQLGroup.ts +167 -0
- package/src/graphql/GraphQLMiddleware.ts +253 -0
- package/src/graphql/GraphQLProtocol.ts +1286 -0
- package/src/graphql/GraphQLResolver.ts +1010 -0
- package/src/graphql/index.ts +48 -0
- package/src/http/FindMyWay/internal/router.ts +4 -7
- package/src/http/HttpClient.ts +110 -12
- package/src/http/HttpEffect.ts +11 -5
- package/src/http/HttpMiddleware.ts +33 -34
- package/src/http/HttpRouter.ts +35 -0
- package/src/http/HttpServerResponse.ts +28 -16
- package/src/http/HttpTraceContext.ts +8 -2
- package/src/http/Multipart.ts +17 -1
- package/src/http-api/HttpApiScalar.ts +5 -0
- package/src/internal/dateTime.ts +13 -10
- package/src/internal/effect.ts +22 -15
- package/src/internal/graphql.ts +263 -0
- package/src/internal/graphqlHeaders.ts +11 -0
- package/src/internal/request.ts +31 -3
- package/src/internal/schema/toCodeDocument.ts +7 -1
- package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
- package/src/internal/version.ts +1 -1
- package/src/net/AddressResolver.ts +222 -0
- package/src/net/Dns.ts +1035 -0
- package/src/net/Host.ts +544 -0
- package/src/net/IpInterface.ts +117 -7
- package/src/net/IpNetwork.ts +116 -17
- package/src/net/NetAddress.ts +620 -76
- package/src/net/index.ts +18 -0
- package/src/observability/OtlpMetrics.ts +12 -23
- package/src/observability/PrometheusMetrics.ts +1 -0
- package/src/persistence/PersistedQueue.ts +146 -53
- package/src/rpc/RpcClient.ts +41 -19
- package/src/rpc/RpcServer.ts +115 -40
- package/src/socket/DatagramSocket.ts +919 -0
- package/src/socket/Socket.ts +28 -2
- package/src/socket/index.ts +6 -0
- package/src/sql/Migrator.ts +21 -5
- package/src/sql/SqlClient.ts +39 -15
- package/src/testing/TestCrypto.ts +94 -0
- package/src/testing/index.ts +6 -0
- package/src/workflow/DurableDeferred.ts +160 -47
- package/src/workflow/WorkflowEngine.ts +50 -5
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @stability experimental
|
|
3
|
+
* @since 4.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// @barrel: Auto-generated exports. Do not edit manually.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @stability experimental
|
|
10
|
+
* @since 4.0.0
|
|
11
|
+
*/
|
|
12
|
+
export * as GraphQL from "./GraphQL.ts"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @stability experimental
|
|
16
|
+
* @since 4.0.0
|
|
17
|
+
*/
|
|
18
|
+
export * as GraphQLClient from "./GraphQLClient.ts"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @stability experimental
|
|
22
|
+
* @since 4.0.0
|
|
23
|
+
*/
|
|
24
|
+
export * as GraphQLClientError from "./GraphQLClientError.ts"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @stability experimental
|
|
28
|
+
* @since 4.0.0
|
|
29
|
+
*/
|
|
30
|
+
export * as GraphQLGroup from "./GraphQLGroup.ts"
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @stability experimental
|
|
34
|
+
* @since 4.0.0
|
|
35
|
+
*/
|
|
36
|
+
export * as GraphQLMiddleware from "./GraphQLMiddleware.ts"
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @stability experimental
|
|
40
|
+
* @since 4.0.0
|
|
41
|
+
*/
|
|
42
|
+
export * as GraphQLProtocol from "./GraphQLProtocol.ts"
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @stability experimental
|
|
46
|
+
* @since 4.0.0
|
|
47
|
+
*/
|
|
48
|
+
export * as GraphQLResolver from "./GraphQLResolver.ts"
|
|
@@ -338,13 +338,6 @@ class RouterImpl<A> implements Router.Router<A> {
|
|
|
338
338
|
path = path.replace(FULL_PATH_REGEXP, "/")
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
// This must be run before sanitizeUrl as the resulting function
|
|
342
|
-
// .sliceParameter must be constructed with same URL string used
|
|
343
|
-
// throughout the rest of this function.
|
|
344
|
-
if (this.options.ignoreDuplicateSlashes) {
|
|
345
|
-
path = removeDuplicateSlashes(path)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
341
|
let sanitizedUrl
|
|
349
342
|
try {
|
|
350
343
|
sanitizedUrl = safeDecodeURI(path)
|
|
@@ -355,6 +348,10 @@ class RouterImpl<A> implements Router.Router<A> {
|
|
|
355
348
|
return undefined
|
|
356
349
|
}
|
|
357
350
|
|
|
351
|
+
if (this.options.ignoreDuplicateSlashes) {
|
|
352
|
+
path = removeDuplicateSlashes(path)
|
|
353
|
+
}
|
|
354
|
+
|
|
358
355
|
if (this.options.ignoreTrailingSlash) {
|
|
359
356
|
path = trimLastSlash(path)
|
|
360
357
|
}
|
package/src/http/HttpClient.ts
CHANGED
|
@@ -1204,6 +1204,9 @@ export const retry: {
|
|
|
1204
1204
|
* Specifying a `while` predicate allows you to consider other errors as
|
|
1205
1205
|
* transient, and is ignored in "response-only" mode.
|
|
1206
1206
|
*
|
|
1207
|
+
* A `Retry-After` header on a transient response, or on the response of a
|
|
1208
|
+
* status code error, sets the minimum delay before the next attempt.
|
|
1209
|
+
*
|
|
1207
1210
|
* @stability unstable
|
|
1208
1211
|
* @category error handling
|
|
1209
1212
|
* @since 4.0.0
|
|
@@ -1221,6 +1224,9 @@ export const retryTransient: {
|
|
|
1221
1224
|
* Specifying a `while` predicate allows you to consider other errors as
|
|
1222
1225
|
* transient, and is ignored in "response-only" mode.
|
|
1223
1226
|
*
|
|
1227
|
+
* A `Retry-After` header on a transient response, or on the response of a
|
|
1228
|
+
* status code error, sets the minimum delay before the next attempt.
|
|
1229
|
+
*
|
|
1224
1230
|
* @stability unstable
|
|
1225
1231
|
* @category error handling
|
|
1226
1232
|
* @since 4.0.0
|
|
@@ -1257,6 +1263,9 @@ export const retryTransient: {
|
|
|
1257
1263
|
* Specifying a `while` predicate allows you to consider other errors as
|
|
1258
1264
|
* transient, and is ignored in "response-only" mode.
|
|
1259
1265
|
*
|
|
1266
|
+
* A `Retry-After` header on a transient response, or on the response of a
|
|
1267
|
+
* status code error, sets the minimum delay before the next attempt.
|
|
1268
|
+
*
|
|
1260
1269
|
* @stability unstable
|
|
1261
1270
|
* @category error handling
|
|
1262
1271
|
* @since 4.0.0
|
|
@@ -1295,6 +1304,9 @@ export const retryTransient: {
|
|
|
1295
1304
|
* Specifying a `while` predicate allows you to consider other errors as
|
|
1296
1305
|
* transient, and is ignored in "response-only" mode.
|
|
1297
1306
|
*
|
|
1307
|
+
* A `Retry-After` header on a transient response, or on the response of a
|
|
1308
|
+
* status code error, sets the minimum delay before the next attempt.
|
|
1309
|
+
*
|
|
1298
1310
|
* @stability unstable
|
|
1299
1311
|
* @category error handling
|
|
1300
1312
|
* @since 4.0.0
|
|
@@ -1314,6 +1326,9 @@ export const retryTransient: {
|
|
|
1314
1326
|
* Specifying a `while` predicate allows you to consider other errors as
|
|
1315
1327
|
* transient, and is ignored in "response-only" mode.
|
|
1316
1328
|
*
|
|
1329
|
+
* A `Retry-After` header on a transient response, or on the response of a
|
|
1330
|
+
* status code error, sets the minimum delay before the next attempt.
|
|
1331
|
+
*
|
|
1317
1332
|
* @stability unstable
|
|
1318
1333
|
* @category error handling
|
|
1319
1334
|
* @since 4.0.0
|
|
@@ -1341,14 +1356,15 @@ export const retryTransient: {
|
|
|
1341
1356
|
): HttpClient.With<E | ES, R1 | R> => {
|
|
1342
1357
|
const isOnlySchedule = Schedule.isSchedule(options)
|
|
1343
1358
|
const retryOn = isOnlySchedule ? "errors-and-responses" : options.retryOn ?? "errors-and-responses"
|
|
1344
|
-
const schedule =
|
|
1345
|
-
|
|
1359
|
+
const schedule = withRetryAfter(
|
|
1360
|
+
isOnlySchedule ? options : options.schedule ?? Schedule.forever
|
|
1361
|
+
)
|
|
1346
1362
|
const times = isOnlySchedule ? undefined : options.times
|
|
1347
1363
|
return transformResponse(
|
|
1348
1364
|
self,
|
|
1349
1365
|
flow(
|
|
1350
1366
|
retryOn === "errors-only" ? identity : Effect.repeat({
|
|
1351
|
-
schedule
|
|
1367
|
+
schedule,
|
|
1352
1368
|
times,
|
|
1353
1369
|
while: isTransientResponse
|
|
1354
1370
|
}),
|
|
@@ -1466,6 +1482,8 @@ export declare namespace WithRateLimiter {
|
|
|
1466
1482
|
* It can update limits by inspecting common rate limit response headers and
|
|
1467
1483
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1468
1484
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1485
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1486
|
+
* reported reset once that budget is exhausted.
|
|
1469
1487
|
*
|
|
1470
1488
|
* **Gotchas**
|
|
1471
1489
|
*
|
|
@@ -1486,6 +1504,8 @@ export const withRateLimiter: {
|
|
|
1486
1504
|
* It can update limits by inspecting common rate limit response headers and
|
|
1487
1505
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1488
1506
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1507
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1508
|
+
* reported reset once that budget is exhausted.
|
|
1489
1509
|
*
|
|
1490
1510
|
* **Gotchas**
|
|
1491
1511
|
*
|
|
@@ -1508,6 +1528,8 @@ export const withRateLimiter: {
|
|
|
1508
1528
|
* It can update limits by inspecting common rate limit response headers and
|
|
1509
1529
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1510
1530
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1531
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1532
|
+
* reported reset once that budget is exhausted.
|
|
1511
1533
|
*
|
|
1512
1534
|
* **Gotchas**
|
|
1513
1535
|
*
|
|
@@ -1551,14 +1573,36 @@ export const withRateLimiter: {
|
|
|
1551
1573
|
return initialState
|
|
1552
1574
|
}
|
|
1553
1575
|
|
|
1576
|
+
const budgets = new Map<string, RateLimitBudget>()
|
|
1577
|
+
const getBudget = (key: string): RateLimitBudget => {
|
|
1578
|
+
let budget = budgets.get(key)
|
|
1579
|
+
if (budget === undefined) {
|
|
1580
|
+
budget = { sent: 0, observed: 0, remaining: 0, resetAt: 0 }
|
|
1581
|
+
budgets.set(key, budget)
|
|
1582
|
+
}
|
|
1583
|
+
return budget
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1554
1586
|
const onResponse = options.disableResponseInspection
|
|
1555
1587
|
? undefined
|
|
1556
|
-
: (clock: Clock, key: string, headers: Headers.Headers, tokens: number) => {
|
|
1588
|
+
: (clock: Clock, key: string, headers: Headers.Headers, tokens: number, sent: number) => {
|
|
1557
1589
|
const current = getState(key)
|
|
1558
1590
|
const next = parseRateLimiterState(current, clock, headers, tokens, headerNames)
|
|
1559
1591
|
if (next.limit !== current.limit || !Duration.equals(next.window, current.window)) {
|
|
1560
1592
|
states.set(key, next)
|
|
1561
1593
|
}
|
|
1594
|
+
const remaining = parseRateLimitRemaining(headers, headerNames)
|
|
1595
|
+
const budget = getBudget(key)
|
|
1596
|
+
// responses to earlier requests carry stale counts
|
|
1597
|
+
if (remaining === undefined || sent <= budget.observed) {
|
|
1598
|
+
return
|
|
1599
|
+
}
|
|
1600
|
+
const resetAfter = parseRateLimitWindow(clock, headers, headerNames)
|
|
1601
|
+
budget.observed = sent
|
|
1602
|
+
budget.remaining = remaining
|
|
1603
|
+
if (resetAfter !== undefined) {
|
|
1604
|
+
budget.resetAt = clock.currentTimeMillisUnsafe() + Duration.toMillis(resetAfter)
|
|
1605
|
+
}
|
|
1562
1606
|
}
|
|
1563
1607
|
|
|
1564
1608
|
return transform(self, function loop(effect, request, retries = 0): Effect.Effect<
|
|
@@ -1570,6 +1614,15 @@ export const withRateLimiter: {
|
|
|
1570
1614
|
const clock = fiber.getRef(Clock)
|
|
1571
1615
|
const key = resolveKey(request)
|
|
1572
1616
|
const tokens = Math.max(resolveTokens(request), 1)
|
|
1617
|
+
const budget = getBudget(key)
|
|
1618
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
1619
|
+
// requests admitted after the observed one are not reflected in its count
|
|
1620
|
+
if (budget.resetAt > now && budget.remaining - (budget.sent - budget.observed) < tokens) {
|
|
1621
|
+
return Effect.flatMap(Effect.sleep(budget.resetAt - now), () => loop(effect, request, retries))
|
|
1622
|
+
}
|
|
1623
|
+
// counted at admission, so requests already admitted are sent even if a
|
|
1624
|
+
// later response exhausts the budget
|
|
1625
|
+
const sent = budget.sent += tokens
|
|
1573
1626
|
const current = getState(key)
|
|
1574
1627
|
const canRetry = options.times === undefined || retries < options.times
|
|
1575
1628
|
function retry(retryAfter: Duration.Duration | undefined) {
|
|
@@ -1581,7 +1634,7 @@ export const withRateLimiter: {
|
|
|
1581
1634
|
response: HttpClientResponse.HttpClientResponse,
|
|
1582
1635
|
adaptive: RateLimiter.AdaptiveConsumeResult | undefined
|
|
1583
1636
|
) => {
|
|
1584
|
-
onResponse?.(clock, key, response.headers, tokens)
|
|
1637
|
+
onResponse?.(clock, key, response.headers, tokens, sent)
|
|
1585
1638
|
if (options.disableResponseInspection || response.status !== 429) {
|
|
1586
1639
|
return Effect.succeed</**
|
|
1587
1640
|
* Applies request rate limiting using the `RateLimiter` service.
|
|
@@ -1591,6 +1644,8 @@ export const withRateLimiter: {
|
|
|
1591
1644
|
* It can update limits by inspecting common rate limit response headers and
|
|
1592
1645
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1593
1646
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1647
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1648
|
+
* reported reset once that budget is exhausted.
|
|
1594
1649
|
*
|
|
1595
1650
|
* **Gotchas**
|
|
1596
1651
|
*
|
|
@@ -1604,7 +1659,10 @@ export const withRateLimiter: {
|
|
|
1604
1659
|
*/
|
|
1605
1660
|
Duration.Duration | undefined>(undefined);
|
|
1606
1661
|
}
|
|
1607
|
-
const retryAfter = parseRetryAfter(
|
|
1662
|
+
const retryAfter = parseRetryAfter(
|
|
1663
|
+
clock.currentTimeMillisUnsafe(),
|
|
1664
|
+
getHeader(response.headers, ...headerNames.retryAfter)
|
|
1665
|
+
)
|
|
1608
1666
|
if (retryAfter === undefined) {
|
|
1609
1667
|
return Effect.succeed</**
|
|
1610
1668
|
* Applies request rate limiting using the `RateLimiter` service.
|
|
@@ -1614,6 +1672,8 @@ export const withRateLimiter: {
|
|
|
1614
1672
|
* It can update limits by inspecting common rate limit response headers and
|
|
1615
1673
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1616
1674
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1675
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1676
|
+
* reported reset once that budget is exhausted.
|
|
1617
1677
|
*
|
|
1618
1678
|
* **Gotchas**
|
|
1619
1679
|
*
|
|
@@ -1637,6 +1697,8 @@ export const withRateLimiter: {
|
|
|
1637
1697
|
* It can update limits by inspecting common rate limit response headers and
|
|
1638
1698
|
* automatically retries HTTP `429` responses (or `HttpClientError` values
|
|
1639
1699
|
* wrapping a `429` response) by forcing the retry back through the limiter.
|
|
1700
|
+
* When a response reports the remaining budget, requests wait for the
|
|
1701
|
+
* reported reset once that budget is exhausted.
|
|
1640
1702
|
*
|
|
1641
1703
|
* **Gotchas**
|
|
1642
1704
|
*
|
|
@@ -1756,6 +1818,17 @@ interface RateLimiterState {
|
|
|
1756
1818
|
readonly initial: boolean
|
|
1757
1819
|
}
|
|
1758
1820
|
|
|
1821
|
+
interface RateLimitBudget {
|
|
1822
|
+
/** Total tokens admitted for the key. */
|
|
1823
|
+
sent: number
|
|
1824
|
+
/** The `sent` total of the request whose remaining count was last applied. */
|
|
1825
|
+
observed: number
|
|
1826
|
+
/** The remaining count reported in response to the `observed` request. */
|
|
1827
|
+
remaining: number
|
|
1828
|
+
/** When the server budget resets, in epoch milliseconds. */
|
|
1829
|
+
resetAt: number
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1759
1832
|
const parseRateLimiterState = (
|
|
1760
1833
|
state: RateLimiterState,
|
|
1761
1834
|
clock: Clock,
|
|
@@ -1811,21 +1884,24 @@ const parseRateLimitWindow = (
|
|
|
1811
1884
|
}
|
|
1812
1885
|
|
|
1813
1886
|
const parseRetryAfter = (
|
|
1814
|
-
|
|
1887
|
+
nowMillis: number,
|
|
1815
1888
|
value: string | undefined
|
|
1816
1889
|
): Duration.Duration | undefined => {
|
|
1817
1890
|
if (value === undefined) {
|
|
1818
1891
|
return undefined
|
|
1819
1892
|
}
|
|
1820
|
-
const
|
|
1821
|
-
if (
|
|
1822
|
-
|
|
1893
|
+
const trimmed = value.trim()
|
|
1894
|
+
if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
1895
|
+
const numeric = Number(trimmed)
|
|
1896
|
+
return Number.isFinite(numeric)
|
|
1897
|
+
? Duration.max(Duration.seconds(numeric), Duration.millis(1))
|
|
1898
|
+
: undefined
|
|
1823
1899
|
}
|
|
1824
|
-
const parsedDate = Date.parse(
|
|
1900
|
+
const parsedDate = Date.parse(trimmed)
|
|
1825
1901
|
if (Number.isNaN(parsedDate)) {
|
|
1826
1902
|
return undefined
|
|
1827
1903
|
}
|
|
1828
|
-
const millis = parsedDate -
|
|
1904
|
+
const millis = parsedDate - nowMillis
|
|
1829
1905
|
if (millis <= 0) {
|
|
1830
1906
|
return Duration.millis(1)
|
|
1831
1907
|
}
|
|
@@ -2363,6 +2439,28 @@ const isTransientHttpError = (error: unknown) =>
|
|
|
2363
2439
|
(error.reason._tag === "TransportError" ||
|
|
2364
2440
|
(error.reason._tag === "StatusCodeError" && isTransientResponse(error.reason.response)))
|
|
2365
2441
|
|
|
2442
|
+
const retryAfterOf = (input: unknown, nowMillis: number): Duration.Duration | undefined => {
|
|
2443
|
+
const response = Error.isHttpClientError(input)
|
|
2444
|
+
? input.response
|
|
2445
|
+
: Predicate.hasProperty(input, HttpClientResponse.TypeId)
|
|
2446
|
+
? input as HttpClientResponse.HttpClientResponse
|
|
2447
|
+
: undefined
|
|
2448
|
+
return parseRetryAfter(nowMillis, response?.headers["retry-after"])
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
const withRetryAfter = <ES, R1>(
|
|
2452
|
+
schedule: Schedule.Schedule<unknown, any, ES, R1>
|
|
2453
|
+
): Schedule.Schedule<unknown, any, ES, R1> =>
|
|
2454
|
+
Schedule.fromStep(
|
|
2455
|
+
Effect.map(Schedule.toStep(schedule), (step) => (now: number, input: unknown) => {
|
|
2456
|
+
const retryAfter = retryAfterOf(input, now)
|
|
2457
|
+
if (retryAfter === undefined) {
|
|
2458
|
+
return step(now, input)
|
|
2459
|
+
}
|
|
2460
|
+
return Effect.map(step(now, input), ([output, delay]) => [output, Duration.max(delay, retryAfter)])
|
|
2461
|
+
})
|
|
2462
|
+
)
|
|
2463
|
+
|
|
2366
2464
|
const isTooManyRequestsHttpClientError = (
|
|
2367
2465
|
error: unknown
|
|
2368
2466
|
): error is Error.HttpClientError & { readonly reason: Error.StatusCodeError } =>
|
package/src/http/HttpEffect.ts
CHANGED
|
@@ -347,10 +347,11 @@ export const toWebHandlerWith = <Provided, R = never, ReqR = Exclude<R, Provided
|
|
|
347
347
|
const httpApp = toHandled(self, (request, response) => {
|
|
348
348
|
const withoutBody = request.method === "HEAD"
|
|
349
349
|
if (!Response.omitsBody(response, withoutBody)) {
|
|
350
|
+
// The transferred request scope closes when an abort interrupts the body, even if it is no longer read.
|
|
350
351
|
response = scopeTransferToStream(response)
|
|
351
352
|
}
|
|
352
353
|
;(request as any)[resolveSymbol](
|
|
353
|
-
Response.toWeb(response, { withoutBody, context })
|
|
354
|
+
Response.toWeb(response, { withoutBody, context, signal: (request.source as globalThis.Request).signal })
|
|
354
355
|
)
|
|
355
356
|
return Effect.void
|
|
356
357
|
}, middleware)
|
|
@@ -366,10 +367,15 @@ export const toWebHandlerWith = <Provided, R = never, ReqR = Exclude<R, Provided
|
|
|
366
367
|
reqContext = Context.add(reqContext, HttpServerRequest, httpServerRequest)
|
|
367
368
|
;(httpServerRequest as any)[resolveSymbol] = resolve
|
|
368
369
|
const fiber = Effect.runForkWith(reqContext)(httpApp as any)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
const signal = request.signal
|
|
371
|
+
if (fiber.pollUnsafe() === undefined && signal) {
|
|
372
|
+
const onAbort = () => fiber.interruptUnsafe(undefined, ClientAbort.annotation)
|
|
373
|
+
if (signal.aborted) {
|
|
374
|
+
onAbort()
|
|
375
|
+
} else {
|
|
376
|
+
signal.addEventListener("abort", onAbort, { once: true })
|
|
377
|
+
fiber.addObserver(() => signal.removeEventListener("abort", onAbort))
|
|
378
|
+
}
|
|
373
379
|
}
|
|
374
380
|
})
|
|
375
381
|
}
|
|
@@ -268,42 +268,41 @@ export const tracer: <E, R>(
|
|
|
268
268
|
}
|
|
269
269
|
const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames)
|
|
270
270
|
const headerFilter = fiber.getRef(TracerHeaderFilter)
|
|
271
|
-
fiber
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
tracing.addUrlAttributes(span, url.value)
|
|
294
|
-
}
|
|
295
|
-
if (request.headers["user-agent"] !== undefined) {
|
|
296
|
-
span.attribute("user_agent.original", request.headers["user-agent"])
|
|
297
|
-
}
|
|
298
|
-
tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames)
|
|
299
|
-
if (Option.isSome(request.remoteAddress)) {
|
|
300
|
-
span.attribute("client.address", request.remoteAddress.value)
|
|
271
|
+
// Complete the span before the request fiber exits so shutdown can flush it.
|
|
272
|
+
let response: HttpServerResponse
|
|
273
|
+
let spanExit: Exit.Exit<HttpServerResponse, unknown>
|
|
274
|
+
if (Exit.isFailure(exit)) {
|
|
275
|
+
const [failureResponse, cause] = causeResponseStripped(exit.cause)
|
|
276
|
+
response = failureResponse
|
|
277
|
+
// OpenTelemetry only treats 5xx responses as server span errors; interrupts still fail the span.
|
|
278
|
+
spanExit = Option.isSome(cause) && (response.status >= 500 || Cause.hasInterrupts(cause.value))
|
|
279
|
+
? Exit.failCause(cause.value)
|
|
280
|
+
: responseSpanExit(request, response)
|
|
281
|
+
} else {
|
|
282
|
+
response = exit.value
|
|
283
|
+
spanExit = responseSpanExit(request, response)
|
|
284
|
+
}
|
|
285
|
+
if (span.sampled) {
|
|
286
|
+
span.attribute("http.request.method", request.method)
|
|
287
|
+
const url = Request.toURL(request)
|
|
288
|
+
if (Option.isSome(url)) {
|
|
289
|
+
if (!request.url.startsWith("/") && (url.value.username !== "" || url.value.password !== "")) {
|
|
290
|
+
url.value.username = "REDACTED"
|
|
291
|
+
url.value.password = "REDACTED"
|
|
301
292
|
}
|
|
302
|
-
|
|
303
|
-
tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames)
|
|
293
|
+
tracing.addUrlAttributes(span, url.value)
|
|
304
294
|
}
|
|
305
|
-
|
|
306
|
-
|
|
295
|
+
if (request.headers["user-agent"] !== undefined) {
|
|
296
|
+
span.attribute("user_agent.original", request.headers["user-agent"])
|
|
297
|
+
}
|
|
298
|
+
tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames)
|
|
299
|
+
if (Option.isSome(request.remoteAddress)) {
|
|
300
|
+
span.attribute("client.address", request.remoteAddress.value)
|
|
301
|
+
}
|
|
302
|
+
span.attribute("http.response.status_code", response.status)
|
|
303
|
+
tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames)
|
|
304
|
+
}
|
|
305
|
+
span.end(endTime, spanExit)
|
|
307
306
|
return undefined
|
|
308
307
|
})
|
|
309
308
|
return httpApp
|
package/src/http/HttpRouter.ts
CHANGED
|
@@ -1410,6 +1410,41 @@ export const serve = <A, E, R, HE, HR = Request.Only<"Requires", R> | Request.On
|
|
|
1410
1410
|
* request arrives, in which case that request waits for the build to finish.
|
|
1411
1411
|
* If the build fails, every request rejects with the build error.
|
|
1412
1412
|
*
|
|
1413
|
+
* **Gotchas**
|
|
1414
|
+
*
|
|
1415
|
+
* On Cloudflare Workers, build the handler at module scope: fibers started
|
|
1416
|
+
* inside `fetch` capture that request's async context. Layer construction
|
|
1417
|
+
* must not require request-time I/O; bindings are available from
|
|
1418
|
+
* `cloudflare:workers` via its `env` export.
|
|
1419
|
+
* Prefer request-local work or a Durable Object's supported lifecycle APIs,
|
|
1420
|
+
* not indefinite background execution. For shared fibers, provide a sync
|
|
1421
|
+
* scheduler to both layers and requests as below; see `Scheduler.MixedScheduler`
|
|
1422
|
+
* for its limitations. Bound waits for shared work with request-local timeouts.
|
|
1423
|
+
*
|
|
1424
|
+
* **Example** (Opting in to the sync scheduler on Workers)
|
|
1425
|
+
*
|
|
1426
|
+
* ```ts
|
|
1427
|
+
* import { Effect, Layer, Scheduler } from "effect"
|
|
1428
|
+
* import { HttpRouter, HttpServer, HttpServerResponse } from "effect/http"
|
|
1429
|
+
*
|
|
1430
|
+
* const SyncScheduler = Layer.succeed(
|
|
1431
|
+
* Scheduler.Scheduler,
|
|
1432
|
+
* new Scheduler.MixedScheduler("sync")
|
|
1433
|
+
* )
|
|
1434
|
+
* const AppLive = HttpRouter.add(
|
|
1435
|
+
* "GET",
|
|
1436
|
+
* "/ping",
|
|
1437
|
+
* Effect.succeed(HttpServerResponse.text("pong"))
|
|
1438
|
+
* ).pipe(Layer.provide(HttpServer.layerServices))
|
|
1439
|
+
*
|
|
1440
|
+
* // Build once at module scope. The scheduler is also retained for requests.
|
|
1441
|
+
* const app = HttpRouter.toWebHandler(
|
|
1442
|
+
* AppLive.pipe(Layer.provideMerge(SyncScheduler))
|
|
1443
|
+
* )
|
|
1444
|
+
*
|
|
1445
|
+
* export default { fetch: (request: Request) => app.handler(request) }
|
|
1446
|
+
* ```
|
|
1447
|
+
*
|
|
1413
1448
|
* @stability unstable
|
|
1414
1449
|
* @category converting
|
|
1415
1450
|
* @since 4.0.0
|
|
@@ -1346,23 +1346,35 @@ export const setStatus: {
|
|
|
1346
1346
|
export const omitsBody = (response: HttpServerResponse, withoutBody = false): boolean =>
|
|
1347
1347
|
withoutBody || response.status === 204 || response.status === 205 || response.status === 304
|
|
1348
1348
|
|
|
1349
|
+
const mergeHeaders = (target: globalThis.Headers, headers: globalThis.Headers): globalThis.Headers => {
|
|
1350
|
+
for (const [key, value] of headers as any) {
|
|
1351
|
+
if (key === "set-cookie") {
|
|
1352
|
+
target.append(key, value)
|
|
1353
|
+
} else {
|
|
1354
|
+
target.set(key, value)
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
return target
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1349
1360
|
/**
|
|
1350
1361
|
* Converts an `HttpServerResponse` to a Web `Response`.
|
|
1351
1362
|
*
|
|
1352
1363
|
* **Details**
|
|
1353
1364
|
*
|
|
1354
1365
|
* Cookies are appended as `Set-Cookie` headers. Stream bodies are converted using
|
|
1355
|
-
* the supplied context.
|
|
1366
|
+
* the supplied context, and are interrupted when `signal` aborts.
|
|
1356
1367
|
*
|
|
1357
1368
|
* Bodies are omitted for statuses 204, 205, and 304, or when `withoutBody` is true
|
|
1358
1369
|
* (for example, for HEAD responses). Omitted raw `ReadableStream` bodies are
|
|
1359
1370
|
* cancelled without awaiting completion, and cancellation errors are ignored.
|
|
1360
1371
|
*
|
|
1361
|
-
* A raw `Response`
|
|
1362
|
-
*
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1372
|
+
* A raw `Response` is returned as-is when there are no outer headers or cookies
|
|
1373
|
+
* to merge. Otherwise, they are merged into a copy without modifying the raw
|
|
1374
|
+
* response: outer headers replace native ones, and cookies are appended. When
|
|
1375
|
+
* its body is omitted, the bodyless `Response` keeps those merged
|
|
1376
|
+
* headers and uses the outer status for 204, 205 and 304, or the raw `Response`
|
|
1377
|
+
* status otherwise.
|
|
1366
1378
|
*
|
|
1367
1379
|
* @stability unstable
|
|
1368
1380
|
* @category converting
|
|
@@ -1373,6 +1385,7 @@ export const toWeb = (
|
|
|
1373
1385
|
options?: {
|
|
1374
1386
|
readonly withoutBody?: boolean | undefined
|
|
1375
1387
|
readonly context?: Context.Context<never> | undefined
|
|
1388
|
+
readonly signal?: AbortSignal | undefined
|
|
1376
1389
|
}
|
|
1377
1390
|
): Response => {
|
|
1378
1391
|
const headers = new globalThis.Headers(response.headers)
|
|
@@ -1387,15 +1400,13 @@ export const toWeb = (
|
|
|
1387
1400
|
const withoutBody = outerOmitsBody || options?.withoutBody === true
|
|
1388
1401
|
if (body._tag === "Raw" && body.body instanceof Response) {
|
|
1389
1402
|
const raw = body.body
|
|
1390
|
-
for (const [key, value] of headers as any) {
|
|
1391
|
-
if (key === "set-cookie") {
|
|
1392
|
-
raw.headers.append(key, value)
|
|
1393
|
-
} else {
|
|
1394
|
-
raw.headers.set(key, value)
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
1403
|
if (!withoutBody) {
|
|
1398
|
-
|
|
1404
|
+
if (headers.keys().next().done) {
|
|
1405
|
+
return raw
|
|
1406
|
+
}
|
|
1407
|
+
const copy = new Response(raw.body, raw)
|
|
1408
|
+
mergeHeaders(copy.headers, headers)
|
|
1409
|
+
return copy
|
|
1399
1410
|
}
|
|
1400
1411
|
// A bodyless outer status is what the caller chose to send; otherwise the
|
|
1401
1412
|
// body is only omitted for HEAD and the raw Response describes the resource.
|
|
@@ -1403,7 +1414,7 @@ export const toWeb = (
|
|
|
1403
1414
|
return new Response(undefined, {
|
|
1404
1415
|
status: source.status,
|
|
1405
1416
|
statusText: source.statusText as string,
|
|
1406
|
-
headers: raw.headers
|
|
1417
|
+
headers: mergeHeaders(new globalThis.Headers(raw.headers), headers)
|
|
1407
1418
|
})
|
|
1408
1419
|
}
|
|
1409
1420
|
if (withoutBody) {
|
|
@@ -1449,7 +1460,8 @@ export const toWeb = (
|
|
|
1449
1460
|
return new Response(
|
|
1450
1461
|
Stream.toReadableStreamWith(
|
|
1451
1462
|
body.stream,
|
|
1452
|
-
options?.context ?? Context.empty()
|
|
1463
|
+
options?.context ?? Context.empty(),
|
|
1464
|
+
{ signal: options?.signal }
|
|
1453
1465
|
),
|
|
1454
1466
|
{
|
|
1455
1467
|
status: response.status,
|
|
@@ -93,7 +93,7 @@ export const b3: FromHeaders = (headers) => {
|
|
|
93
93
|
return Option.none()
|
|
94
94
|
}
|
|
95
95
|
const parts = headers["b3"].split("-")
|
|
96
|
-
if (parts.length < 2) {
|
|
96
|
+
if (parts.length < 2 || isB3Ids(parts[0], parts[1]) === false) {
|
|
97
97
|
return Option.none()
|
|
98
98
|
}
|
|
99
99
|
return Option.some(Tracer.externalSpan({
|
|
@@ -116,7 +116,10 @@ export const b3: FromHeaders = (headers) => {
|
|
|
116
116
|
* @since 4.0.0
|
|
117
117
|
*/
|
|
118
118
|
export const xb3: FromHeaders = (headers) => {
|
|
119
|
-
if (
|
|
119
|
+
if (
|
|
120
|
+
!(headers["x-b3-traceid"]) || !(headers["x-b3-spanid"]) ||
|
|
121
|
+
isB3Ids(headers["x-b3-traceid"], headers["x-b3-spanid"]) === false
|
|
122
|
+
) {
|
|
120
123
|
return Option.none()
|
|
121
124
|
}
|
|
122
125
|
return Option.some(Tracer.externalSpan({
|
|
@@ -130,6 +133,9 @@ export const xb3: FromHeaders = (headers) => {
|
|
|
130
133
|
const w3cTraceId = /^[0-9a-f]{32}$/i
|
|
131
134
|
const w3cSpanId = /^[0-9a-f]{16}$/i
|
|
132
135
|
|
|
136
|
+
const b3TraceId = /^(?:[0-9a-f]{16}|[0-9a-f]{32})$/i
|
|
137
|
+
const isB3Ids = (traceId: string, spanId: string): boolean => b3TraceId.test(traceId) && w3cSpanId.test(spanId)
|
|
138
|
+
|
|
133
139
|
/**
|
|
134
140
|
* Decodes an external span safely from the W3C `traceparent` header.
|
|
135
141
|
*
|
package/src/http/Multipart.ts
CHANGED
|
@@ -689,6 +689,22 @@ const defaultWriteFile = (path: string, file: File) =>
|
|
|
689
689
|
(fs) => Effect.mapError(Stream.run(file.content, fs.sink(path)), toMultipartError)
|
|
690
690
|
)
|
|
691
691
|
|
|
692
|
+
const maxPersistedFileNameBytes = 128
|
|
693
|
+
const fileNameEncoder = new TextEncoder()
|
|
694
|
+
|
|
695
|
+
// Keeps the trailing 128 UTF-8 bytes without splitting code points. Empty and dot names fall back to "file".
|
|
696
|
+
const persistedFileName = (baseName: string): string => {
|
|
697
|
+
const codePoints = Array.from(baseName)
|
|
698
|
+
let fileName = ""
|
|
699
|
+
let bytes = 0
|
|
700
|
+
for (let i = codePoints.length - 1; i >= 0; i--) {
|
|
701
|
+
bytes += fileNameEncoder.encode(codePoints[i]).length
|
|
702
|
+
if (bytes > maxPersistedFileNameBytes) break
|
|
703
|
+
fileName = codePoints[i] + fileName
|
|
704
|
+
}
|
|
705
|
+
return fileName === "" || fileName === "." || fileName === ".." ? "file" : fileName
|
|
706
|
+
}
|
|
707
|
+
|
|
692
708
|
/**
|
|
693
709
|
* Runs a channel of byte chunks and collects all output into a single
|
|
694
710
|
* `Uint8Array`.
|
|
@@ -748,7 +764,7 @@ export const toPersisted = (
|
|
|
748
764
|
return Effect.void
|
|
749
765
|
}
|
|
750
766
|
const file = part
|
|
751
|
-
const fileName = path_.basename(file.name)
|
|
767
|
+
const fileName = persistedFileName(path_.basename(file.name))
|
|
752
768
|
let path = path_.join(dir, fileName)
|
|
753
769
|
while (usedPaths.has(path)) {
|
|
754
770
|
path = path_.join(dir, `${fileIndex++}-${fileName}`)
|
|
@@ -60,6 +60,11 @@ export type ScalarConfig = {
|
|
|
60
60
|
layout?: "modern" | "classic"
|
|
61
61
|
/** URL to a request proxy for the API client */
|
|
62
62
|
proxyUrl?: string
|
|
63
|
+
/** Authentication preferences for the API client */
|
|
64
|
+
authentication?: {
|
|
65
|
+
/** OpenAPI security scheme names to select by default */
|
|
66
|
+
preferredSecurityScheme?: string | Array<string | Array<string>> | null
|
|
67
|
+
}
|
|
63
68
|
/** Browser JavaScript function expression used by Scalar for documents and test requests */
|
|
64
69
|
customFetch?: string
|
|
65
70
|
/** Whether to show the sidebar */
|