effect 4.0.3 → 4.0.5
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 +117 -0
- package/ai-docs/src/52_graphql/20_testing.ts +45 -0
- package/ai-docs/src/52_graphql/30_batching.ts +118 -0
- package/ai-docs/src/52_graphql/40_aliased-batching.ts +124 -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/Array.d.ts +90 -6
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -2
- package/dist/Array.js.map +1 -1
- package/dist/Channel.d.ts +6 -1
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +17 -3
- package/dist/Channel.js.map +1 -1
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +33 -32
- package/dist/ConfigProvider.js.map +1 -1
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +9 -0
- package/dist/Context.js.map +1 -1
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +2 -5
- package/dist/Duration.js.map +1 -1
- package/dist/Effect.d.ts +61 -19
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +58 -39
- package/dist/Effect.js.map +1 -1
- package/dist/FileSystem.d.ts +23 -9
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js.map +1 -1
- package/dist/Match.d.ts +20 -6
- package/dist/Match.d.ts.map +1 -1
- package/dist/Match.js +2 -2
- package/dist/Match.js.map +1 -1
- package/dist/Path.d.ts +37 -5
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +36 -5
- package/dist/Path.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 +15 -2
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +16 -2
- 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/Stream.d.ts +11 -0
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +16 -1
- package/dist/Stream.js.map +1 -1
- package/dist/String.d.ts +12 -0
- package/dist/String.d.ts.map +1 -1
- package/dist/String.js +17 -4
- package/dist/String.js.map +1 -1
- package/dist/TxRef.d.ts.map +1 -1
- package/dist/TxRef.js +3 -2
- package/dist/TxRef.js.map +1 -1
- package/dist/Utils.js +8 -3
- package/dist/Utils.js.map +1 -1
- package/dist/ai/AiError.d.ts +10 -10
- package/dist/ai/AiError.d.ts.map +1 -1
- package/dist/ai/AiError.js +1 -1
- package/dist/ai/AiError.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/Decision.d.ts +70 -19
- package/dist/ai/Decision.d.ts.map +1 -1
- package/dist/ai/Decision.js +37 -3
- package/dist/ai/Decision.js.map +1 -1
- package/dist/ai/DecisionModel.js +1 -1
- package/dist/ai/DecisionModel.js.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 +70 -40
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpServer.d.ts +61 -52
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +134 -50
- 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 +5 -4
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js.map +1 -1
- package/dist/cli/Completions.d.ts +5 -0
- package/dist/cli/Completions.d.ts.map +1 -1
- package/dist/cli/Completions.js.map +1 -1
- package/dist/cli/Flag.d.ts +100 -0
- package/dist/cli/Flag.d.ts.map +1 -1
- package/dist/cli/Flag.js +33 -0
- package/dist/cli/Flag.js.map +1 -1
- package/dist/cli/Param.d.ts +87 -0
- package/dist/cli/Param.d.ts.map +1 -1
- package/dist/cli/Param.js +30 -0
- package/dist/cli/Param.js.map +1 -1
- package/dist/cli/internal/command.d.ts.map +1 -1
- package/dist/cli/internal/command.js +2 -1
- package/dist/cli/internal/command.js.map +1 -1
- package/dist/cli/internal/completions/bash.js +4 -3
- package/dist/cli/internal/completions/bash.js.map +1 -1
- package/dist/cli/internal/completions/descriptor.js +8 -1
- package/dist/cli/internal/completions/descriptor.js.map +1 -1
- package/dist/cli/internal/completions/fish.js +4 -4
- package/dist/cli/internal/completions/fish.js.map +1 -1
- package/dist/cli/internal/completions/powershell.js +3 -2
- package/dist/cli/internal/completions/powershell.js.map +1 -1
- package/dist/cli/internal/completions/zsh.js +7 -3
- package/dist/cli/internal/completions/zsh.js.map +1 -1
- package/dist/cli/internal/parser.js +11 -3
- package/dist/cli/internal/parser.js.map +1 -1
- package/dist/cli/internal/wizard.d.ts.map +1 -1
- package/dist/cli/internal/wizard.js +2 -1
- package/dist/cli/internal/wizard.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts +1 -0
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +113 -21
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +20 -12
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +54 -34
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +6 -4
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +1 -1
- 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 +63 -71
- 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 +505 -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 +969 -0
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
- package/dist/graphql/GraphQLProtocol.js +809 -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 +582 -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/FetchHttpClient.d.ts +6 -3
- package/dist/http/FetchHttpClient.d.ts.map +1 -1
- package/dist/http/FetchHttpClient.js +6 -3
- package/dist/http/FetchHttpClient.js.map +1 -1
- package/dist/http/FindMyWay/internal/router.js +2 -1
- package/dist/http/FindMyWay/internal/router.js.map +1 -1
- package/dist/http/Headers.d.ts +3 -3
- package/dist/http/Headers.d.ts.map +1 -1
- package/dist/http/Headers.js +6 -5
- package/dist/http/Headers.js.map +1 -1
- package/dist/http/HttpClient.d.ts +27 -0
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +38 -11
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpClientError.d.ts.map +1 -1
- package/dist/http/HttpClientError.js +8 -6
- package/dist/http/HttpClientError.js.map +1 -1
- package/dist/http/HttpClientRequest.d.ts +147 -4
- package/dist/http/HttpClientRequest.d.ts.map +1 -1
- package/dist/http/HttpClientRequest.js +47 -1
- package/dist/http/HttpClientRequest.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 +60 -0
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +93 -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 +87 -13
- package/dist/http/HttpRouter.js.map +1 -1
- package/dist/http/HttpServerError.d.ts.map +1 -1
- package/dist/http/HttpServerError.js +6 -4
- package/dist/http/HttpServerError.js.map +1 -1
- package/dist/http/HttpServerRequest.js +2 -2
- package/dist/http/HttpServerRequest.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/HttpStaticServer.d.ts.map +1 -1
- package/dist/http/HttpStaticServer.js +4 -10
- package/dist/http/HttpStaticServer.js.map +1 -1
- package/dist/http/HttpTraceContext.js +1 -1
- 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/Url.d.ts.map +1 -1
- package/dist/http/Url.js +2 -1
- package/dist/http/Url.js.map +1 -1
- package/dist/http/UrlParams.d.ts +112 -8
- package/dist/http/UrlParams.d.ts.map +1 -1
- package/dist/http/UrlParams.js +141 -14
- package/dist/http/UrlParams.js.map +1 -1
- package/dist/http/internal/compression.d.ts +1 -1
- package/dist/http/internal/compression.d.ts.map +1 -1
- package/dist/http/internal/compression.js +2 -10
- package/dist/http/internal/compression.js.map +1 -1
- package/dist/http/internal/compressionBody.d.ts +2 -0
- package/dist/http/internal/compressionBody.d.ts.map +1 -0
- package/dist/http/internal/compressionBody.js +36 -0
- package/dist/http/internal/compressionBody.js.map +1 -0
- package/dist/http/internal/decompression.d.ts +2 -0
- package/dist/http/internal/decompression.d.ts.map +1 -0
- package/dist/http/internal/decompression.js +162 -0
- package/dist/http/internal/decompression.js.map +1 -0
- package/dist/http/internal/tracing.js +14 -4
- package/dist/http/internal/tracing.js.map +1 -1
- package/dist/http/internal/urlParams.d.ts +2 -0
- package/dist/http/internal/urlParams.d.ts.map +1 -0
- package/dist/http/internal/urlParams.js +4 -0
- package/dist/http/internal/urlParams.js.map +1 -0
- package/dist/http-api/HttpApiClient.d.ts.map +1 -1
- package/dist/http-api/HttpApiClient.js +2 -1
- package/dist/http-api/HttpApiClient.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/core.js +17 -1
- package/dist/internal/core.js.map +1 -1
- package/dist/internal/dateTime.js +40 -35
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +13 -6
- 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 +205 -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/Host.d.ts +49 -0
- package/dist/net/Host.d.ts.map +1 -1
- package/dist/net/Host.js +55 -0
- package/dist/net/Host.js.map +1 -1
- package/dist/net/NetAddress.d.ts +8 -0
- package/dist/net/NetAddress.d.ts.map +1 -1
- package/dist/net/NetAddress.js +8 -0
- package/dist/net/NetAddress.js.map +1 -1
- package/dist/observability/Otlp.d.ts +15 -0
- package/dist/observability/Otlp.d.ts.map +1 -1
- package/dist/observability/Otlp.js +3 -0
- package/dist/observability/Otlp.js.map +1 -1
- package/dist/observability/OtlpExporter.d.ts +8 -0
- package/dist/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/observability/OtlpExporter.js +10 -1
- package/dist/observability/OtlpExporter.js.map +1 -1
- package/dist/observability/OtlpLogger.d.ts +17 -0
- package/dist/observability/OtlpLogger.d.ts.map +1 -1
- package/dist/observability/OtlpLogger.js +14 -1
- package/dist/observability/OtlpLogger.js.map +1 -1
- package/dist/observability/OtlpMetrics.d.ts +17 -0
- package/dist/observability/OtlpMetrics.d.ts.map +1 -1
- package/dist/observability/OtlpMetrics.js +13 -0
- package/dist/observability/OtlpMetrics.js.map +1 -1
- package/dist/observability/OtlpTracer.d.ts +17 -0
- package/dist/observability/OtlpTracer.d.ts.map +1 -1
- package/dist/observability/OtlpTracer.js +15 -2
- package/dist/observability/OtlpTracer.js.map +1 -1
- package/dist/reactivity/AsyncResult.d.ts +6 -0
- package/dist/reactivity/AsyncResult.d.ts.map +1 -1
- package/dist/reactivity/AsyncResult.js +27 -0
- package/dist/reactivity/AsyncResult.js.map +1 -1
- package/dist/reactivity/AtomRegistry.js +3 -1
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +6 -1
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +36 -9
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/socket/Socket.d.ts +6 -0
- package/dist/socket/Socket.d.ts.map +1 -1
- package/dist/socket/Socket.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +8 -7
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +8 -7
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/testing/TestClock.d.ts +16 -3
- package/dist/testing/TestClock.d.ts.map +1 -1
- package/dist/testing/TestClock.js +112 -22
- package/dist/testing/TestClock.js.map +1 -1
- package/dist/workflow/Activity.js +1 -0
- package/dist/workflow/Activity.js.map +1 -1
- package/dist/workflow/DurableDeferred.d.ts.map +1 -1
- package/dist/workflow/DurableDeferred.js +32 -3
- package/dist/workflow/DurableDeferred.js.map +1 -1
- package/dist/workflow/Workflow.d.ts +22 -0
- package/dist/workflow/Workflow.d.ts.map +1 -1
- package/dist/workflow/Workflow.js +15 -3
- package/dist/workflow/Workflow.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts +44 -3
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +45 -22
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +6 -3
- package/src/Array.ts +95 -6
- package/src/Channel.ts +24 -3
- package/src/ConfigProvider.ts +40 -44
- package/src/Context.ts +9 -0
- package/src/Duration.ts +4 -5
- package/src/Effect.ts +140 -53
- package/src/FileSystem.ts +23 -9
- package/src/Match.ts +65 -12
- package/src/Path.ts +52 -7
- package/src/RequestResolver.ts +4 -10
- package/src/Scheduler.ts +9 -0
- package/src/Schema.ts +25 -4
- package/src/SchemaGetter.ts +47 -35
- package/src/Sink.ts +45 -18
- package/src/Stream.ts +33 -4
- package/src/String.ts +17 -4
- package/src/TxRef.ts +4 -2
- package/src/Utils.ts +8 -5
- package/src/ai/AiError.ts +3 -1
- package/src/ai/AnthropicStructuredOutput.ts +2 -2
- package/src/ai/Chat.ts +2 -2
- package/src/ai/Decision.ts +70 -21
- package/src/ai/DecisionModel.ts +1 -1
- package/src/ai/LanguageModel.ts +141 -63
- package/src/ai/McpServer.ts +259 -145
- package/src/ai/Prompt.ts +24 -10
- package/src/ai/Response.ts +6 -3
- package/src/cli/Completions.ts +5 -0
- package/src/cli/Flag.ts +101 -0
- package/src/cli/Param.ts +96 -0
- package/src/cli/internal/command.ts +2 -1
- package/src/cli/internal/completions/bash.ts +4 -3
- package/src/cli/internal/completions/descriptor.ts +10 -1
- package/src/cli/internal/completions/fish.ts +4 -4
- package/src/cli/internal/completions/powershell.ts +5 -2
- package/src/cli/internal/completions/zsh.ts +7 -3
- package/src/cli/internal/parser.ts +12 -3
- package/src/cli/internal/wizard.ts +2 -1
- package/src/cluster/ClusterWorkflowEngine.ts +143 -36
- package/src/cluster/MessageStorage.ts +14 -3
- package/src/cluster/Runners.ts +88 -66
- package/src/cluster/Sharding.ts +12 -9
- package/src/cluster/internal/entityManager.ts +1 -1
- package/src/encoding/Sse.ts +70 -80
- package/src/graphql/GraphQL.ts +494 -0
- package/src/graphql/GraphQLClient.ts +924 -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 +1540 -0
- package/src/graphql/GraphQLResolver.ts +1038 -0
- package/src/graphql/index.ts +48 -0
- package/src/http/FetchHttpClient.ts +6 -3
- package/src/http/FindMyWay/internal/router.ts +2 -1
- package/src/http/Headers.ts +6 -9
- package/src/http/HttpClient.ts +72 -11
- package/src/http/HttpClientError.ts +10 -6
- package/src/http/HttpClientRequest.ts +173 -7
- package/src/http/HttpEffect.ts +11 -5
- package/src/http/HttpMiddleware.ts +113 -34
- package/src/http/HttpRouter.ts +95 -13
- package/src/http/HttpServerError.ts +8 -4
- package/src/http/HttpServerRequest.ts +2 -2
- package/src/http/HttpServerResponse.ts +28 -16
- package/src/http/HttpStaticServer.ts +4 -10
- package/src/http/HttpTraceContext.ts +1 -1
- package/src/http/Multipart.ts +17 -1
- package/src/http/Url.ts +2 -1
- package/src/http/UrlParams.ts +225 -26
- package/src/http/internal/compression.ts +3 -17
- package/src/http/internal/compressionBody.ts +56 -0
- package/src/http/internal/decompression.ts +226 -0
- package/src/http/internal/tracing.ts +21 -4
- package/src/http/internal/urlParams.ts +5 -0
- package/src/http-api/HttpApiClient.ts +2 -1
- package/src/http-api/HttpApiScalar.ts +5 -0
- package/src/internal/core.ts +21 -1
- package/src/internal/dateTime.ts +13 -10
- package/src/internal/effect.ts +13 -7
- package/src/internal/graphql.ts +286 -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/Host.ts +72 -0
- package/src/net/NetAddress.ts +9 -0
- package/src/observability/Otlp.ts +18 -0
- package/src/observability/OtlpExporter.ts +14 -3
- package/src/observability/OtlpLogger.ts +31 -2
- package/src/observability/OtlpMetrics.ts +30 -1
- package/src/observability/OtlpTracer.ts +32 -3
- package/src/reactivity/AsyncResult.ts +31 -0
- package/src/reactivity/AtomRegistry.ts +3 -1
- package/src/rpc/RpcClient.ts +6 -1
- package/src/rpc/RpcServer.ts +55 -15
- package/src/socket/Socket.ts +6 -0
- package/src/sql/SqlClient.ts +9 -7
- package/src/testing/TestClock.ts +144 -29
- package/src/workflow/Activity.ts +1 -0
- package/src/workflow/DurableDeferred.ts +43 -5
- package/src/workflow/Workflow.ts +31 -5
- package/src/workflow/WorkflowEngine.ts +107 -25
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { isReadonlyArrayNonEmpty, type NonEmptyReadonlyArray } from "../Array.ts"
|
|
2
|
+
import * as Context from "../Context.ts"
|
|
3
|
+
import * as Effect from "../Effect.ts"
|
|
4
|
+
import type * as GraphQL from "../graphql/GraphQL.ts"
|
|
5
|
+
import type { DocumentTemplate } from "../graphql/GraphQL.ts"
|
|
6
|
+
import type { PartialResult } from "../graphql/GraphQLClient.ts"
|
|
7
|
+
import {
|
|
8
|
+
DecodeError,
|
|
9
|
+
EncodeError,
|
|
10
|
+
GraphQLClientError,
|
|
11
|
+
type GraphQLError,
|
|
12
|
+
type Reason,
|
|
13
|
+
ResponseError
|
|
14
|
+
} from "../graphql/GraphQLClientError.ts"
|
|
15
|
+
import type { ExecutionResult, GraphQLProtocol, GraphQLRequest } from "../graphql/GraphQLProtocol.ts"
|
|
16
|
+
import * as Schema from "../Schema.ts"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Builds a response error with raw data and transport metadata.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export const responseError = (
|
|
24
|
+
result: ExecutionResult | undefined,
|
|
25
|
+
errors: NonEmptyReadonlyArray<GraphQLError>
|
|
26
|
+
): ResponseError =>
|
|
27
|
+
new ResponseError({
|
|
28
|
+
errors,
|
|
29
|
+
...(result?.data === undefined ? {} : { data: result.data as Schema.Json }),
|
|
30
|
+
...(result?.extensions === undefined ? {} : { extensions: result.extensions }),
|
|
31
|
+
...(result?.status === undefined ? {} : { status: result.status }),
|
|
32
|
+
...(result?.headers === undefined ? {} : { headers: result.headers })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Raw responses for partial results, used by resolvers to preserve error
|
|
37
|
+
* metadata. Mock client results have no entry.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export const partialResponses = new WeakMap<object, ExecutionResult>()
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Joins two printed pieces, adding a space only where two non-punctuators
|
|
45
|
+
* would otherwise touch. This is the generator printer's rule, so joining a
|
|
46
|
+
* generated template reproduces the printed document exactly.
|
|
47
|
+
*/
|
|
48
|
+
const concat = (left: string, right: string): string =>
|
|
49
|
+
/[\w"-]$/.test(left) && /^[\w"-]/.test(right) ? `${left} ${right}` : left + right
|
|
50
|
+
|
|
51
|
+
const concatAll = (pieces: ReadonlyArray<string>): string => pieces.reduce(concat, "")
|
|
52
|
+
|
|
53
|
+
/** Renders template parts, prefixing every name (the odd entries). */
|
|
54
|
+
const renderParts = (parts: DocumentTemplate.Parts, prefix: string): string =>
|
|
55
|
+
parts.map((part, i) => i % 2 === 1 ? prefix + part : part).join("")
|
|
56
|
+
|
|
57
|
+
const isShared = (fragment: DocumentTemplate.Fragment): boolean => fragment.body.length <= 1
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Renders the template once per prefix. The empty prefix renders it as
|
|
61
|
+
* written; any other prefix renames variables and per-slice fragments and
|
|
62
|
+
* aliases root fields to the prefixed response key.
|
|
63
|
+
*/
|
|
64
|
+
const render = (
|
|
65
|
+
kind: string,
|
|
66
|
+
name: string,
|
|
67
|
+
template: DocumentTemplate,
|
|
68
|
+
prefixes: ReadonlyArray<string>
|
|
69
|
+
): string =>
|
|
70
|
+
concatAll([
|
|
71
|
+
kind,
|
|
72
|
+
name,
|
|
73
|
+
template.variables.length === 0 ? "" : `(${
|
|
74
|
+
concatAll(
|
|
75
|
+
prefixes.flatMap((prefix) =>
|
|
76
|
+
template.variables.map((variable) => `$${prefix}${variable.name}:${variable.body}`)
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
})`,
|
|
80
|
+
template.directives === undefined ? "" : renderParts(template.directives, ""),
|
|
81
|
+
`{${
|
|
82
|
+
concatAll(prefixes.flatMap((prefix) =>
|
|
83
|
+
template.selections.map((selection) => {
|
|
84
|
+
const key = prefix === "" ? selection.alias : `${prefix}${selection.alias ?? selection.name}`
|
|
85
|
+
return `${key === undefined ? "" : `${key}:`}${selection.name ?? ""}${renderParts(selection.body, prefix)}`
|
|
86
|
+
})
|
|
87
|
+
))
|
|
88
|
+
}}`,
|
|
89
|
+
...template.fragments.flatMap((fragment) =>
|
|
90
|
+
(isShared(fragment) ? [""] : prefixes).map((prefix) =>
|
|
91
|
+
concatAll(["fragment", prefix + fragment.name, renderParts(fragment.body, prefix)])
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Joins a template into the document it was split from.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export const joinTemplate = (kind: string, name: string, template: DocumentTemplate): string =>
|
|
102
|
+
render(kind, name, template, [""])
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Why the template can't be sliced for any prefixes, if it can't: an operation
|
|
106
|
+
* directive references a variable, or a root selection is a fragment spread or
|
|
107
|
+
* inline fragment.
|
|
108
|
+
*
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export const templateUnsliceableReason = (template: DocumentTemplate): string | undefined => {
|
|
112
|
+
if (template.directives !== undefined && template.directives.length > 1) {
|
|
113
|
+
return "its operation directives reference variables"
|
|
114
|
+
}
|
|
115
|
+
if (template.selections.some((selection) => selection.name === undefined)) {
|
|
116
|
+
return "it has a fragment spread or inline fragment at the root"
|
|
117
|
+
}
|
|
118
|
+
return undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Renders one operation that repeats the template once per prefix. Each
|
|
123
|
+
* slice's variables, root response keys and per-slice fragments are renamed
|
|
124
|
+
* with its prefix; operation directives and shared fragments appear once.
|
|
125
|
+
* Prefixes must start a GraphQL name, and none may be a prefix of another.
|
|
126
|
+
*
|
|
127
|
+
* Throws rather than producing an invalid document when the template is
|
|
128
|
+
* unsliceable (see {@link templateUnsliceableReason}) or a renamed fragment
|
|
129
|
+
* would take a shared fragment's name.
|
|
130
|
+
*
|
|
131
|
+
* @internal
|
|
132
|
+
*/
|
|
133
|
+
export const renderSlices = (
|
|
134
|
+
kind: string,
|
|
135
|
+
name: string,
|
|
136
|
+
template: DocumentTemplate,
|
|
137
|
+
prefixes: NonEmptyReadonlyArray<string>
|
|
138
|
+
): string => {
|
|
139
|
+
const shared = new Set(template.fragments.filter(isShared).map((fragment) => fragment.name))
|
|
140
|
+
const collision = template.fragments
|
|
141
|
+
.flatMap((fragment) => isShared(fragment) ? [] : prefixes.map((prefix) => prefix + fragment.name))
|
|
142
|
+
.find((renamed) => shared.has(renamed))
|
|
143
|
+
const reason = templateUnsliceableReason(template) ??
|
|
144
|
+
(collision === undefined ? undefined : `fragment ${collision} would collide with a shared fragment`)
|
|
145
|
+
if (reason !== undefined) throw new Error(`Cannot render slices of operation ${name}: ${reason}`)
|
|
146
|
+
return render(kind, name, template, prefixes)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Forces POST for alias batches to avoid GET URL length limits.
|
|
151
|
+
*
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
export const ForcePost = Context.Reference<boolean>("effect/graphql/ForcePost", { defaultValue: () => false })
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The URL a resolver batch was grouped by, and the protocol `url` that
|
|
158
|
+
* resolved it, so that transport sends the batch there without resolving it
|
|
159
|
+
* again.
|
|
160
|
+
*
|
|
161
|
+
* @internal
|
|
162
|
+
*/
|
|
163
|
+
export type ResolvedUrl = {
|
|
164
|
+
readonly resolve: GraphQLProtocol["Service"]["url"]
|
|
165
|
+
readonly url: string
|
|
166
|
+
} | undefined
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Carries a {@link ResolvedUrl} from a resolver batch to the transport. The
|
|
170
|
+
* client clears it while middleware run, so calls they make resolve their
|
|
171
|
+
* own URL.
|
|
172
|
+
*
|
|
173
|
+
* @internal
|
|
174
|
+
*/
|
|
175
|
+
export const ResolvedUrl = Context.Reference<ResolvedUrl>("effect/graphql/ResolvedUrl", {
|
|
176
|
+
defaultValue: () => undefined
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* JSON with object keys sorted, so equal JSON values give equal strings.
|
|
181
|
+
*
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
export const canonicalJson = (value: unknown): string =>
|
|
185
|
+
JSON.stringify(value, (_, value) =>
|
|
186
|
+
value !== null && typeof value === "object" && !Array.isArray(value)
|
|
187
|
+
? Object.fromEntries(Object.keys(value).sort().map((key) => [key, value[key]]))
|
|
188
|
+
: value)
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Shared variable encoding, result decoding and client error mapping.
|
|
192
|
+
*
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
export const makeCodec = <Op extends GraphQL.Any>(operation: Op) => {
|
|
196
|
+
const fail = (reason: Reason) => new GraphQLClientError({ operation: operation.name, reason })
|
|
197
|
+
const schemaFail = (make: (description: string) => Reason) => (error: Schema.SchemaError) =>
|
|
198
|
+
Effect.fail(fail(make(error.message)))
|
|
199
|
+
const encode = Schema.encodeEffect(operation.variables)
|
|
200
|
+
const decode = Schema.decodeUnknownEffect(operation.result)
|
|
201
|
+
|
|
202
|
+
const encodeVariables = (
|
|
203
|
+
variables: GraphQL.Variables<Op> | undefined
|
|
204
|
+
): Effect.Effect<unknown, GraphQLClientError, Op["variables"]["EncodingServices"]> =>
|
|
205
|
+
Effect.catch(encode(variables ?? {}), schemaFail((description) => new EncodeError({ description })))
|
|
206
|
+
|
|
207
|
+
const decodeData = (
|
|
208
|
+
data: unknown
|
|
209
|
+
): Effect.Effect<GraphQL.Result<Op>, GraphQLClientError, Op["result"]["DecodingServices"]> =>
|
|
210
|
+
Effect.catch(decode(data), schemaFail((description) => new DecodeError({ description })))
|
|
211
|
+
|
|
212
|
+
// GraphQL requires errors when data is null or missing. Check before decoding,
|
|
213
|
+
// since schemas such as Schema.Json accept null.
|
|
214
|
+
const failResponse = (result: ExecutionResult): Effect.Effect<never, GraphQLClientError> => {
|
|
215
|
+
const errors = result.errors ?? []
|
|
216
|
+
return Effect.fail(fail(
|
|
217
|
+
isReadonlyArrayNonEmpty(errors)
|
|
218
|
+
? responseError(result, errors)
|
|
219
|
+
: new DecodeError({ description: "data is null or missing without errors" })
|
|
220
|
+
))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const decodeStrict = (
|
|
224
|
+
result: ExecutionResult
|
|
225
|
+
): Effect.Effect<GraphQL.Result<Op>, GraphQLClientError, Op["result"]["DecodingServices"]> =>
|
|
226
|
+
isReadonlyArrayNonEmpty(result.errors ?? []) || result.data === null || result.data === undefined
|
|
227
|
+
? failResponse(result)
|
|
228
|
+
: decodeData(result.data)
|
|
229
|
+
|
|
230
|
+
const decodePartial = (
|
|
231
|
+
result: ExecutionResult
|
|
232
|
+
): Effect.Effect<PartialResult<GraphQL.Result<Op>>, GraphQLClientError, Op["result"]["DecodingServices"]> => {
|
|
233
|
+
if (result.data === null || result.data === undefined) return failResponse(result)
|
|
234
|
+
return Effect.map(decodeData(result.data), (data): PartialResult<GraphQL.Result<Op>> => {
|
|
235
|
+
const partial = { data, errors: result.errors ?? [] }
|
|
236
|
+
partialResponses.set(partial, result)
|
|
237
|
+
return partial
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { fail, schemaFail, encodeVariables, failResponse, decodeStrict, decodePartial } as const
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Client batch runners (middleware and protocol, using POST) and URLs for
|
|
246
|
+
* resolver grouping. Mock and custom methods are not registered.
|
|
247
|
+
*
|
|
248
|
+
* @internal
|
|
249
|
+
*/
|
|
250
|
+
export const rawExecutors = new WeakMap<object, {
|
|
251
|
+
readonly execute: (request: GraphQLRequest) => Effect.Effect<ExecutionResult, unknown, unknown>
|
|
252
|
+
readonly url: GraphQLProtocol["Service"]["url"]
|
|
253
|
+
}>()
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Splits a batched response, removing prefixes from root keys and error paths.
|
|
257
|
+
* Returns undefined if an error matches no slice or data is null or missing.
|
|
258
|
+
*
|
|
259
|
+
* @internal
|
|
260
|
+
*/
|
|
261
|
+
export const splitSlices = (
|
|
262
|
+
result: ExecutionResult,
|
|
263
|
+
template: DocumentTemplate,
|
|
264
|
+
prefixes: ReadonlyArray<string>
|
|
265
|
+
): ReadonlyArray<ExecutionResult> | undefined => {
|
|
266
|
+
const sliceErrors = prefixes.map((): Array<GraphQLError> => [])
|
|
267
|
+
for (const error of result.errors ?? []) {
|
|
268
|
+
const key = error.path?.[0]
|
|
269
|
+
// No prefix is a prefix of another, so at most one matches.
|
|
270
|
+
const index = typeof key === "string" ? prefixes.findIndex((prefix) => key.startsWith(prefix)) : -1
|
|
271
|
+
if (index === -1) return undefined
|
|
272
|
+
sliceErrors[index].push(
|
|
273
|
+
{ ...error, path: [(key as string).slice(prefixes[index].length), ...error.path!.slice(1)] } as GraphQLError
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
if (result.data === null || result.data === undefined) return undefined
|
|
277
|
+
const data = result.data as Record<string, unknown>
|
|
278
|
+
return prefixes.map((prefix, i) => {
|
|
279
|
+
const sliceData: Record<string, unknown> = {}
|
|
280
|
+
for (const selection of template.selections) {
|
|
281
|
+
const key = (selection.alias ?? selection.name)!
|
|
282
|
+
if (Object.hasOwn(data, prefix + key)) sliceData[key] = data[prefix + key]
|
|
283
|
+
}
|
|
284
|
+
return { ...result, data: sliceData, errors: sliceErrors[i] }
|
|
285
|
+
})
|
|
286
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as Headers from "../http/Headers.ts"
|
|
2
|
+
import * as Schema from "../Schema.ts"
|
|
3
|
+
import * as SchemaGetter from "../SchemaGetter.ts"
|
|
4
|
+
|
|
5
|
+
// Keep headers branded at runtime while encoding only their string entries.
|
|
6
|
+
export const HeadersSchema = Schema.Record(Schema.String, Schema.String).pipe(
|
|
7
|
+
Schema.decodeTo(Schema.declare<Headers.Headers>(Headers.isHeaders), {
|
|
8
|
+
decode: SchemaGetter.transform(Headers.fromInput),
|
|
9
|
+
encode: SchemaGetter.transform((headers) => ({ ...headers }))
|
|
10
|
+
})
|
|
11
|
+
)
|
package/src/internal/request.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { Effect } from "../Effect.ts"
|
|
|
4
4
|
import type { Exit } from "../Exit.ts"
|
|
5
5
|
import type { Fiber } from "../Fiber.ts"
|
|
6
6
|
import { dual } from "../Function.ts"
|
|
7
|
+
import * as MutableHashMap from "../MutableHashMap.ts"
|
|
8
|
+
import * as Option from "../Option.ts"
|
|
7
9
|
import type * as Request from "../Request.ts"
|
|
8
10
|
import { makeEntry } from "../Request.ts"
|
|
9
11
|
import type { RequestResolver } from "../RequestResolver.ts"
|
|
@@ -67,10 +69,36 @@ export const requestUnsafe = <A extends Request.Any>(
|
|
|
67
69
|
return () => removeEntryUnsafe(options.resolver, entry)
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Pending batches of resolvers whose `batchKey` is in this set match keys with
|
|
74
|
+
* `Equal` instead of by reference.
|
|
75
|
+
*
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export const equalBatchKeys = new WeakSet<Function>()
|
|
79
|
+
|
|
80
|
+
interface BatchMap {
|
|
81
|
+
get(key: unknown): Batch | undefined
|
|
82
|
+
has(key: unknown): boolean
|
|
83
|
+
set(key: unknown, batch: Batch): void
|
|
84
|
+
delete(key: unknown): void
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const makeBatchMap = (resolver: RequestResolver<any>): BatchMap => {
|
|
88
|
+
if (!equalBatchKeys.has(resolver.batchKey)) return new Map()
|
|
89
|
+
const map = MutableHashMap.empty<unknown, Batch>()
|
|
90
|
+
return {
|
|
91
|
+
get: (key) => Option.getOrUndefined(MutableHashMap.get(map, key)),
|
|
92
|
+
has: (key) => MutableHashMap.has(map, key),
|
|
93
|
+
set: (key, batch) => MutableHashMap.set(map, key, batch),
|
|
94
|
+
delete: (key) => MutableHashMap.remove(map, key)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
70
98
|
interface Batch {
|
|
71
99
|
key: unknown
|
|
72
100
|
resolver: RequestResolver<any>
|
|
73
|
-
map:
|
|
101
|
+
map: BatchMap
|
|
74
102
|
readonly entrySet: Set<Request.Entry<any>>
|
|
75
103
|
readonly entries: Set<Request.Entry<any>>
|
|
76
104
|
readonly delayEffect: Effect<void>
|
|
@@ -79,7 +107,7 @@ interface Batch {
|
|
|
79
107
|
}
|
|
80
108
|
|
|
81
109
|
const batchPool: Array<Batch> = []
|
|
82
|
-
const pendingBatches = new WeakMap<RequestResolver<any>,
|
|
110
|
+
const pendingBatches = new WeakMap<RequestResolver<any>, BatchMap>()
|
|
83
111
|
|
|
84
112
|
const addEntry = <A extends Request.Any>(
|
|
85
113
|
resolver: RequestResolver<A>,
|
|
@@ -93,7 +121,7 @@ const addEntry = <A extends Request.Any>(
|
|
|
93
121
|
) => {
|
|
94
122
|
let batchMap = pendingBatches.get(resolver)
|
|
95
123
|
if (!batchMap) {
|
|
96
|
-
batchMap =
|
|
124
|
+
batchMap = makeBatchMap(resolver)
|
|
97
125
|
pendingBatches.set(resolver, batchMap)
|
|
98
126
|
}
|
|
99
127
|
let batch: Batch | undefined
|
|
@@ -48,6 +48,8 @@ function renderEmittableAnnotation(input: unknown): string | undefined {
|
|
|
48
48
|
return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
const canonicalFiniteCheck = `Schema.isFinite().annotate({ "expected": "a finite number" })`
|
|
52
|
+
|
|
51
53
|
function renderAnnotations(
|
|
52
54
|
annotations: Schema.Annotations.Annotations | undefined
|
|
53
55
|
): string | undefined {
|
|
@@ -341,7 +343,11 @@ export function toCodeDocument(
|
|
|
341
343
|
let runtime = base.runtime + runtimeAnnotate(representation.annotations)
|
|
342
344
|
for (let index = 0; index < representation.checks.length; index++) {
|
|
343
345
|
const check = representation.checks[index]
|
|
344
|
-
|
|
346
|
+
const compiledCheck = compileCheck(check, [...path, "checks", index])
|
|
347
|
+
// Only collapse a bare number's leading canonical check.
|
|
348
|
+
runtime = runtime === "Schema.Number" && compiledCheck === canonicalFiniteCheck
|
|
349
|
+
? "Schema.Finite"
|
|
350
|
+
: `${runtime}.check(${compiledCheck})`
|
|
345
351
|
}
|
|
346
352
|
return makeCode(runtime, base.Type)
|
|
347
353
|
}
|
|
@@ -121,6 +121,37 @@ function hasNoCollisions(left: JsonSchema.JsonSchema, rightKeys: ReadonlyArray<s
|
|
|
121
121
|
return typeof left.$ref !== "string" && rightKeys.every((key) => !Object.hasOwn(left, key))
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// Every bound on one schema object applies, so a repeated bound reduces to its tighter value.
|
|
125
|
+
const lowerBoundKeywords = "|minimum|exclusiveMinimum|minLength|minItems|minProperties|"
|
|
126
|
+
const upperBoundKeywords = "|maximum|exclusiveMaximum|maxLength|maxItems|maxProperties|"
|
|
127
|
+
|
|
128
|
+
// Tightens repeated bounds and drops an inclusive or exclusive bound implied by the other on the same side. Returns
|
|
129
|
+
// `undefined` for a `$ref` schema or a non-bound collision, so the caller falls back to `allOf`.
|
|
130
|
+
function mergeInlineCheck(
|
|
131
|
+
left: JsonSchema.JsonSchema,
|
|
132
|
+
right: JsonSchema.JsonSchema,
|
|
133
|
+
rightKeys: ReadonlyArray<string>
|
|
134
|
+
): JsonSchema.JsonSchema | undefined {
|
|
135
|
+
if (typeof left.$ref === "string") return undefined
|
|
136
|
+
const out: JsonSchema.JsonSchema = { ...left, ...right }
|
|
137
|
+
for (const key of rightKeys) {
|
|
138
|
+
if (!Object.hasOwn(left, key)) continue
|
|
139
|
+
const a = left[key]
|
|
140
|
+
const b = right[key]
|
|
141
|
+
if (typeof a !== "number" || typeof b !== "number") return undefined
|
|
142
|
+
if (lowerBoundKeywords.includes(`|${key}|`)) out[key] = Math.max(a, b)
|
|
143
|
+
else if (upperBoundKeywords.includes(`|${key}|`)) out[key] = Math.min(a, b)
|
|
144
|
+
else return undefined
|
|
145
|
+
}
|
|
146
|
+
if (typeof out.minimum === "number" && typeof out.exclusiveMinimum === "number") {
|
|
147
|
+
delete out[out.exclusiveMinimum >= out.minimum ? "minimum" : "exclusiveMinimum"]
|
|
148
|
+
}
|
|
149
|
+
if (typeof out.maximum === "number" && typeof out.exclusiveMaximum === "number") {
|
|
150
|
+
delete out[out.exclusiveMaximum <= out.maximum ? "maximum" : "exclusiveMaximum"]
|
|
151
|
+
}
|
|
152
|
+
return out
|
|
153
|
+
}
|
|
154
|
+
|
|
124
155
|
// `format` and `content*` can affect validation, so they are not treated as pure annotations.
|
|
125
156
|
const promotableAnnotationKeywords = "|title|description|default|examples|readOnly|writeOnly|"
|
|
126
157
|
const inlineableAnnotatedCheckKeywords = inlineableCheckKeywords + promotableAnnotationKeywords
|
|
@@ -149,8 +180,9 @@ function appendJsonSchema(
|
|
|
149
180
|
: appendJsonSchema(base, extracted.schema, inlineCheck)
|
|
150
181
|
}
|
|
151
182
|
}
|
|
152
|
-
if (inlineCheck
|
|
153
|
-
|
|
183
|
+
if (inlineCheck) {
|
|
184
|
+
const merged = mergeInlineCheck(left, right, rightKeys)
|
|
185
|
+
if (merged !== undefined) return merged
|
|
154
186
|
}
|
|
155
187
|
const members = Array.isArray(right.allOf) && rightKeys.length === 1 ? right.allOf : [right]
|
|
156
188
|
if (Array.isArray(left.allOf)) {
|
package/src/internal/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = "4.0.
|
|
2
|
+
export const version = "4.0.5"
|
package/src/net/Host.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @stability experimental
|
|
10
10
|
* @since 4.0.0
|
|
11
11
|
*/
|
|
12
|
+
import type * as Arr from "../Array.ts"
|
|
12
13
|
import type * as Brand from "../Brand.ts"
|
|
13
14
|
import * as Equal from "../Equal.ts"
|
|
14
15
|
import * as Hash from "../Hash.ts"
|
|
@@ -321,6 +322,28 @@ export const formatHost = (self: Host): string => typeof self === "string" ? sel
|
|
|
321
322
|
*/
|
|
322
323
|
export const isFullyQualified = (self: DomainName): boolean => self.endsWith(".")
|
|
323
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Appends the trailing dot to a domain name, unless it is already fully
|
|
327
|
+
* qualified.
|
|
328
|
+
*
|
|
329
|
+
* @stability experimental
|
|
330
|
+
* @category converting
|
|
331
|
+
* @since 4.0.0
|
|
332
|
+
*/
|
|
333
|
+
export const toFullyQualified = (self: DomainName): DomainName =>
|
|
334
|
+
isFullyQualified(self) ? self : `${self}.` as DomainName
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Removes the trailing dot of a fully qualified domain name, except from the
|
|
338
|
+
* root name `.`.
|
|
339
|
+
*
|
|
340
|
+
* @stability experimental
|
|
341
|
+
* @category converting
|
|
342
|
+
* @since 4.0.0
|
|
343
|
+
*/
|
|
344
|
+
export const toRelative = (self: DomainName): DomainName =>
|
|
345
|
+
self.length > 1 && isFullyQualified(self) ? self.slice(0, -1) as DomainName : self
|
|
346
|
+
|
|
324
347
|
const HostPortProto = {
|
|
325
348
|
...Inspectable.BaseProto,
|
|
326
349
|
_tag: "HostPort",
|
|
@@ -470,3 +493,52 @@ export const formatHostPort = (self: HostPort): string => {
|
|
|
470
493
|
const host = formatHost(self.host)
|
|
471
494
|
return host.includes(":") ? `[${host}]:${self.port}` : `${host}:${self.port}`
|
|
472
495
|
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The addresses of host names, as read from a hosts file.
|
|
499
|
+
*
|
|
500
|
+
* @see {@link parseHostsFile} for reading a hosts file
|
|
501
|
+
* @stability experimental
|
|
502
|
+
* @category models
|
|
503
|
+
* @since 4.0.0
|
|
504
|
+
*/
|
|
505
|
+
export type HostsTable = ReadonlyMap<DomainName, Arr.NonEmptyReadonlyArray<NetAddress.IpAddress>>
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Parses a hosts file into the addresses of each host name. Invalid entries
|
|
509
|
+
* are skipped.
|
|
510
|
+
*
|
|
511
|
+
* **Example** (Parsing a hosts file)
|
|
512
|
+
*
|
|
513
|
+
* ```ts import.meta.vitest
|
|
514
|
+
* import { Host, NetAddress } from "effect/net"
|
|
515
|
+
*
|
|
516
|
+
* const hosts = Host.parseHostsFile(`
|
|
517
|
+
* 127.0.0.1 localhost
|
|
518
|
+
* ::1 localhost ip6-localhost # loopback
|
|
519
|
+
* `)
|
|
520
|
+
* hosts.get(Host.domainNameFromStringUnsafe("localhost"))?.map(NetAddress.formatIp) // => ["127.0.0.1", "::1"]
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @stability experimental
|
|
524
|
+
* @category decoding
|
|
525
|
+
* @since 4.0.0
|
|
526
|
+
*/
|
|
527
|
+
export const parseHostsFile = (text: string): HostsTable => {
|
|
528
|
+
const hosts = new Map<DomainName, Array<NetAddress.IpAddress>>()
|
|
529
|
+
for (const line of text.split(/\r?\n/)) {
|
|
530
|
+
const comment = line.indexOf("#")
|
|
531
|
+
const [first, ...names] = (comment === -1 ? line : line.slice(0, comment)).trim().split(/\s+/)
|
|
532
|
+
const address = NetAddress.ipFromString(first)
|
|
533
|
+
if (Result.isFailure(address)) continue
|
|
534
|
+
for (const name of names) {
|
|
535
|
+
const domain = domainNameFromString(name)
|
|
536
|
+
if (Result.isFailure(domain)) continue
|
|
537
|
+
const key = toRelative(domain.success)
|
|
538
|
+
const addresses = hosts.get(key)
|
|
539
|
+
if (addresses === undefined) hosts.set(key, [address.success])
|
|
540
|
+
else if (!addresses.some((existing) => Equal.equals(existing, address.success))) addresses.push(address.success)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return hosts as unknown as HostsTable
|
|
544
|
+
}
|
package/src/net/NetAddress.ts
CHANGED
|
@@ -864,6 +864,15 @@ export const ipv4Loopback: Ipv4Address = makeIpv4(0x7f000001)
|
|
|
864
864
|
*/
|
|
865
865
|
export const ipv6Loopback: Ipv6Address = makeIpv6(0, 0, 0, 1)
|
|
866
866
|
|
|
867
|
+
/**
|
|
868
|
+
* The loopback addresses `127.0.0.1` and `::1`.
|
|
869
|
+
*
|
|
870
|
+
* @stability unstable
|
|
871
|
+
* @category constants
|
|
872
|
+
* @since 4.0.0
|
|
873
|
+
*/
|
|
874
|
+
export const loopbackAddresses: readonly [Ipv4Address, Ipv6Address] = [ipv4Loopback, ipv6Loopback]
|
|
875
|
+
|
|
867
876
|
/**
|
|
868
877
|
* The unspecified IPv4 address `0.0.0.0`.
|
|
869
878
|
*
|
|
@@ -42,6 +42,11 @@ export const layer = (options: {
|
|
|
42
42
|
readonly attributes?: Record<string, unknown>
|
|
43
43
|
} | undefined
|
|
44
44
|
readonly headers?: Headers.Input | undefined
|
|
45
|
+
/**
|
|
46
|
+
* Request compression. Defaults to "none". Gzip uses
|
|
47
|
+
* HttpClientRequest's default minimum body size of 1KB.
|
|
48
|
+
*/
|
|
49
|
+
readonly compression?: "gzip" | "none" | undefined
|
|
45
50
|
readonly maxBatchSize?: number | undefined
|
|
46
51
|
readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
|
|
47
52
|
readonly loggerExportInterval?: Duration.Input | undefined
|
|
@@ -59,6 +64,7 @@ export const layer = (options: {
|
|
|
59
64
|
url: url("/v1/logs"),
|
|
60
65
|
resource: options.resource,
|
|
61
66
|
headers: options.headers,
|
|
67
|
+
compression: options.compression,
|
|
62
68
|
exportInterval: options.loggerExportInterval,
|
|
63
69
|
maxBatchSize: options.maxBatchSize,
|
|
64
70
|
shutdownTimeout: options.shutdownTimeout,
|
|
@@ -69,6 +75,7 @@ export const layer = (options: {
|
|
|
69
75
|
url: url("/v1/metrics"),
|
|
70
76
|
resource: options.resource,
|
|
71
77
|
headers: options.headers,
|
|
78
|
+
compression: options.compression,
|
|
72
79
|
exportInterval: options.metricsExportInterval,
|
|
73
80
|
shutdownTimeout: options.shutdownTimeout,
|
|
74
81
|
temporality: options.metricsTemporality
|
|
@@ -77,6 +84,7 @@ export const layer = (options: {
|
|
|
77
84
|
url: url("/v1/traces"),
|
|
78
85
|
resource: options.resource,
|
|
79
86
|
headers: options.headers,
|
|
87
|
+
compression: options.compression,
|
|
80
88
|
exportInterval: options.tracerExportInterval,
|
|
81
89
|
maxBatchSize: options.maxBatchSize,
|
|
82
90
|
context: options.tracerContext,
|
|
@@ -138,6 +146,11 @@ export const layerJson: (options: {
|
|
|
138
146
|
readonly attributes?: Record<string, unknown>
|
|
139
147
|
} | undefined
|
|
140
148
|
readonly headers?: Headers.Input | undefined
|
|
149
|
+
/**
|
|
150
|
+
* Request compression. Defaults to "none". Gzip uses
|
|
151
|
+
* HttpClientRequest's default minimum body size of 1KB.
|
|
152
|
+
*/
|
|
153
|
+
readonly compression?: "gzip" | "none" | undefined
|
|
141
154
|
readonly maxBatchSize?: number | undefined
|
|
142
155
|
readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
|
|
143
156
|
readonly loggerExportInterval?: Duration.Input | undefined
|
|
@@ -165,6 +178,11 @@ export const layerProtobuf: (options: {
|
|
|
165
178
|
readonly attributes?: Record<string, unknown>
|
|
166
179
|
} | undefined
|
|
167
180
|
readonly headers?: Headers.Input | undefined
|
|
181
|
+
/**
|
|
182
|
+
* Request compression. Defaults to "none". Gzip uses
|
|
183
|
+
* HttpClientRequest's default minimum body size of 1KB.
|
|
184
|
+
*/
|
|
185
|
+
readonly compression?: "gzip" | "none" | undefined
|
|
168
186
|
readonly maxBatchSize?: number | undefined
|
|
169
187
|
readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
|
|
170
188
|
readonly loggerExportInterval?: Duration.Input | undefined
|
|
@@ -161,6 +161,12 @@ export const layerFlusher: Layer.Layer<Flusher> = Layer.sync(Flusher, () => {
|
|
|
161
161
|
* after unhandled failures, and flushes during scope finalization up to
|
|
162
162
|
* `shutdownTimeout`.
|
|
163
163
|
*
|
|
164
|
+
* Optional gzip compression uses `HttpClientRequest.compressBody` before the
|
|
165
|
+
* retry loop, so retries reuse the compressed bytes. Bodies smaller than 1KB
|
|
166
|
+
* remain uncompressed. Compression requires runtime `CompressionStream` support;
|
|
167
|
+
* compression failures follow the same temporary-disable behavior as transport
|
|
168
|
+
* failures and do not fail flushing or shutdown.
|
|
169
|
+
*
|
|
164
170
|
* @stability unstable
|
|
165
171
|
* @category constructors
|
|
166
172
|
* @since 4.0.0
|
|
@@ -169,6 +175,8 @@ export const make: (
|
|
|
169
175
|
options: {
|
|
170
176
|
readonly url: string
|
|
171
177
|
readonly headers: Headers.Input | undefined
|
|
178
|
+
/** Gzip uses the default 1KB minimum body size; smaller batches remain uncompressed. */
|
|
179
|
+
readonly compression?: "gzip" | "none" | undefined
|
|
172
180
|
readonly label: string
|
|
173
181
|
readonly exportInterval: Duration.Input
|
|
174
182
|
readonly maxBatchSize: number | "disabled"
|
|
@@ -218,9 +226,12 @@ export const make: (
|
|
|
218
226
|
const items = buffer
|
|
219
227
|
buffer = []
|
|
220
228
|
const [body, onSuccess] = options.body(items)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
const batchRequest = HttpClientRequest.setBody(request, body)
|
|
230
|
+
// Compress before client.execute so transient retries reuse the encoded bytes.
|
|
231
|
+
const response = options.compression === "gzip"
|
|
232
|
+
? HttpClientRequest.compressBody(batchRequest, "gzip").pipe(Effect.flatMap(client.execute))
|
|
233
|
+
: client.execute(batchRequest)
|
|
234
|
+
return response.pipe(
|
|
224
235
|
Effect.andThen(onSuccess),
|
|
225
236
|
Effect.asVoid,
|
|
226
237
|
Effect.withTracerEnabled(false)
|