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
package/src/socket/Socket.ts
CHANGED
|
@@ -161,6 +161,12 @@ export interface Writer {
|
|
|
161
161
|
* @since 4.0.0
|
|
162
162
|
*/
|
|
163
163
|
export interface TlsUpgradeOptions {
|
|
164
|
+
/**
|
|
165
|
+
* DNS hostname used for Server Name Indication (SNI) and certificate identity
|
|
166
|
+
* verification during client upgrades. Server upgrades ignore this option.
|
|
167
|
+
* When omitted, the adapter retains its existing hostname behavior.
|
|
168
|
+
*/
|
|
169
|
+
readonly servername?: string | undefined
|
|
164
170
|
readonly key?:
|
|
165
171
|
| Redacted.Redacted<string | Uint8Array>
|
|
166
172
|
| ReadonlyArray<Redacted.Redacted<string | Uint8Array>>
|
|
@@ -799,13 +805,18 @@ export interface WebSocketLike {
|
|
|
799
805
|
}
|
|
800
806
|
|
|
801
807
|
/**
|
|
802
|
-
*
|
|
808
|
+
* Client options for Node and Bun WebSocket implementations, allowing
|
|
809
|
+
* handshake headers and subprotocols to be supplied together.
|
|
803
810
|
*
|
|
804
811
|
* @stability unstable
|
|
805
812
|
* @category models
|
|
806
813
|
* @since 4.0.0
|
|
807
814
|
*/
|
|
808
815
|
export interface WebSocketClientOptions {
|
|
816
|
+
/**
|
|
817
|
+
* Subprotocols to offer in the opening handshake.
|
|
818
|
+
*/
|
|
819
|
+
readonly protocols?: string | Array<string> | undefined
|
|
809
820
|
/**
|
|
810
821
|
* Headers to include in the opening handshake.
|
|
811
822
|
*
|
|
@@ -857,6 +868,7 @@ export class WebSocketConstructor extends Context.Service<
|
|
|
857
868
|
|
|
858
869
|
/**
|
|
859
870
|
* Layer that provides `WebSocketConstructor` using `globalThis.WebSocket`.
|
|
871
|
+
* Rejects client options objects, including those with handshake headers.
|
|
860
872
|
*
|
|
861
873
|
* @stability unstable
|
|
862
874
|
* @category layers
|
|
@@ -874,6 +886,8 @@ export const layerWebSocketConstructorGlobal: Layer.Layer<WebSocketConstructor>
|
|
|
874
886
|
/**
|
|
875
887
|
* Creates a `Socket` backed by a `WebSocketConstructor`, dialing the
|
|
876
888
|
* WebSocket for each reader acquisition.
|
|
889
|
+
* Optional handshake headers require a Node or Bun constructor; without
|
|
890
|
+
* headers, protocols are passed directly for browser compatibility.
|
|
877
891
|
*
|
|
878
892
|
* @stability unstable
|
|
879
893
|
* @category constructors
|
|
@@ -882,13 +896,21 @@ export const layerWebSocketConstructorGlobal: Layer.Layer<WebSocketConstructor>
|
|
|
882
896
|
export const makeWebSocket = (url: string | Effect.Effect<string>, options?: {
|
|
883
897
|
readonly openTimeout?: Duration.Input | undefined
|
|
884
898
|
readonly protocols?: string | Array<string> | undefined
|
|
899
|
+
readonly headers?: Readonly<Record<string, string>> | undefined
|
|
885
900
|
readonly highWaterMark?: number | undefined
|
|
886
901
|
}): Effect.Effect<Socket, never, WebSocketConstructor> =>
|
|
887
902
|
WebSocketConstructor.use((makeWs) =>
|
|
888
903
|
fromWebSocket(
|
|
889
904
|
Effect.acquireRelease(
|
|
890
905
|
(typeof url === "string" ? Effect.succeed(url) : url).pipe(
|
|
891
|
-
Effect.map((url) =>
|
|
906
|
+
Effect.map((url) =>
|
|
907
|
+
makeWs(
|
|
908
|
+
url,
|
|
909
|
+
options?.headers === undefined
|
|
910
|
+
? options?.protocols
|
|
911
|
+
: { protocols: options.protocols, headers: options.headers }
|
|
912
|
+
)
|
|
913
|
+
)
|
|
892
914
|
),
|
|
893
915
|
(ws) => Effect.sync(() => ws.close(1000))
|
|
894
916
|
),
|
|
@@ -1165,6 +1187,7 @@ const defaultHighWaterMark = 64 * 1024
|
|
|
1165
1187
|
/**
|
|
1166
1188
|
* Creates a binary `Channel` backed by a WebSocket URL, requiring a
|
|
1167
1189
|
* `WebSocketConstructor` service.
|
|
1190
|
+
* Supports subprotocols and, with a Node or Bun constructor, handshake headers.
|
|
1168
1191
|
*
|
|
1169
1192
|
* @stability unstable
|
|
1170
1193
|
* @category constructors
|
|
@@ -1175,6 +1198,7 @@ export const makeWebSocketChannel = <IE = never>(
|
|
|
1175
1198
|
options?: {
|
|
1176
1199
|
readonly openTimeout?: Duration.Input | undefined
|
|
1177
1200
|
readonly protocols?: string | Array<string> | undefined
|
|
1201
|
+
readonly headers?: Readonly<Record<string, string>> | undefined
|
|
1178
1202
|
readonly highWaterMark?: number | undefined
|
|
1179
1203
|
}
|
|
1180
1204
|
): Channel.Channel<
|
|
@@ -1193,6 +1217,7 @@ export const makeWebSocketChannel = <IE = never>(
|
|
|
1193
1217
|
/**
|
|
1194
1218
|
* Layer that provides a `Socket` service backed by a WebSocket URL or URL
|
|
1195
1219
|
* effect.
|
|
1220
|
+
* Supports subprotocols and, with a Node or Bun constructor, handshake headers.
|
|
1196
1221
|
*
|
|
1197
1222
|
* @stability unstable
|
|
1198
1223
|
* @category layers
|
|
@@ -1203,6 +1228,7 @@ export const layerWebSocket: (
|
|
|
1203
1228
|
options?: {
|
|
1204
1229
|
readonly openTimeout?: Duration.Input | undefined
|
|
1205
1230
|
readonly protocols?: string | Array<string> | undefined
|
|
1231
|
+
readonly headers?: Readonly<Record<string, string>> | undefined
|
|
1206
1232
|
readonly highWaterMark?: number | undefined
|
|
1207
1233
|
} | undefined
|
|
1208
1234
|
) => Layer.Layer<Socket, never, WebSocketConstructor> = flow(makeWebSocket, Layer.effect(Socket))
|
package/src/socket/index.ts
CHANGED
package/src/sql/Migrator.ts
CHANGED
|
@@ -140,14 +140,21 @@ export const make = <RD = never>({
|
|
|
140
140
|
PRIMARY KEY (migration_id)
|
|
141
141
|
)`,
|
|
142
142
|
pg: () =>
|
|
143
|
-
Effect.
|
|
144
|
-
sql
|
|
145
|
-
() =>
|
|
146
|
-
sql`CREATE TABLE ${sql(table)} (
|
|
143
|
+
Effect.flatMap(
|
|
144
|
+
migrationsTableExists(sql, table),
|
|
145
|
+
(exists) =>
|
|
146
|
+
exists ? Effect.void : Effect.asVoid(sql`CREATE TABLE ${sql(table)} (
|
|
147
147
|
migration_id integer primary key,
|
|
148
148
|
created_at timestamp with time zone not null default now(),
|
|
149
149
|
name text not null
|
|
150
|
-
)`
|
|
150
|
+
)`).pipe(
|
|
151
|
+
Effect.catch((error) =>
|
|
152
|
+
Effect.flatMap(
|
|
153
|
+
migrationsTableExists(sql, table),
|
|
154
|
+
(exists) => exists ? Effect.void : Effect.fail(error)
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
)
|
|
151
158
|
),
|
|
152
159
|
orElse: () =>
|
|
153
160
|
sql`CREATE TABLE IF NOT EXISTS ${sql(table)} (
|
|
@@ -328,6 +335,15 @@ export const make = <RD = never>({
|
|
|
328
335
|
return completed
|
|
329
336
|
})
|
|
330
337
|
|
|
338
|
+
const migrationsTableExists = (sql: Client.SqlClient, table: string): Effect.Effect<boolean, SqlError> => {
|
|
339
|
+
// Match query quoting: to_regclass folds unquoted names to lowercase.
|
|
340
|
+
const quotedTable = sql`${sql(table)}`.compile(true)[0]
|
|
341
|
+
return Effect.map(
|
|
342
|
+
sql`SELECT 1 AS found WHERE to_regclass(${quotedTable}) IS NOT NULL`.withoutTransform,
|
|
343
|
+
(rows) => rows.length > 0
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
|
|
331
347
|
const migrationOrder = Order.make<ResolvedMigration>(([a], [b]) => Order.Number(a, b))
|
|
332
348
|
|
|
333
349
|
const isConstraintConflict = (error: SqlError): boolean =>
|
package/src/sql/SqlClient.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @stability unstable
|
|
10
10
|
* @since 4.0.0
|
|
11
11
|
*/
|
|
12
|
-
import
|
|
12
|
+
import * as Cause from "../Cause.ts"
|
|
13
13
|
import { Clock } from "../Clock.ts"
|
|
14
14
|
import * as Context from "../Context.ts"
|
|
15
15
|
import * as Effect from "../Effect.ts"
|
|
@@ -280,6 +280,12 @@ export const make = Effect.fnUntraced(function*(options: SqlClient.MakeOptions)
|
|
|
280
280
|
*
|
|
281
281
|
* **Details**
|
|
282
282
|
*
|
|
283
|
+
* Both acquirers lease the connection into the current scope and hold the
|
|
284
|
+
* semaphore until that scope closes, so a statement or stream keeps the
|
|
285
|
+
* connection until it finishes and another fiber cannot begin a transaction
|
|
286
|
+
* underneath it. Effects that use the connection directly, such as an export,
|
|
287
|
+
* have to run inside `Effect.scoped`.
|
|
288
|
+
*
|
|
283
289
|
* SQLite can keep a transaction open after COMMIT fails, for example on a
|
|
284
290
|
* deferred foreign key violation. `onCommitFailure` rolls it back before the
|
|
285
291
|
* connection is reused. If that ROLLBACK fails, the connection is rejected, and
|
|
@@ -300,7 +306,7 @@ export const makeSqliteAcquirers = <C extends Connection.Connection>(options: {
|
|
|
300
306
|
readonly semaphore: Semaphore.Semaphore
|
|
301
307
|
readonly isTransaction?: ((conn: C) => boolean) | undefined
|
|
302
308
|
}): {
|
|
303
|
-
readonly acquirer: Effect.Effect<C, SqlError>
|
|
309
|
+
readonly acquirer: Effect.Effect<C, SqlError, Scope.Scope>
|
|
304
310
|
readonly transactionAcquirer: Effect.Effect<C, SqlError, Scope.Scope>
|
|
305
311
|
readonly onCommitFailure: (conn: Connection.Connection) => Effect.Effect<void, SqlError>
|
|
306
312
|
} => {
|
|
@@ -336,15 +342,17 @@ export const makeSqliteAcquirers = <C extends Connection.Connection>(options: {
|
|
|
336
342
|
})
|
|
337
343
|
})
|
|
338
344
|
|
|
345
|
+
const lease = Effect.uninterruptibleMask((restore) =>
|
|
346
|
+
Effect.flatMap(Effect.scope, (scope) =>
|
|
347
|
+
restore(semaphore.take(1)).pipe(
|
|
348
|
+
Effect.andThen(Scope.addFinalizer(scope, semaphore.release(1))),
|
|
349
|
+
Effect.andThen(available)
|
|
350
|
+
))
|
|
351
|
+
)
|
|
352
|
+
|
|
339
353
|
return {
|
|
340
|
-
acquirer:
|
|
341
|
-
transactionAcquirer:
|
|
342
|
-
Effect.flatMap(Effect.scope, (scope) =>
|
|
343
|
-
restore(semaphore.take(1)).pipe(
|
|
344
|
-
Effect.andThen(Scope.addFinalizer(scope, semaphore.release(1))),
|
|
345
|
-
Effect.andThen(available)
|
|
346
|
-
))
|
|
347
|
-
),
|
|
354
|
+
acquirer: lease,
|
|
355
|
+
transactionAcquirer: lease,
|
|
348
356
|
onCommitFailure: (conn) => Effect.tapCause(rollback(conn as C), (cause) => Effect.sync(() => poison(cause)))
|
|
349
357
|
}
|
|
350
358
|
}
|
|
@@ -359,6 +367,18 @@ const isNoTransactionError = (error: SqlError): boolean => {
|
|
|
359
367
|
return /no transaction is active/i.test(message);
|
|
360
368
|
}
|
|
361
369
|
|
|
370
|
+
/**
|
|
371
|
+
* A failed COMMIT cleanup leaves the connection in an unsafe state, so both the
|
|
372
|
+
* COMMIT error and the cleanup error are promoted to defects: typed recovery
|
|
373
|
+
* must not be able to hide them.
|
|
374
|
+
*/
|
|
375
|
+
const failuresToDefects = <E>(cause: Cause.Cause<E>): Cause.Cause<never> =>
|
|
376
|
+
Cause.fromReasons(cause.reasons.map((reason) =>
|
|
377
|
+
Cause.isFailReason(reason)
|
|
378
|
+
? Cause.makeDieReason(reason.error).annotate(Cause.reasonAnnotations(reason))
|
|
379
|
+
: reason
|
|
380
|
+
))
|
|
381
|
+
|
|
362
382
|
/**
|
|
363
383
|
* Builds a transaction wrapper that begins top-level transactions, uses
|
|
364
384
|
* savepoints for nested transactions, commits on success, and rolls back on
|
|
@@ -424,15 +444,19 @@ export const makeWithTransaction = <I, S>(options: {
|
|
|
424
444
|
)
|
|
425
445
|
),
|
|
426
446
|
(exit) => {
|
|
427
|
-
let effect: Effect.Effect<void>
|
|
447
|
+
let effect: Effect.Effect<void, SqlError>
|
|
428
448
|
if (Exit.isSuccess(exit)) {
|
|
429
449
|
if (id === 0) {
|
|
430
450
|
span.event("effect.sql.transaction.commit", clock.currentTimeNanosUnsafe())
|
|
431
451
|
const onCommitFailure = options.onCommitFailure
|
|
432
|
-
effect =
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
452
|
+
effect = onCommitFailure
|
|
453
|
+
? Effect.catchCause(options.commit(conn), (commitCause) =>
|
|
454
|
+
Effect.matchCauseEffect(onCommitFailure(conn), {
|
|
455
|
+
onFailure: (cleanupCause) =>
|
|
456
|
+
Effect.failCause(failuresToDefects(Cause.combine(commitCause, cleanupCause))),
|
|
457
|
+
onSuccess: () => Effect.failCause(commitCause)
|
|
458
|
+
}))
|
|
459
|
+
: options.commit(conn)
|
|
436
460
|
} else {
|
|
437
461
|
span.event("effect.sql.transaction.savepoint", clock.currentTimeNanosUnsafe())
|
|
438
462
|
effect = Effect.void
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a deterministic implementation of the Effect `Crypto` service for
|
|
3
|
+
* tests.
|
|
4
|
+
*
|
|
5
|
+
* Random bytes, numbers, shuffles, UUIDs, and ULIDs are derived from a seeded
|
|
6
|
+
* pseudo-random generator. Digest operations are delegated to an underlying
|
|
7
|
+
* platform `Crypto` service.
|
|
8
|
+
*
|
|
9
|
+
* @stability unstable
|
|
10
|
+
* @since 4.0.0
|
|
11
|
+
*/
|
|
12
|
+
import * as Crypto from "../Crypto.ts"
|
|
13
|
+
import * as Effect from "../Effect.ts"
|
|
14
|
+
import * as Layer from "../Layer.ts"
|
|
15
|
+
import * as Random from "../Random.ts"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a deterministic `Crypto` service using a seeded pseudo-random
|
|
19
|
+
* generator.
|
|
20
|
+
*
|
|
21
|
+
* **When to use**
|
|
22
|
+
*
|
|
23
|
+
* Use to construct a `Crypto` service whose random operations are reproducible
|
|
24
|
+
* in tests while preserving the digest implementation of a platform `Crypto`
|
|
25
|
+
* service.
|
|
26
|
+
*
|
|
27
|
+
* **Details**
|
|
28
|
+
*
|
|
29
|
+
* The effect requires an existing `Crypto` service and delegates digest
|
|
30
|
+
* operations to it. Each evaluation starts a new random sequence from `seed`;
|
|
31
|
+
* every other random operation is derived from that sequence.
|
|
32
|
+
*
|
|
33
|
+
* **Gotchas**
|
|
34
|
+
*
|
|
35
|
+
* The generated values are deterministic and must not be used for
|
|
36
|
+
* security-sensitive purposes. The sequence depends on the order in which
|
|
37
|
+
* random operations are evaluated. UUIDv7 and ULID values also include the
|
|
38
|
+
* current `Clock` time, which must be controlled separately when their complete
|
|
39
|
+
* output needs to be reproducible.
|
|
40
|
+
*
|
|
41
|
+
* @see {@link layer} for providing the service as a layer
|
|
42
|
+
*
|
|
43
|
+
* @stability unstable
|
|
44
|
+
* @category constructors
|
|
45
|
+
* @since 4.0.0
|
|
46
|
+
*/
|
|
47
|
+
export const make: (
|
|
48
|
+
seed: string | number
|
|
49
|
+
) => Effect.Effect<Crypto.Crypto, never, Crypto.Crypto> = Effect.fnUntraced(
|
|
50
|
+
function*(_seed: string | number) {
|
|
51
|
+
const crypto = yield* Crypto.Crypto
|
|
52
|
+
const seededRandom = yield* Random.Random
|
|
53
|
+
return Crypto.make({
|
|
54
|
+
randomBytes: (size) => {
|
|
55
|
+
const bytes = new Uint8Array(size)
|
|
56
|
+
for (let i = 0; i < size; i++) {
|
|
57
|
+
bytes[i] = Math.floor(seededRandom.nextDoubleUnsafe() * 256)
|
|
58
|
+
}
|
|
59
|
+
return bytes
|
|
60
|
+
},
|
|
61
|
+
digest: crypto.digest
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
(effect, seed) => Random.withSeed(effect, seed)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates a layer that provides deterministic `Crypto` random operations from
|
|
69
|
+
* a seed.
|
|
70
|
+
*
|
|
71
|
+
* **When to use**
|
|
72
|
+
*
|
|
73
|
+
* Use to provide reproducible random bytes, numbers, shuffles, UUIDs, and ULIDs
|
|
74
|
+
* to an Effect program under test.
|
|
75
|
+
*
|
|
76
|
+
* **Details**
|
|
77
|
+
*
|
|
78
|
+
* The layer requires an existing platform `Crypto` service whose digest
|
|
79
|
+
* implementation is retained. Each layer build starts the random sequence from
|
|
80
|
+
* `seed`.
|
|
81
|
+
*
|
|
82
|
+
* **Gotchas**
|
|
83
|
+
*
|
|
84
|
+
* UUIDv7 and ULID values also depend on the current `Clock`. Provide a test
|
|
85
|
+
* clock when their complete output needs to be reproducible.
|
|
86
|
+
*
|
|
87
|
+
* @see {@link make} for constructing the service directly
|
|
88
|
+
*
|
|
89
|
+
* @stability unstable
|
|
90
|
+
* @category layers
|
|
91
|
+
* @since 4.0.0
|
|
92
|
+
*/
|
|
93
|
+
export const layer = (seed: string | number): Layer.Layer<Crypto.Crypto, never, Crypto.Crypto> =>
|
|
94
|
+
Layer.effect(Crypto.Crypto, make(seed))
|
package/src/testing/index.ts
CHANGED
|
@@ -17,6 +17,12 @@ export * as TestClock from "./TestClock.ts"
|
|
|
17
17
|
*/
|
|
18
18
|
export * as TestConsole from "./TestConsole.ts"
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @stability unstable
|
|
22
|
+
* @since 4.0.0
|
|
23
|
+
*/
|
|
24
|
+
export * as TestCrypto from "./TestCrypto.ts"
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* @stability unstable
|
|
22
28
|
* @since 4.0.0
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* @stability unstable
|
|
11
11
|
* @since 4.0.0
|
|
12
12
|
*/
|
|
13
|
-
import * as Arr from "../Array.ts"
|
|
14
13
|
import type { NonEmptyReadonlyArray } from "../Array.ts"
|
|
15
14
|
import type * as Brand from "../Brand.ts"
|
|
16
15
|
import * as Cause from "../Cause.ts"
|
|
@@ -18,8 +17,9 @@ import * as Context from "../Context.ts"
|
|
|
18
17
|
import * as Effect from "../Effect.ts"
|
|
19
18
|
import * as Base64Url from "../encoding/Base64Url.ts"
|
|
20
19
|
import * as Exit from "../Exit.ts"
|
|
21
|
-
import * as
|
|
20
|
+
import * as Fiber from "../Fiber.ts"
|
|
22
21
|
import { dual } from "../Function.ts"
|
|
22
|
+
import * as Latch from "../Latch.ts"
|
|
23
23
|
import * as Option from "../Option.ts"
|
|
24
24
|
import * as Schema from "../Schema.ts"
|
|
25
25
|
import * as SchemaGetter from "../SchemaGetter.ts"
|
|
@@ -134,6 +134,25 @@ const CurrentAttempt = Context.Reference<number>(
|
|
|
134
134
|
{ defaultValue: () => 1 }
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
+
/** Awaits registered under the instance copy of a `recordHeld` body. */
|
|
138
|
+
const awaitOwners = new WeakMap<WorkflowInstance["Service"], Array<string>>()
|
|
139
|
+
|
|
140
|
+
const registerAwait = (instance: WorkflowInstance["Service"], name: string) => {
|
|
141
|
+
instance.awaitedDeferreds.set(name, (instance.awaitedDeferreds.get(name) ?? 0) + 1)
|
|
142
|
+
awaitOwners.get(instance)?.push(name)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const releaseAwaits = (instance: WorkflowInstance["Service"], names: Array<string>) => {
|
|
146
|
+
for (const name of names) {
|
|
147
|
+
const count = instance.awaitedDeferreds.get(name)! - 1
|
|
148
|
+
if (count === 0) {
|
|
149
|
+
instance.awaitedDeferreds.delete(name)
|
|
150
|
+
} else {
|
|
151
|
+
instance.awaitedDeferreds.set(name, count)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
137
156
|
const await_: <Success extends Schema.Constraint, Error extends Schema.Constraint>(
|
|
138
157
|
self: DurableDeferred<Success, Error>
|
|
139
158
|
) => Effect.Effect<
|
|
@@ -150,7 +169,7 @@ const await_: <Success extends Schema.Constraint, Error extends Schema.Constrain
|
|
|
150
169
|
const engine = yield* EngineTag
|
|
151
170
|
const instance = yield* InstanceTag
|
|
152
171
|
// Register before the read so any later completion can preempt the run.
|
|
153
|
-
instance
|
|
172
|
+
registerAwait(instance, self.name)
|
|
154
173
|
const exit = yield* Workflow.wrapActivityResult(
|
|
155
174
|
engine.deferredResult(self),
|
|
156
175
|
Option.isNone
|
|
@@ -180,6 +199,20 @@ export {
|
|
|
180
199
|
* Runs an effect and records its exit into the durable deferred, resuming
|
|
181
200
|
* workflows that are waiting on that deferred.
|
|
182
201
|
*
|
|
202
|
+
* **Details**
|
|
203
|
+
*
|
|
204
|
+
* The effect and the write of its exit count as one in-flight activity of the
|
|
205
|
+
* workflow, so concurrent branches parked on durable waits do not suspend the
|
|
206
|
+
* run until the exit is recorded. This applies even when the effect itself is
|
|
207
|
+
* not an activity. If the effect parks on a durable wait, the run suspends
|
|
208
|
+
* once the other in-flight activities finish, and nothing is recorded.
|
|
209
|
+
*
|
|
210
|
+
* **Gotchas**
|
|
211
|
+
*
|
|
212
|
+
* A long-running effect keeps the run active instead of letting a parked
|
|
213
|
+
* sibling suspend it. Completing a deferred the run is waiting on still
|
|
214
|
+
* interrupts and replays the run.
|
|
215
|
+
*
|
|
183
216
|
* @stability unstable
|
|
184
217
|
* @category combinators
|
|
185
218
|
* @since 4.0.0
|
|
@@ -189,6 +222,20 @@ export const into: {
|
|
|
189
222
|
* Runs an effect and records its exit into the durable deferred, resuming
|
|
190
223
|
* workflows that are waiting on that deferred.
|
|
191
224
|
*
|
|
225
|
+
* **Details**
|
|
226
|
+
*
|
|
227
|
+
* The effect and the write of its exit count as one in-flight activity of the
|
|
228
|
+
* workflow, so concurrent branches parked on durable waits do not suspend the
|
|
229
|
+
* run until the exit is recorded. This applies even when the effect itself is
|
|
230
|
+
* not an activity. If the effect parks on a durable wait, the run suspends
|
|
231
|
+
* once the other in-flight activities finish, and nothing is recorded.
|
|
232
|
+
*
|
|
233
|
+
* **Gotchas**
|
|
234
|
+
*
|
|
235
|
+
* A long-running effect keeps the run active instead of letting a parked
|
|
236
|
+
* sibling suspend it. Completing a deferred the run is waiting on still
|
|
237
|
+
* interrupts and replays the run.
|
|
238
|
+
*
|
|
192
239
|
* @stability unstable
|
|
193
240
|
* @category combinators
|
|
194
241
|
* @since 4.0.0
|
|
@@ -210,6 +257,20 @@ export const into: {
|
|
|
210
257
|
* Runs an effect and records its exit into the durable deferred, resuming
|
|
211
258
|
* workflows that are waiting on that deferred.
|
|
212
259
|
*
|
|
260
|
+
* **Details**
|
|
261
|
+
*
|
|
262
|
+
* The effect and the write of its exit count as one in-flight activity of the
|
|
263
|
+
* workflow, so concurrent branches parked on durable waits do not suspend the
|
|
264
|
+
* run until the exit is recorded. This applies even when the effect itself is
|
|
265
|
+
* not an activity. If the effect parks on a durable wait, the run suspends
|
|
266
|
+
* once the other in-flight activities finish, and nothing is recorded.
|
|
267
|
+
*
|
|
268
|
+
* **Gotchas**
|
|
269
|
+
*
|
|
270
|
+
* A long-running effect keeps the run active instead of letting a parked
|
|
271
|
+
* sibling suspend it. Completing a deferred the run is waiting on still
|
|
272
|
+
* interrupts and replays the run.
|
|
273
|
+
*
|
|
213
274
|
* @stability unstable
|
|
214
275
|
* @category combinators
|
|
215
276
|
* @since 4.0.0
|
|
@@ -243,42 +304,104 @@ export const into: {
|
|
|
243
304
|
| Error["DecodingServices"]
|
|
244
305
|
| Success["EncodingServices"]
|
|
245
306
|
| Error["EncodingServices"]
|
|
246
|
-
> =>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
307
|
+
> => recordHeld(effect, self, false)
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Records `exit` into `self`. An interrupt-only exit is never a result: the
|
|
312
|
+
* effect was suspended, preempted or interrupted, so nothing is recorded.
|
|
313
|
+
* Interrupts mixed into a failure are dropped from the recorded cause.
|
|
314
|
+
*/
|
|
315
|
+
const recordExit = (
|
|
316
|
+
engine: WorkflowEngine["Service"],
|
|
317
|
+
instance: WorkflowInstance["Service"],
|
|
318
|
+
self: DurableDeferred<any, any>,
|
|
319
|
+
exit: Exit.Exit<any, any>
|
|
320
|
+
) => {
|
|
321
|
+
if (Exit.isFailure(exit)) {
|
|
322
|
+
if (Cause.hasInterruptsOnly(exit.cause)) return Effect.void
|
|
323
|
+
if (Cause.hasInterrupts(exit.cause)) {
|
|
324
|
+
exit = Exit.failCause(Cause.fromReasons(
|
|
325
|
+
exit.cause.reasons.filter((reason) => !Cause.isInterruptReason(reason))
|
|
326
|
+
))
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return engine.deferredDone(self, {
|
|
330
|
+
workflowName: instance.workflow._tag,
|
|
331
|
+
executionId: instance.executionId,
|
|
332
|
+
deferredName: self.name,
|
|
333
|
+
exit
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Runs `effect` as one activity of the enclosing workflow and records its exit
|
|
339
|
+
* into `self`, returning a previously recorded exit instead when `checkCache`
|
|
340
|
+
* is set. The hold spans the lookup, `effect` and the write, so a parked
|
|
341
|
+
* sibling cannot suspend the run before the continuation starts.
|
|
342
|
+
*/
|
|
343
|
+
const recordHeld = Effect.fnUntraced(function*<A, E, R>(
|
|
344
|
+
effect: Effect.Effect<A, E, R>,
|
|
345
|
+
self: DurableDeferred<any, any>,
|
|
346
|
+
checkCache: boolean
|
|
347
|
+
): Effect.fn.Return<A, E, R | WorkflowEngine | WorkflowInstance> {
|
|
348
|
+
const engine = yield* EngineTag
|
|
349
|
+
const instance = yield* InstanceTag
|
|
350
|
+
const exit = yield* Workflow.wrapActivityResult(
|
|
351
|
+
Effect.gen(function*() {
|
|
352
|
+
if (checkCache) {
|
|
353
|
+
const cached = yield* engine.deferredResult(self)
|
|
354
|
+
if (Option.isSome(cached)) return cached
|
|
355
|
+
}
|
|
356
|
+
// `effect` counts on its own activity state, so a durable await inside
|
|
357
|
+
// it waits for its own siblings and not for the hold above. The copy
|
|
358
|
+
// starts unsuspended: a parked arm of an enclosing race leaves that
|
|
359
|
+
// copy marked suspended while only interrupting its own fiber.
|
|
360
|
+
const local: WorkflowInstance["Service"] = {
|
|
361
|
+
...instance,
|
|
362
|
+
suspended: false,
|
|
363
|
+
activityState: { count: 0, latch: Latch.makeUnsafe() }
|
|
364
|
+
}
|
|
365
|
+
// A losing branch parked on a durable wait has already exited when the
|
|
366
|
+
// race settles, so this body owns the waits registered beneath it.
|
|
367
|
+
const owned: Array<string> = []
|
|
368
|
+
awaitOwners.set(local, owned)
|
|
369
|
+
// A parked `effect` interrupts the fiber it runs on. The child fiber
|
|
370
|
+
// keeps that from skipping the suspension handling below.
|
|
371
|
+
const fiber = yield* effect.pipe(
|
|
372
|
+
Effect.provideService(InstanceTag, local),
|
|
373
|
+
Effect.onExit((exit) => recordExit(engine, instance, self, exit)),
|
|
374
|
+
Effect.forkChild({ startImmediately: true })
|
|
375
|
+
)
|
|
376
|
+
return yield* Effect.onInterrupt(Fiber.await(fiber), () => Fiber.interrupt(fiber)).pipe(
|
|
377
|
+
Effect.map((exit) =>
|
|
378
|
+
local.suspended && Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
|
|
379
|
+
? Option.none()
|
|
380
|
+
: Option.some(exit)
|
|
381
|
+
),
|
|
382
|
+
// The waits are over once the body has a result. Otherwise every branch
|
|
383
|
+
// parked, or an enclosing race interrupted the body, and the waits move
|
|
384
|
+
// to the enclosing owner, if any, to be released when that race settles.
|
|
385
|
+
Effect.onExit((result) =>
|
|
386
|
+
Effect.sync(() => {
|
|
387
|
+
if (Exit.isSuccess(result) && Option.isSome(result.value)) {
|
|
388
|
+
releaseAwaits(instance, owned)
|
|
389
|
+
} else {
|
|
390
|
+
awaitOwners.get(instance)?.push(...owned)
|
|
270
391
|
}
|
|
271
|
-
yield* engine.deferredDone(self, {
|
|
272
|
-
workflowName: instance.workflow._tag,
|
|
273
|
-
executionId: instance.executionId,
|
|
274
|
-
deferredName: self.name,
|
|
275
|
-
exit
|
|
276
|
-
})
|
|
277
392
|
})
|
|
278
393
|
)
|
|
279
|
-
|
|
280
|
-
)
|
|
281
|
-
|
|
394
|
+
)
|
|
395
|
+
}),
|
|
396
|
+
// Every branch of `effect` parked: release the hold and let the enclosing
|
|
397
|
+
// activities finish before suspending. External preemption does not wait.
|
|
398
|
+
Option.isNone
|
|
399
|
+
)
|
|
400
|
+
if (Option.isNone(exit)) {
|
|
401
|
+
return yield* Workflow.suspend(instance)
|
|
402
|
+
}
|
|
403
|
+
return yield* exit.value
|
|
404
|
+
})
|
|
282
405
|
|
|
283
406
|
/**
|
|
284
407
|
* Runs effects as a durable race, returning a previously persisted result when
|
|
@@ -312,17 +435,7 @@ export const raceAll = <
|
|
|
312
435
|
success: options.success,
|
|
313
436
|
error: options.error
|
|
314
437
|
})
|
|
315
|
-
return Effect.
|
|
316
|
-
const engine = yield* EngineTag
|
|
317
|
-
const exit = yield* engine.deferredResult(deferred)
|
|
318
|
-
if (Option.isSome(exit)) {
|
|
319
|
-
return yield* exit.value
|
|
320
|
-
}
|
|
321
|
-
return yield* into(
|
|
322
|
-
Effect.raceAll(options.effects),
|
|
323
|
-
deferred
|
|
324
|
-
)
|
|
325
|
-
})
|
|
438
|
+
return recordHeld(Effect.raceAll(options.effects), deferred, true)
|
|
326
439
|
}
|
|
327
440
|
|
|
328
441
|
/**
|