effect 4.0.6 → 4.1.0
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/README.md +1 -1
- package/dist/Arbitrary.d.ts +10 -2
- package/dist/Arbitrary.d.ts.map +1 -1
- package/dist/Arbitrary.js +2 -1
- package/dist/Arbitrary.js.map +1 -1
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +3 -0
- package/dist/BigDecimal.js.map +1 -1
- package/dist/Channel.d.ts +12 -6
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +30 -9
- package/dist/Channel.js.map +1 -1
- package/dist/Config.d.ts +7 -1
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js.map +1 -1
- package/dist/Context.d.ts +18 -1
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +27 -8
- package/dist/Context.js.map +1 -1
- package/dist/Cron.d.ts +81 -11
- package/dist/Cron.d.ts.map +1 -1
- package/dist/Cron.js +101 -26
- package/dist/Cron.js.map +1 -1
- package/dist/Data.d.ts +54 -235
- package/dist/Data.d.ts.map +1 -1
- package/dist/Data.js.map +1 -1
- package/dist/Duration.d.ts +78 -0
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +123 -0
- package/dist/Duration.js.map +1 -1
- package/dist/Effect.d.ts +165 -8
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +12 -2
- package/dist/Effect.js.map +1 -1
- package/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +8 -1
- package/dist/Equal.js.map +1 -1
- package/dist/Exit.d.ts +3 -3
- package/dist/Exit.d.ts.map +1 -1
- package/dist/Exit.js.map +1 -1
- package/dist/FileSystem.d.ts +59 -4
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js +9 -2
- package/dist/FileSystem.js.map +1 -1
- package/dist/Formatter.d.ts.map +1 -1
- package/dist/Formatter.js +27 -13
- package/dist/Formatter.js.map +1 -1
- package/dist/Glob.d.ts +165 -0
- package/dist/Glob.d.ts.map +1 -0
- package/dist/Glob.js +127 -0
- package/dist/Glob.js.map +1 -0
- package/dist/Hash.d.ts.map +1 -1
- package/dist/Hash.js +4 -1
- package/dist/Hash.js.map +1 -1
- package/dist/JsonPatch.js +4 -0
- package/dist/JsonPatch.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +13 -40
- package/dist/JsonSchema.js.map +1 -1
- package/dist/Match.d.ts +86 -0
- package/dist/Match.d.ts.map +1 -1
- package/dist/Match.js +72 -0
- package/dist/Match.js.map +1 -1
- package/dist/Metric.d.ts +3 -3
- package/dist/MutableList.d.ts +5 -2
- package/dist/MutableList.d.ts.map +1 -1
- package/dist/MutableList.js +7 -5
- package/dist/MutableList.js.map +1 -1
- package/dist/MutableRef.d.ts.map +1 -1
- package/dist/MutableRef.js +7 -0
- package/dist/MutableRef.js.map +1 -1
- package/dist/PartitionedSemaphore.d.ts +2 -1
- package/dist/PartitionedSemaphore.d.ts.map +1 -1
- package/dist/PartitionedSemaphore.js +22 -12
- package/dist/PartitionedSemaphore.js.map +1 -1
- package/dist/Pool.d.ts +51 -0
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +136 -36
- package/dist/Pool.js.map +1 -1
- package/dist/PubSub.d.ts.map +1 -1
- package/dist/PubSub.js +5 -6
- package/dist/PubSub.js.map +1 -1
- package/dist/Random.d.ts.map +1 -1
- package/dist/Random.js +3 -3
- package/dist/Random.js.map +1 -1
- package/dist/RcMap.d.ts.map +1 -1
- package/dist/RcMap.js +8 -2
- package/dist/RcMap.js.map +1 -1
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +8 -0
- package/dist/Ref.js.map +1 -1
- package/dist/Schedule.d.ts +18 -1
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +8 -3
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +690 -49
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +687 -28
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaAST.d.ts +4 -2
- package/dist/SchemaAST.d.ts.map +1 -1
- package/dist/SchemaAST.js +14 -17
- package/dist/SchemaAST.js.map +1 -1
- package/dist/SchemaGetter.d.ts +6 -1
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +22 -11
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/SchemaParser.d.ts.map +1 -1
- package/dist/SchemaParser.js +48 -22
- package/dist/SchemaParser.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +33 -10
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +39 -13
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +12 -4
- package/dist/SchemaTransformation.d.ts.map +1 -1
- package/dist/SchemaTransformation.js +15 -5
- package/dist/SchemaTransformation.js.map +1 -1
- package/dist/ScopedCache.d.ts.map +1 -1
- package/dist/ScopedCache.js +5 -7
- package/dist/ScopedCache.js.map +1 -1
- package/dist/Semaphore.d.ts +130 -0
- package/dist/Semaphore.d.ts.map +1 -1
- package/dist/Semaphore.js +94 -35
- package/dist/Semaphore.js.map +1 -1
- package/dist/Sink.d.ts.map +1 -1
- package/dist/Sink.js +7 -2
- package/dist/Sink.js.map +1 -1
- package/dist/Stream.d.ts +13 -4
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +31 -10
- package/dist/Stream.js.map +1 -1
- package/dist/String.d.ts +3 -0
- package/dist/String.d.ts.map +1 -1
- package/dist/String.js +3 -0
- package/dist/String.js.map +1 -1
- package/dist/Terminal.d.ts +18 -1
- package/dist/Terminal.d.ts.map +1 -1
- package/dist/Terminal.js +1 -1
- package/dist/Terminal.js.map +1 -1
- package/dist/Tracer.d.ts +11 -1
- package/dist/Tracer.d.ts.map +1 -1
- package/dist/Tracer.js +6 -1
- package/dist/Tracer.js.map +1 -1
- package/dist/ai/AiError.d.ts +43 -116
- package/dist/ai/AiError.d.ts.map +1 -1
- package/dist/ai/AiError.js +53 -72
- package/dist/ai/AiError.js.map +1 -1
- package/dist/ai/Decision.d.ts +196 -15
- package/dist/ai/Decision.d.ts.map +1 -1
- package/dist/ai/Decision.js +130 -0
- package/dist/ai/Decision.js.map +1 -1
- package/dist/ai/DecisionModel.d.ts +54 -13
- package/dist/ai/DecisionModel.d.ts.map +1 -1
- package/dist/ai/DecisionModel.js +123 -12
- package/dist/ai/DecisionModel.js.map +1 -1
- package/dist/ai/LanguageModel.d.ts.map +1 -1
- package/dist/ai/LanguageModel.js +9 -0
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpSchema.d.ts +110 -110
- package/dist/ai/McpSchema.d.ts.map +1 -1
- package/dist/ai/McpServer.d.ts +630 -29
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +583 -131
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/McpTest.d.ts +130 -0
- package/dist/ai/McpTest.d.ts.map +1 -0
- package/dist/ai/McpTest.js +237 -0
- package/dist/ai/McpTest.js.map +1 -0
- package/dist/ai/Prompt.d.ts +52 -0
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +38 -0
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Response.d.ts +17 -28
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js +5 -10
- package/dist/ai/Response.js.map +1 -1
- package/dist/ai/Tool.d.ts +43 -1
- package/dist/ai/Tool.d.ts.map +1 -1
- package/dist/ai/Tool.js +5 -2
- package/dist/ai/Tool.js.map +1 -1
- package/dist/ai/Toolkit.d.ts.map +1 -1
- package/dist/ai/Toolkit.js +8 -1
- package/dist/ai/Toolkit.js.map +1 -1
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/internal/mcpCore.js +17 -12
- package/dist/ai/internal/mcpCore.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +18 -10
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js +13 -0
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +5 -3
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/ai/internal/mcpStatefulRuntime.js +6 -1
- package/dist/ai/internal/mcpStatefulRuntime.js.map +1 -1
- package/dist/cli/Argument.d.ts +21 -0
- package/dist/cli/Argument.d.ts.map +1 -1
- package/dist/cli/Argument.js +7 -0
- package/dist/cli/Argument.js.map +1 -1
- package/dist/cli/CliError.d.ts +78 -0
- package/dist/cli/CliError.d.ts.map +1 -1
- package/dist/cli/CliError.js +65 -3
- package/dist/cli/CliError.js.map +1 -1
- package/dist/cli/CliOutput.d.ts +220 -7
- package/dist/cli/CliOutput.d.ts.map +1 -1
- package/dist/cli/CliOutput.js +156 -43
- package/dist/cli/CliOutput.js.map +1 -1
- package/dist/cli/Command.d.ts +47 -16
- package/dist/cli/Command.d.ts.map +1 -1
- package/dist/cli/Command.js +67 -29
- package/dist/cli/Command.js.map +1 -1
- package/dist/cli/Completions.d.ts +58 -0
- package/dist/cli/Completions.d.ts.map +1 -1
- package/dist/cli/Completions.js +49 -9
- package/dist/cli/Completions.js.map +1 -1
- package/dist/cli/Flag.d.ts +122 -0
- package/dist/cli/Flag.d.ts.map +1 -1
- package/dist/cli/Flag.js +42 -0
- package/dist/cli/Flag.js.map +1 -1
- package/dist/cli/GlobalFlag.d.ts.map +1 -1
- package/dist/cli/GlobalFlag.js +15 -7
- package/dist/cli/GlobalFlag.js.map +1 -1
- package/dist/cli/HelpDoc.d.ts +15 -1
- package/dist/cli/HelpDoc.d.ts.map +1 -1
- package/dist/cli/Param.d.ts +15 -0
- package/dist/cli/Param.d.ts.map +1 -1
- package/dist/cli/Param.js +23 -2
- package/dist/cli/Param.js.map +1 -1
- package/dist/cli/Primitive.d.ts +14 -0
- package/dist/cli/Primitive.d.ts.map +1 -1
- package/dist/cli/Primitive.js +15 -1
- package/dist/cli/Primitive.js.map +1 -1
- package/dist/cli/Prompt.d.ts +1 -0
- package/dist/cli/Prompt.d.ts.map +1 -1
- package/dist/cli/Prompt.js +1 -0
- package/dist/cli/Prompt.js.map +1 -1
- package/dist/cli/TerminalCapabilities.d.ts +3 -2
- package/dist/cli/TerminalCapabilities.d.ts.map +1 -1
- package/dist/cli/TerminalCapabilities.js +3 -1
- package/dist/cli/TerminalCapabilities.js.map +1 -1
- package/dist/cli/internal/completions/bash.js +73 -27
- package/dist/cli/internal/completions/bash.js.map +1 -1
- package/dist/cli/internal/help.d.ts.map +1 -1
- package/dist/cli/internal/help.js +17 -10
- package/dist/cli/internal/help.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +45 -10
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +23 -45
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +6 -7
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +151 -49
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/ShardingConfig.d.ts +76 -0
- package/dist/cluster/ShardingConfig.d.ts.map +1 -1
- package/dist/cluster/ShardingConfig.js +4 -0
- package/dist/cluster/ShardingConfig.js.map +1 -1
- package/dist/cluster/SingleRunner.d.ts +3 -3
- package/dist/cluster/SingleRunner.d.ts.map +1 -1
- package/dist/cluster/SingleRunner.js +1 -1
- package/dist/cluster/Snowflake.d.ts.map +1 -1
- package/dist/cluster/Snowflake.js +3 -7
- package/dist/cluster/Snowflake.js.map +1 -1
- package/dist/cluster/SqlMessageStorage.d.ts +37 -6
- package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/cluster/SqlMessageStorage.js +34 -7
- package/dist/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/cluster/SqlRunnerStorage.js +109 -46
- package/dist/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +54 -20
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/crypto/Aead.d.ts +322 -0
- package/dist/crypto/Aead.d.ts.map +1 -0
- package/dist/crypto/Aead.js +331 -0
- package/dist/crypto/Aead.js.map +1 -0
- package/dist/crypto/Cipher.d.ts +235 -0
- package/dist/crypto/Cipher.d.ts.map +1 -0
- package/dist/crypto/Cipher.js +233 -0
- package/dist/crypto/Cipher.js.map +1 -0
- package/dist/crypto/CryptoError.d.ts +212 -0
- package/dist/crypto/CryptoError.d.ts.map +1 -0
- package/dist/crypto/CryptoError.js +202 -0
- package/dist/crypto/CryptoError.js.map +1 -0
- package/dist/crypto/Digest.d.ts +143 -0
- package/dist/crypto/Digest.d.ts.map +1 -0
- package/dist/crypto/Digest.js +146 -0
- package/dist/crypto/Digest.js.map +1 -0
- package/dist/crypto/Kdf.d.ts +220 -0
- package/dist/crypto/Kdf.d.ts.map +1 -0
- package/dist/crypto/Kdf.js +212 -0
- package/dist/crypto/Kdf.js.map +1 -0
- package/dist/crypto/KeyAgreement.d.ts +323 -0
- package/dist/crypto/KeyAgreement.d.ts.map +1 -0
- package/dist/crypto/KeyAgreement.js +303 -0
- package/dist/crypto/KeyAgreement.js.map +1 -0
- package/dist/crypto/KeyFormat.d.ts +309 -0
- package/dist/crypto/KeyFormat.d.ts.map +1 -0
- package/dist/crypto/KeyFormat.js +556 -0
- package/dist/crypto/KeyFormat.js.map +1 -0
- package/dist/crypto/KeyWrap.d.ts +389 -0
- package/dist/crypto/KeyWrap.d.ts.map +1 -0
- package/dist/crypto/KeyWrap.js +381 -0
- package/dist/crypto/KeyWrap.js.map +1 -0
- package/dist/crypto/Mac.d.ts +277 -0
- package/dist/crypto/Mac.d.ts.map +1 -0
- package/dist/crypto/Mac.js +288 -0
- package/dist/crypto/Mac.js.map +1 -0
- package/dist/crypto/SecureRandom.d.ts +340 -0
- package/dist/crypto/SecureRandom.d.ts.map +1 -0
- package/dist/crypto/SecureRandom.js +257 -0
- package/dist/crypto/SecureRandom.js.map +1 -0
- package/dist/crypto/Signature.d.ts +448 -0
- package/dist/crypto/Signature.d.ts.map +1 -0
- package/dist/crypto/Signature.js +484 -0
- package/dist/crypto/Signature.js.map +1 -0
- package/dist/crypto/WebCrypto.d.ts +61 -0
- package/dist/crypto/WebCrypto.d.ts.map +1 -0
- package/dist/crypto/WebCrypto.js +63 -0
- package/dist/crypto/WebCrypto.js.map +1 -0
- package/dist/crypto/index.d.ts +65 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +66 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/internal/asymmetric.d.ts +2 -0
- package/dist/crypto/internal/asymmetric.d.ts.map +1 -0
- package/dist/crypto/internal/asymmetric.js +210 -0
- package/dist/crypto/internal/asymmetric.js.map +1 -0
- package/dist/crypto/internal/chacha.d.ts +46 -0
- package/dist/crypto/internal/chacha.d.ts.map +1 -0
- package/dist/crypto/internal/chacha.js +885 -0
- package/dist/crypto/internal/chacha.js.map +1 -0
- package/dist/crypto/internal/der.d.ts +6 -0
- package/dist/crypto/internal/der.d.ts.map +1 -0
- package/dist/crypto/internal/der.js +86 -0
- package/dist/crypto/internal/der.js.map +1 -0
- package/dist/crypto/internal/digest.d.ts +2 -0
- package/dist/crypto/internal/digest.d.ts.map +1 -0
- package/dist/crypto/internal/digest.js +15 -0
- package/dist/crypto/internal/digest.js.map +1 -0
- package/dist/crypto/internal/key.d.ts +2 -0
- package/dist/crypto/internal/key.d.ts.map +1 -0
- package/dist/crypto/internal/key.js +170 -0
- package/dist/crypto/internal/key.js.map +1 -0
- package/dist/crypto/internal/md5.d.ts +39 -0
- package/dist/crypto/internal/md5.d.ts.map +1 -0
- package/dist/crypto/internal/md5.js +179 -0
- package/dist/crypto/internal/md5.js.map +1 -0
- package/dist/crypto/internal/webCrypto.d.ts +2 -0
- package/dist/crypto/internal/webCrypto.d.ts.map +1 -0
- package/dist/crypto/internal/webCrypto.js +111 -0
- package/dist/crypto/internal/webCrypto.js.map +1 -0
- package/dist/devtools/DevToolsClient.d.ts.map +1 -1
- package/dist/devtools/DevToolsClient.js +2 -0
- package/dist/devtools/DevToolsClient.js.map +1 -1
- package/dist/encoding/Base64.js +36 -0
- package/dist/encoding/Base64.js.map +1 -1
- package/dist/encoding/Base64Url.d.ts.map +1 -1
- package/dist/encoding/Base64Url.js +3 -2
- package/dist/encoding/Base64Url.js.map +1 -1
- package/dist/encoding/Csv.d.ts +608 -0
- package/dist/encoding/Csv.d.ts.map +1 -0
- package/dist/encoding/Csv.js +718 -0
- package/dist/encoding/Csv.js.map +1 -0
- package/dist/encoding/Hex.d.ts +3 -2
- package/dist/encoding/Hex.d.ts.map +1 -1
- package/dist/encoding/Hex.js +3 -2
- package/dist/encoding/Hex.js.map +1 -1
- package/dist/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/encoding/SchemaBinary.js +42 -4
- package/dist/encoding/SchemaBinary.js.map +1 -1
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +3 -3
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/encoding/Toml.d.ts +17 -3
- package/dist/encoding/Toml.d.ts.map +1 -1
- package/dist/encoding/Toml.js +124 -31
- package/dist/encoding/Toml.js.map +1 -1
- package/dist/encoding/Yaml.d.ts +8 -1
- package/dist/encoding/Yaml.d.ts.map +1 -1
- package/dist/encoding/Yaml.js +54 -18
- package/dist/encoding/Yaml.js.map +1 -1
- package/dist/encoding/index.d.ts +5 -0
- package/dist/encoding/index.d.ts.map +1 -1
- package/dist/encoding/index.js +5 -0
- package/dist/encoding/index.js.map +1 -1
- package/dist/eventlog/EventJournal.d.ts.map +1 -1
- package/dist/eventlog/EventJournal.js +6 -3
- package/dist/eventlog/EventJournal.js.map +1 -1
- package/dist/eventlog/EventLog.d.ts +119 -4
- package/dist/eventlog/EventLog.d.ts.map +1 -1
- package/dist/eventlog/EventLog.js +200 -12
- package/dist/eventlog/EventLog.js.map +1 -1
- package/dist/eventlog/EventLogEncryption.d.ts +41 -4
- package/dist/eventlog/EventLogEncryption.d.ts.map +1 -1
- package/dist/eventlog/EventLogEncryption.js +83 -64
- package/dist/eventlog/EventLogEncryption.js.map +1 -1
- package/dist/eventlog/EventLogMessage.d.ts +279 -0
- package/dist/eventlog/EventLogMessage.d.ts.map +1 -1
- package/dist/eventlog/EventLogMessage.js +217 -1
- package/dist/eventlog/EventLogMessage.js.map +1 -1
- package/dist/eventlog/EventLogRemote.d.ts +200 -28
- package/dist/eventlog/EventLogRemote.d.ts.map +1 -1
- package/dist/eventlog/EventLogRemote.js +186 -16
- package/dist/eventlog/EventLogRemote.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts +3 -0
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +18 -10
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.d.ts +326 -5
- package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.js +769 -14
- package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.d.ts +5 -3
- package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.js +41 -64
- package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
- package/dist/eventlog/EventRow.d.ts +4 -0
- package/dist/eventlog/EventRow.d.ts.map +1 -1
- package/dist/eventlog/EventRow.js +12 -2
- package/dist/eventlog/EventRow.js.map +1 -1
- package/dist/eventlog/SqlEventJournal.d.ts.map +1 -1
- package/dist/eventlog/SqlEventJournal.js +47 -27
- package/dist/eventlog/SqlEventJournal.js.map +1 -1
- package/dist/eventlog/SqlEventLogServerEncrypted.d.ts +21 -5
- package/dist/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/SqlEventLogServerEncrypted.js +1048 -113
- package/dist/eventlog/SqlEventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/SqlEventLogServerUnencrypted.js +2 -2
- package/dist/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
- package/dist/eventlog/internal/batches.d.ts +12 -0
- package/dist/eventlog/internal/batches.d.ts.map +1 -0
- package/dist/eventlog/internal/batches.js +26 -0
- package/dist/eventlog/internal/batches.js.map +1 -0
- package/dist/eventlog/internal/compaction.d.ts +38 -0
- package/dist/eventlog/internal/compaction.d.ts.map +1 -0
- package/dist/eventlog/internal/compaction.js +33 -0
- package/dist/eventlog/internal/compaction.js.map +1 -0
- package/dist/eventlog/internal/conflicts.d.ts +0 -1
- package/dist/eventlog/internal/conflicts.d.ts.map +1 -1
- package/dist/eventlog/internal/conflicts.js +0 -3
- package/dist/eventlog/internal/conflicts.js.map +1 -1
- package/dist/eventlog/internal/identityRootSecretDerivation.js +66 -67
- package/dist/eventlog/internal/identityRootSecretDerivation.js.map +1 -1
- package/dist/eventlog/internal/remoteCompaction.d.ts +31 -0
- package/dist/eventlog/internal/remoteCompaction.d.ts.map +1 -0
- package/dist/eventlog/internal/remoteCompaction.js +76 -0
- package/dist/eventlog/internal/remoteCompaction.js.map +1 -0
- package/dist/http/Cookies.d.ts +172 -0
- package/dist/http/Cookies.d.ts.map +1 -1
- package/dist/http/Cookies.js +153 -10
- package/dist/http/Cookies.js.map +1 -1
- package/dist/http/FetchHttpClient.d.ts +26 -2
- package/dist/http/FetchHttpClient.d.ts.map +1 -1
- package/dist/http/FetchHttpClient.js +78 -15
- package/dist/http/FetchHttpClient.js.map +1 -1
- package/dist/http/HttpClient.d.ts +502 -8
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +296 -12
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpClientError.d.ts +8 -1
- package/dist/http/HttpClientError.d.ts.map +1 -1
- package/dist/http/HttpClientError.js +37 -1
- package/dist/http/HttpClientError.js.map +1 -1
- package/dist/http/HttpClientRequest.d.ts +9 -5
- package/dist/http/HttpClientRequest.d.ts.map +1 -1
- package/dist/http/HttpClientRequest.js +3 -4
- package/dist/http/HttpClientRequest.js.map +1 -1
- package/dist/http/HttpClientResponse.d.ts.map +1 -1
- package/dist/http/HttpClientResponse.js +3 -2
- package/dist/http/HttpClientResponse.js.map +1 -1
- package/dist/http/HttpMethod.d.ts +7 -1
- package/dist/http/HttpMethod.d.ts.map +1 -1
- package/dist/http/HttpMethod.js +3 -8
- package/dist/http/HttpMethod.js.map +1 -1
- package/dist/http/HttpMiddleware.d.ts +1 -1
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +14 -16
- package/dist/http/HttpMiddleware.js.map +1 -1
- package/dist/http/HttpRouter.d.ts.map +1 -1
- package/dist/http/HttpRouter.js +2 -1
- package/dist/http/HttpRouter.js.map +1 -1
- package/dist/http/HttpServerError.js +2 -2
- package/dist/http/HttpServerError.js.map +1 -1
- package/dist/http/HttpServerRequest.d.ts +11 -3
- package/dist/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/http/HttpServerRequest.js +77 -25
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/HttpServerResponse.d.ts.map +1 -1
- package/dist/http/HttpServerResponse.js +3 -2
- package/dist/http/HttpServerResponse.js.map +1 -1
- package/dist/http/HttpStaticServer.d.ts.map +1 -1
- package/dist/http/HttpStaticServer.js +48 -21
- package/dist/http/HttpStaticServer.js.map +1 -1
- package/dist/http/HttpTraceContext.d.ts +2 -1
- package/dist/http/HttpTraceContext.d.ts.map +1 -1
- package/dist/http/HttpTraceContext.js +7 -5
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http/MultipartParser/internal/contentType.js +3 -3
- package/dist/http/ProblemDetails.d.ts +96 -0
- package/dist/http/ProblemDetails.d.ts.map +1 -0
- package/dist/http/ProblemDetails.js +98 -0
- package/dist/http/ProblemDetails.js.map +1 -0
- package/dist/http/UrlParams.d.ts +3 -1
- package/dist/http/UrlParams.d.ts.map +1 -1
- package/dist/http/UrlParams.js +4 -2
- package/dist/http/UrlParams.js.map +1 -1
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +5 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/internal/compression.js +4 -5
- package/dist/http/internal/compression.js.map +1 -1
- package/dist/http/internal/decompression.js +6 -2
- package/dist/http/internal/decompression.js.map +1 -1
- package/dist/http/internal/httpBody.js +8 -1
- package/dist/http/internal/httpBody.js.map +1 -1
- package/dist/http/internal/urlParams.js +33 -0
- package/dist/http/internal/urlParams.js.map +1 -1
- package/dist/http-api/HttpApi.d.ts +3 -6
- package/dist/http-api/HttpApi.d.ts.map +1 -1
- package/dist/http-api/HttpApi.js +3 -6
- package/dist/http-api/HttpApi.js.map +1 -1
- package/dist/http-api/HttpApiBuilder.d.ts.map +1 -1
- package/dist/http-api/HttpApiBuilder.js +43 -13
- package/dist/http-api/HttpApiBuilder.js.map +1 -1
- package/dist/http-api/HttpApiClient.d.ts +140 -7
- package/dist/http-api/HttpApiClient.d.ts.map +1 -1
- package/dist/http-api/HttpApiClient.js +193 -44
- package/dist/http-api/HttpApiClient.js.map +1 -1
- package/dist/http-api/HttpApiEndpoint.d.ts +24 -7
- package/dist/http-api/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/http-api/HttpApiEndpoint.js +123 -32
- package/dist/http-api/HttpApiEndpoint.js.map +1 -1
- package/dist/http-api/HttpApiError.d.ts +185 -13
- package/dist/http-api/HttpApiError.d.ts.map +1 -1
- package/dist/http-api/HttpApiError.js +128 -0
- package/dist/http-api/HttpApiError.js.map +1 -1
- package/dist/http-api/HttpApiMiddleware.d.ts +64 -17
- package/dist/http-api/HttpApiMiddleware.d.ts.map +1 -1
- package/dist/http-api/HttpApiMiddleware.js +85 -1
- package/dist/http-api/HttpApiMiddleware.js.map +1 -1
- package/dist/http-api/HttpApiSchema.d.ts +142 -33
- package/dist/http-api/HttpApiSchema.d.ts.map +1 -1
- package/dist/http-api/HttpApiSchema.js +123 -59
- package/dist/http-api/HttpApiSchema.js.map +1 -1
- package/dist/http-api/HttpApiTest.d.ts.map +1 -1
- package/dist/http-api/HttpApiTest.js +3 -2
- package/dist/http-api/HttpApiTest.js.map +1 -1
- package/dist/http-api/OpenApi.d.ts.map +1 -1
- package/dist/http-api/OpenApi.js +25 -16
- package/dist/http-api/OpenApi.js.map +1 -1
- package/dist/http-api/internal/toCodecHttpProperties.d.ts +2 -0
- package/dist/http-api/internal/toCodecHttpProperties.d.ts.map +1 -0
- package/dist/http-api/internal/toCodecHttpProperties.js +38 -0
- package/dist/http-api/internal/toCodecHttpProperties.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/arbitrary/runner.d.ts.map +1 -1
- package/dist/internal/arbitrary/runner.js +120 -44
- package/dist/internal/arbitrary/runner.js.map +1 -1
- package/dist/internal/core.js +2 -0
- package/dist/internal/core.js.map +1 -1
- package/dist/internal/effect.js +46 -27
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/equal.js +14 -0
- package/dist/internal/equal.js.map +1 -1
- package/dist/internal/glob.d.ts +2 -0
- package/dist/internal/glob.d.ts.map +1 -0
- package/dist/internal/glob.js +630 -0
- package/dist/internal/glob.js.map +1 -0
- package/dist/internal/jsonSchema.d.ts +2 -0
- package/dist/internal/jsonSchema.d.ts.map +1 -0
- package/dist/internal/jsonSchema.js +28 -0
- package/dist/internal/jsonSchema.js.map +1 -0
- package/dist/internal/matcher.js +11 -1
- package/dist/internal/matcher.js.map +1 -1
- package/dist/internal/persistence.js +9 -0
- package/dist/internal/persistence.js.map +1 -1
- package/dist/internal/rcRef.js +4 -1
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/schema/compilerRegistry.js +4 -1
- package/dist/internal/schema/compilerRegistry.js.map +1 -1
- package/dist/internal/schema/content.d.ts +2 -0
- package/dist/internal/schema/content.d.ts.map +1 -0
- package/dist/internal/schema/content.js +13 -0
- package/dist/internal/schema/content.js.map +1 -0
- package/dist/internal/schema/fromJsonSchemaDocument.js +173 -185
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/fromRepresentation.js +2 -1
- package/dist/internal/schema/fromRepresentation.js.map +1 -1
- package/dist/internal/schema/standardSchema.js +8 -2
- package/dist/internal/schema/standardSchema.js.map +1 -1
- package/dist/internal/schema/toCodeDocument.js +9 -6
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/internal/schema/toCodec.js +21 -24
- package/dist/internal/schema/toCodec.js.map +1 -1
- package/dist/internal/schema/toJsonSchemaDocument.js +205 -140
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/schema/toRepresentation.js +107 -129
- package/dist/internal/schema/toRepresentation.js.map +1 -1
- package/dist/internal/sql.d.ts +2 -0
- package/dist/internal/sql.d.ts.map +1 -0
- package/dist/internal/sql.js +13 -0
- package/dist/internal/sql.js.map +1 -0
- package/dist/internal/trie.js +42 -13
- package/dist/internal/trie.js.map +1 -1
- package/dist/internal/ulid.js +41 -0
- package/dist/internal/ulid.js.map +1 -1
- package/dist/internal/uuid.js +52 -8
- package/dist/internal/uuid.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/observability/Otlp.d.ts +15 -4
- package/dist/observability/Otlp.d.ts.map +1 -1
- package/dist/observability/Otlp.js +10 -0
- package/dist/observability/Otlp.js.map +1 -1
- package/dist/observability/OtlpProxy.d.ts +154 -0
- package/dist/observability/OtlpProxy.d.ts.map +1 -0
- package/dist/observability/OtlpProxy.js +235 -0
- package/dist/observability/OtlpProxy.js.map +1 -0
- package/dist/observability/OtlpResource.d.ts +7 -0
- package/dist/observability/OtlpResource.d.ts.map +1 -1
- package/dist/observability/OtlpResource.js +11 -1
- package/dist/observability/OtlpResource.js.map +1 -1
- package/dist/observability/OtlpTracer.d.ts.map +1 -1
- package/dist/observability/OtlpTracer.js +4 -0
- package/dist/observability/OtlpTracer.js.map +1 -1
- package/dist/observability/index.d.ts +5 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +5 -0
- package/dist/observability/index.js.map +1 -1
- package/dist/persistence/CircuitBreaker.d.ts +897 -0
- package/dist/persistence/CircuitBreaker.d.ts.map +1 -0
- package/dist/persistence/CircuitBreaker.js +1165 -0
- package/dist/persistence/CircuitBreaker.js.map +1 -0
- package/dist/persistence/KeyValueStore.d.ts +47 -2
- package/dist/persistence/KeyValueStore.d.ts.map +1 -1
- package/dist/persistence/KeyValueStore.js +129 -18
- package/dist/persistence/KeyValueStore.js.map +1 -1
- package/dist/persistence/PersistedQueue.d.ts +9 -0
- package/dist/persistence/PersistedQueue.d.ts.map +1 -1
- package/dist/persistence/PersistedQueue.js +94 -52
- package/dist/persistence/PersistedQueue.js.map +1 -1
- package/dist/persistence/Persistence.d.ts +10 -4
- package/dist/persistence/Persistence.d.ts.map +1 -1
- package/dist/persistence/Persistence.js +18 -11
- package/dist/persistence/Persistence.js.map +1 -1
- package/dist/persistence/Redis.d.ts +78 -2
- package/dist/persistence/Redis.d.ts.map +1 -1
- package/dist/persistence/Redis.js +62 -7
- package/dist/persistence/Redis.js.map +1 -1
- package/dist/persistence/index.d.ts +5 -0
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +5 -0
- package/dist/persistence/index.js.map +1 -1
- package/dist/process/ChildProcess.d.ts +237 -71
- package/dist/process/ChildProcess.d.ts.map +1 -1
- package/dist/process/ChildProcess.js +56 -158
- package/dist/process/ChildProcess.js.map +1 -1
- package/dist/process/ChildProcessSpawner.d.ts +35 -0
- package/dist/process/ChildProcessSpawner.d.ts.map +1 -1
- package/dist/process/ChildProcessSpawner.js +5 -2
- package/dist/process/ChildProcessSpawner.js.map +1 -1
- package/dist/process/CommandLine.d.ts +294 -0
- package/dist/process/CommandLine.d.ts.map +1 -0
- package/dist/process/CommandLine.js +481 -0
- package/dist/process/CommandLine.js.map +1 -0
- package/dist/process/index.d.ts +5 -0
- package/dist/process/index.d.ts.map +1 -1
- package/dist/process/index.js +5 -0
- package/dist/process/index.js.map +1 -1
- package/dist/reactivity/AsyncResult.d.ts +1 -1
- package/dist/reactivity/AsyncResult.d.ts.map +1 -1
- package/dist/reactivity/Atom.d.ts +2 -1
- package/dist/reactivity/Atom.d.ts.map +1 -1
- package/dist/reactivity/Atom.js +3 -4
- package/dist/reactivity/Atom.js.map +1 -1
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +16 -1
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +59 -24
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcMessage.d.ts +10 -1
- package/dist/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/rpc/RpcMessage.js +1 -0
- package/dist/rpc/RpcMessage.js.map +1 -1
- package/dist/rpc/RpcSerialization.d.ts +14 -0
- package/dist/rpc/RpcSerialization.d.ts.map +1 -1
- package/dist/rpc/RpcSerialization.js +26 -8
- package/dist/rpc/RpcSerialization.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts +31 -0
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +139 -56
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/rpc/RpcTest.d.ts +1 -0
- package/dist/rpc/RpcTest.d.ts.map +1 -1
- package/dist/rpc/RpcTest.js +1 -0
- package/dist/rpc/RpcTest.js.map +1 -1
- package/dist/schema/Model.d.ts +14 -0
- package/dist/schema/Model.d.ts.map +1 -1
- package/dist/schema/Model.js +18 -3
- package/dist/schema/Model.js.map +1 -1
- package/dist/sql/Migrator.d.ts +2 -20
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +13 -55
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/MigratorFileSystem.d.ts +21 -0
- package/dist/sql/MigratorFileSystem.d.ts.map +1 -0
- package/dist/sql/MigratorFileSystem.js +57 -0
- package/dist/sql/MigratorFileSystem.js.map +1 -0
- package/dist/sql/SqlClient.d.ts +9 -4
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +13 -10
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/SqlResolver.d.ts +4 -0
- package/dist/sql/SqlResolver.d.ts.map +1 -1
- package/dist/sql/SqlResolver.js +18 -4
- package/dist/sql/SqlResolver.js.map +1 -1
- package/dist/sql/Statement.d.ts +27 -6
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +21 -15
- package/dist/sql/Statement.js.map +1 -1
- package/dist/sql/index.js +1 -1
- package/dist/testing/index.d.ts +0 -5
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +0 -5
- package/dist/testing/index.js.map +1 -1
- package/dist/workflow/DurableDeferred.d.ts.map +1 -1
- package/dist/workflow/DurableDeferred.js +21 -2
- package/dist/workflow/DurableDeferred.js.map +1 -1
- package/dist/workflow/internal/crypto.js +9 -8
- package/dist/workflow/internal/crypto.js.map +1 -1
- package/package.json +3 -1
- package/src/Arbitrary.ts +10 -2
- package/src/BigDecimal.ts +3 -0
- package/src/Channel.ts +64 -29
- package/src/Config.ts +7 -1
- package/src/Context.ts +34 -10
- package/src/Cron.ts +178 -29
- package/src/Data.ts +60 -265
- package/src/Duration.ts +140 -0
- package/src/Effect.ts +915 -86
- package/src/Equal.ts +8 -1
- package/src/Exit.ts +3 -3
- package/src/FileSystem.ts +65 -4
- package/src/Formatter.ts +28 -13
- package/src/Glob.ts +205 -0
- package/src/Hash.ts +4 -1
- package/src/JsonPatch.ts +4 -0
- package/src/JsonSchema.ts +12 -40
- package/src/Match.ts +127 -0
- package/src/Metric.ts +3 -3
- package/src/MutableList.ts +7 -5
- package/src/MutableRef.ts +8 -1
- package/src/PartitionedSemaphore.ts +24 -13
- package/src/Pool.ts +182 -52
- package/src/PubSub.ts +5 -6
- package/src/Random.ts +3 -3
- package/src/RcMap.ts +5 -1
- package/src/Ref.ts +8 -0
- package/src/Schedule.ts +27 -4
- package/src/Schema.ts +1237 -87
- package/src/SchemaAST.ts +20 -20
- package/src/SchemaGetter.ts +27 -11
- package/src/SchemaParser.ts +51 -18
- package/src/SchemaRepresentation.ts +50 -34
- package/src/SchemaTransformation.ts +17 -5
- package/src/ScopedCache.ts +5 -9
- package/src/Semaphore.ts +208 -40
- package/src/Sink.ts +15 -2
- package/src/Stream.ts +48 -22
- package/src/String.ts +3 -0
- package/src/Terminal.ts +19 -1
- package/src/Tracer.ts +15 -1
- package/src/ai/AiError.ts +56 -80
- package/src/ai/Decision.ts +257 -6
- package/src/ai/DecisionModel.ts +171 -28
- package/src/ai/LanguageModel.ts +9 -0
- package/src/ai/McpServer.ts +1283 -299
- package/src/ai/McpTest.ts +380 -0
- package/src/ai/Prompt.ts +65 -0
- package/src/ai/Response.ts +36 -31
- package/src/ai/Tool.ts +65 -10
- package/src/ai/Toolkit.ts +4 -1
- package/src/ai/index.ts +6 -0
- package/src/ai/internal/mcpCore.ts +78 -44
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +15 -10
- package/src/ai/internal/mcpProtocol.ts +29 -0
- package/src/ai/internal/mcpRuntime.ts +9 -5
- package/src/ai/internal/mcpStatefulRuntime.ts +14 -2
- package/src/cli/Argument.ts +21 -0
- package/src/cli/CliError.ts +90 -3
- package/src/cli/CliOutput.ts +300 -55
- package/src/cli/Command.ts +76 -41
- package/src/cli/Completions.ts +51 -0
- package/src/cli/Flag.ts +122 -0
- package/src/cli/GlobalFlag.ts +6 -7
- package/src/cli/HelpDoc.ts +18 -1
- package/src/cli/Param.ts +35 -6
- package/src/cli/Primitive.ts +16 -1
- package/src/cli/Prompt.ts +1 -0
- package/src/cli/TerminalCapabilities.ts +6 -3
- package/src/cli/internal/completions/bash.ts +79 -29
- package/src/cli/internal/help.ts +21 -9
- package/src/cluster/ClusterWorkflowEngine.ts +46 -16
- package/src/cluster/MessageStorage.ts +19 -14
- package/src/cluster/Sharding.ts +176 -47
- package/src/cluster/ShardingConfig.ts +43 -0
- package/src/cluster/SingleRunner.ts +3 -3
- package/src/cluster/Snowflake.ts +3 -7
- package/src/cluster/SqlMessageStorage.ts +49 -17
- package/src/cluster/SqlRunnerStorage.ts +143 -70
- package/src/cluster/internal/entityManager.ts +75 -22
- package/src/crypto/Aead.ts +517 -0
- package/src/crypto/Cipher.ts +326 -0
- package/src/crypto/CryptoError.ts +242 -0
- package/src/crypto/Digest.ts +167 -0
- package/src/crypto/Kdf.ts +315 -0
- package/src/crypto/KeyAgreement.ts +489 -0
- package/src/crypto/KeyFormat.ts +712 -0
- package/src/crypto/KeyWrap.ts +639 -0
- package/src/crypto/Mac.ts +386 -0
- package/src/crypto/SecureRandom.ts +459 -0
- package/src/crypto/Signature.ts +673 -0
- package/src/crypto/WebCrypto.ts +64 -0
- package/src/crypto/index.ts +78 -0
- package/src/crypto/internal/asymmetric.ts +329 -0
- package/src/crypto/internal/chacha.ts +1088 -0
- package/src/crypto/internal/der.ts +110 -0
- package/src/crypto/internal/digest.ts +16 -0
- package/src/crypto/internal/key.ts +235 -0
- package/src/crypto/internal/md5.ts +170 -0
- package/src/crypto/internal/webCrypto.ts +150 -0
- package/src/devtools/DevToolsClient.ts +2 -0
- package/src/encoding/Base64.ts +38 -0
- package/src/encoding/Base64Url.ts +5 -2
- package/src/encoding/Csv.ts +1298 -0
- package/src/encoding/Hex.ts +3 -2
- package/src/encoding/SchemaBinary.ts +40 -4
- package/src/encoding/Sse.ts +4 -3
- package/src/encoding/Toml.ts +117 -35
- package/src/encoding/Yaml.ts +61 -18
- package/src/encoding/index.ts +6 -0
- package/src/eventlog/EventJournal.ts +4 -3
- package/src/eventlog/EventLog.ts +340 -15
- package/src/eventlog/EventLogEncryption.ts +118 -110
- package/src/eventlog/EventLogMessage.ts +273 -1
- package/src/eventlog/EventLogRemote.ts +367 -23
- package/src/eventlog/EventLogServer.ts +23 -8
- package/src/eventlog/EventLogServerEncrypted.ts +1130 -67
- package/src/eventlog/EventLogSessionAuth.ts +56 -70
- package/src/eventlog/EventRow.ts +12 -3
- package/src/eventlog/SqlEventJournal.ts +68 -45
- package/src/eventlog/SqlEventLogServerEncrypted.ts +1244 -136
- package/src/eventlog/SqlEventLogServerUnencrypted.ts +2 -2
- package/src/eventlog/internal/batches.ts +30 -0
- package/src/eventlog/internal/compaction.ts +64 -0
- package/src/eventlog/internal/conflicts.ts +0 -4
- package/src/eventlog/internal/identityRootSecretDerivation.ts +93 -123
- package/src/eventlog/internal/remoteCompaction.ts +92 -0
- package/src/http/Cookies.ts +291 -12
- package/src/http/FetchHttpClient.ts +88 -16
- package/src/http/HttpClient.ts +636 -29
- package/src/http/HttpClientError.ts +35 -1
- package/src/http/HttpClientRequest.ts +21 -10
- package/src/http/HttpClientResponse.ts +3 -2
- package/src/http/HttpMethod.ts +10 -2
- package/src/http/HttpMiddleware.ts +15 -18
- package/src/http/HttpRouter.ts +2 -1
- package/src/http/HttpServerError.ts +2 -2
- package/src/http/HttpServerRequest.ts +101 -30
- package/src/http/HttpServerResponse.ts +3 -2
- package/src/http/HttpStaticServer.ts +51 -22
- package/src/http/HttpTraceContext.ts +7 -4
- package/src/http/MultipartParser/internal/contentType.ts +3 -3
- package/src/http/ProblemDetails.ts +149 -0
- package/src/http/UrlParams.ts +6 -1
- package/src/http/index.ts +6 -0
- package/src/http/internal/compression.ts +4 -5
- package/src/http/internal/decompression.ts +10 -4
- package/src/http/internal/httpBody.ts +10 -2
- package/src/http/internal/urlParams.ts +37 -0
- package/src/http-api/HttpApi.ts +3 -6
- package/src/http-api/HttpApiBuilder.ts +61 -19
- package/src/http-api/HttpApiClient.ts +284 -72
- package/src/http-api/HttpApiEndpoint.ts +135 -35
- package/src/http-api/HttpApiError.ts +250 -0
- package/src/http-api/HttpApiMiddleware.ts +95 -1
- package/src/http-api/HttpApiSchema.ts +236 -52
- package/src/http-api/HttpApiTest.ts +3 -2
- package/src/http-api/OpenApi.ts +20 -17
- package/src/http-api/internal/toCodecHttpProperties.ts +56 -0
- package/src/index.ts +6 -6
- package/src/internal/arbitrary/runner.ts +133 -36
- package/src/internal/core.ts +2 -0
- package/src/internal/effect.ts +58 -34
- package/src/internal/equal.ts +17 -0
- package/src/internal/glob.ts +736 -0
- package/src/internal/jsonSchema.ts +27 -0
- package/src/internal/matcher.ts +18 -1
- package/src/internal/persistence.ts +11 -0
- package/src/internal/rcRef.ts +4 -1
- package/src/internal/schema/compilerRegistry.ts +6 -1
- package/src/internal/schema/content.ts +18 -0
- package/src/internal/schema/fromJsonSchemaDocument.ts +228 -190
- package/src/internal/schema/fromRepresentation.ts +2 -1
- package/src/internal/schema/standardSchema.ts +2 -2
- package/src/internal/schema/toCodeDocument.ts +11 -7
- package/src/internal/schema/toCodec.ts +26 -22
- package/src/internal/schema/toJsonSchemaDocument.ts +189 -167
- package/src/internal/schema/toRepresentation.ts +122 -140
- package/src/internal/sql.ts +14 -0
- package/src/internal/trie.ts +43 -14
- package/src/internal/ulid.ts +44 -0
- package/src/internal/uuid.ts +57 -16
- package/src/internal/version.ts +1 -1
- package/src/observability/Otlp.ts +21 -4
- package/src/observability/OtlpProxy.ts +314 -0
- package/src/observability/OtlpResource.ts +9 -1
- package/src/observability/OtlpTracer.ts +4 -0
- package/src/observability/index.ts +6 -0
- package/src/persistence/CircuitBreaker.ts +1678 -0
- package/src/persistence/KeyValueStore.ts +219 -22
- package/src/persistence/PersistedQueue.ts +128 -59
- package/src/persistence/Persistence.ts +20 -13
- package/src/persistence/Redis.ts +129 -8
- package/src/persistence/index.ts +6 -0
- package/src/process/ChildProcess.ts +285 -264
- package/src/process/ChildProcessSpawner.ts +46 -2
- package/src/process/CommandLine.ts +630 -0
- package/src/process/index.ts +6 -0
- package/src/reactivity/AsyncResult.ts +1 -1
- package/src/reactivity/Atom.ts +5 -5
- package/src/reactivity/AtomRegistry.ts +1 -1
- package/src/rpc/RpcClient.ts +75 -36
- package/src/rpc/RpcMessage.ts +10 -1
- package/src/rpc/RpcSerialization.ts +35 -9
- package/src/rpc/RpcServer.ts +176 -63
- package/src/rpc/RpcTest.ts +3 -0
- package/src/schema/Model.ts +26 -3
- package/src/sql/Migrator.ts +15 -69
- package/src/sql/MigratorFileSystem.ts +71 -0
- package/src/sql/SqlClient.ts +13 -20
- package/src/sql/SqlResolver.ts +23 -9
- package/src/sql/Statement.ts +38 -9
- package/src/sql/index.ts +1 -1
- package/src/testing/index.ts +0 -6
- package/src/workflow/DurableDeferred.ts +23 -6
- package/src/workflow/internal/crypto.ts +15 -11
- package/dist/Crypto.d.ts +0 -244
- package/dist/Crypto.d.ts.map +0 -1
- package/dist/Crypto.js +0 -139
- package/dist/Crypto.js.map +0 -1
- package/dist/testing/TestCrypto.d.ts +0 -73
- package/dist/testing/TestCrypto.d.ts.map +0 -1
- package/dist/testing/TestCrypto.js +0 -87
- package/dist/testing/TestCrypto.js.map +0 -1
- package/src/Crypto.ts +0 -347
- package/src/testing/TestCrypto.ts +0 -94
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyed circuit breakers for calls to unreliable dependencies.
|
|
3
|
+
*
|
|
4
|
+
* A `CircuitBreaker` holds one circuit per key, so one policy can guard many
|
|
5
|
+
* hosts or tenants. State lives in a `CircuitBreakerStore`: `layerStoreMemory`
|
|
6
|
+
* keeps it in the process, `layerStoreRedis` shares it between processes.
|
|
7
|
+
*
|
|
8
|
+
* **Details**
|
|
9
|
+
*
|
|
10
|
+
* - Closed: calls run, and the trip policy decides when the circuit opens.
|
|
11
|
+
* - Open: calls fail with `CircuitOpen` until the open time ends.
|
|
12
|
+
* - HalfOpen: the next call is the probe, and other calls fail with
|
|
13
|
+
* `CircuitHalfOpen` while it runs. A successful probe closes the circuit and
|
|
14
|
+
* a failed one reopens it. An interrupted or `Ignored` probe frees the slot.
|
|
15
|
+
* - The backoff count survives a close. It resets once the circuit has stayed
|
|
16
|
+
* closed as long as it was last open.
|
|
17
|
+
*
|
|
18
|
+
* The breaker never waits. Retry rejections outside it, as in "Retrying
|
|
19
|
+
* rejections" below. Put `Effect.retry` and `Effect.timeout` inside it, so it
|
|
20
|
+
* sees one outcome per call. With `ExecutionPlan`, put a breaker inside each
|
|
21
|
+
* step under its own key.
|
|
22
|
+
*
|
|
23
|
+
* The breaker emits no metrics or spans. Observe it through `state(key)`,
|
|
24
|
+
* `transitions` and the typed error.
|
|
25
|
+
*
|
|
26
|
+
* **Gotchas**
|
|
27
|
+
*
|
|
28
|
+
* - Use one policy per key. Breakers that share a key and a store share one
|
|
29
|
+
* circuit, and nothing checks that their policies match. Separate them with
|
|
30
|
+
* distinct keys or a Redis `prefix`.
|
|
31
|
+
* - Calls admitted before a trip still run to completion, but their outcomes
|
|
32
|
+
* change nothing.
|
|
33
|
+
* - "Consecutive" counts in order of completion, not of start.
|
|
34
|
+
* - `state(key)` reads the shared store. `transitions` only shows what this
|
|
35
|
+
* breaker instance did.
|
|
36
|
+
* - Store errors the breaker swallows (a fail-open admit, a failed record, a
|
|
37
|
+
* failed Redis lease renewal) are reported with their cause at
|
|
38
|
+
* `References.UnhandledLogLevel`. Set it to `undefined` to silence them.
|
|
39
|
+
* - Cap an exponential `openDuration`, with `Schedule.upTo` or with
|
|
40
|
+
* `Schedule.min` and a `Schedule.spaced`, or it keeps growing for a
|
|
41
|
+
* dependency that flaps.
|
|
42
|
+
*
|
|
43
|
+
* **Example** (Rejecting calls once a circuit opens)
|
|
44
|
+
*
|
|
45
|
+
* ```ts import.meta.vitest
|
|
46
|
+
* import { Data, Effect } from "effect"
|
|
47
|
+
* import { CircuitBreaker } from "effect/persistence"
|
|
48
|
+
*
|
|
49
|
+
* class PaymentsError extends Data.TaggedError("PaymentsError") {}
|
|
50
|
+
*
|
|
51
|
+
* const program = Effect.gen(function*() {
|
|
52
|
+
* const breaker = yield* CircuitBreaker.make({
|
|
53
|
+
* tripPolicy: CircuitBreaker.consecutive(2),
|
|
54
|
+
* openDuration: "30 seconds"
|
|
55
|
+
* })
|
|
56
|
+
* const charge = Effect.fail(new PaymentsError()).pipe(
|
|
57
|
+
* breaker.execute({ key: "payments" })
|
|
58
|
+
* )
|
|
59
|
+
*
|
|
60
|
+
* // Two failures in a row open the circuit.
|
|
61
|
+
* yield* Effect.flip(charge)
|
|
62
|
+
* yield* Effect.flip(charge)
|
|
63
|
+
*
|
|
64
|
+
* // The third call is rejected without running.
|
|
65
|
+
* return yield* charge.pipe(
|
|
66
|
+
* Effect.catchReason("CircuitBreakerError", "CircuitOpen", (reason) => Effect.succeed(`${reason.key} is open`))
|
|
67
|
+
* )
|
|
68
|
+
* }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
|
|
69
|
+
*
|
|
70
|
+
* await Effect.runPromise(program) // => "payments is open"
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* **Example** (Retrying rejections)
|
|
74
|
+
*
|
|
75
|
+
* For `CircuitOpen`, wait at least the rejection's `retryAfter`. For
|
|
76
|
+
* `CircuitHalfOpen`, the jittered backoff keeps waiting callers from retrying
|
|
77
|
+
* at the same moment. Store errors and the call's own errors are not retried.
|
|
78
|
+
*
|
|
79
|
+
* ```ts import.meta.vitest
|
|
80
|
+
* import { Data, Duration, Effect, Ref, Schedule } from "effect"
|
|
81
|
+
* import { CircuitBreaker } from "effect/persistence"
|
|
82
|
+
*
|
|
83
|
+
* const isRejection = (error: unknown): error is CircuitBreaker.CircuitBreakerError =>
|
|
84
|
+
* error instanceof CircuitBreaker.CircuitBreakerError && error.reason._tag !== "CircuitBreakerStoreError"
|
|
85
|
+
*
|
|
86
|
+
* const retryRejections = Schedule.exponential("20 millis").pipe(
|
|
87
|
+
* Schedule.jittered,
|
|
88
|
+
* Schedule.modifyDelay(({ duration, input }) =>
|
|
89
|
+
* Effect.succeed(
|
|
90
|
+
* isRejection(input) && input.reason._tag === "CircuitOpen"
|
|
91
|
+
* ? Duration.max(duration, input.reason.retryAfter)
|
|
92
|
+
* : duration
|
|
93
|
+
* )
|
|
94
|
+
* )
|
|
95
|
+
* )
|
|
96
|
+
*
|
|
97
|
+
* class QuoteError extends Data.TaggedError("QuoteError") {}
|
|
98
|
+
*
|
|
99
|
+
* const program = Effect.gen(function*() {
|
|
100
|
+
* const breaker = yield* CircuitBreaker.make({
|
|
101
|
+
* tripPolicy: CircuitBreaker.consecutive(1),
|
|
102
|
+
* openDuration: "50 millis"
|
|
103
|
+
* })
|
|
104
|
+
* const healthy = yield* Ref.make(false)
|
|
105
|
+
* const quote = Effect.flatMap(Ref.get(healthy), (ok) => ok ? Effect.succeed(42) : Effect.fail(new QuoteError()))
|
|
106
|
+
*
|
|
107
|
+
* // One failure opens the circuit, then the dependency recovers.
|
|
108
|
+
* yield* Effect.flip(quote.pipe(breaker.execute({ key: "quotes" })))
|
|
109
|
+
* yield* Ref.set(healthy, true)
|
|
110
|
+
*
|
|
111
|
+
* // Rejected while open, then retried once the circuit lets a probe through.
|
|
112
|
+
* return yield* quote.pipe(
|
|
113
|
+
* breaker.execute({ key: "quotes" }),
|
|
114
|
+
* Effect.retry({ schedule: retryRejections, while: isRejection, times: 5 })
|
|
115
|
+
* )
|
|
116
|
+
* }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
|
|
117
|
+
*
|
|
118
|
+
* await Effect.runPromise(program) // => 42
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @stability experimental
|
|
122
|
+
* @since 4.0.6
|
|
123
|
+
*/
|
|
124
|
+
import * as Cause from "../Cause.js";
|
|
125
|
+
import * as Clock from "../Clock.js";
|
|
126
|
+
import * as Config from "../Config.js";
|
|
127
|
+
import * as Context from "../Context.js";
|
|
128
|
+
import * as Duration from "../Duration.js";
|
|
129
|
+
import * as Effect from "../Effect.js";
|
|
130
|
+
import * as Exit from "../Exit.js";
|
|
131
|
+
import { dual } from "../Function.js";
|
|
132
|
+
import { findRecoverableError } from "../internal/effect.js";
|
|
133
|
+
import * as Layer from "../Layer.js";
|
|
134
|
+
import * as PubSub from "../PubSub.js";
|
|
135
|
+
import { UnhandledLogLevel } from "../References.js";
|
|
136
|
+
import * as Schedule from "../Schedule.js";
|
|
137
|
+
import * as Schema from "../Schema.js";
|
|
138
|
+
import * as Scope from "../Scope.js";
|
|
139
|
+
import * as Stream from "../Stream.js";
|
|
140
|
+
import * as Redis from "./Redis.js";
|
|
141
|
+
/**
|
|
142
|
+
* Runtime type identifier for `CircuitBreaker` values.
|
|
143
|
+
*
|
|
144
|
+
* @stability experimental
|
|
145
|
+
* @category type IDs
|
|
146
|
+
* @since 4.0.6
|
|
147
|
+
*/
|
|
148
|
+
export const TypeId = "~effect/persistence/CircuitBreaker";
|
|
149
|
+
/**
|
|
150
|
+
* Service tag for the default `CircuitBreaker`.
|
|
151
|
+
*
|
|
152
|
+
* **When to use**
|
|
153
|
+
*
|
|
154
|
+
* Use when one breaker policy serves the whole context. For several policies,
|
|
155
|
+
* use a `LayerMap` keyed by policy, with every entry sharing one store, or
|
|
156
|
+
* define your own tags.
|
|
157
|
+
*
|
|
158
|
+
* **Example** (Several breaker policies with LayerMap)
|
|
159
|
+
*
|
|
160
|
+
* ```ts import.meta.vitest
|
|
161
|
+
* import { Effect, LayerMap, Schedule } from "effect"
|
|
162
|
+
* import { CircuitBreaker } from "effect/persistence"
|
|
163
|
+
*
|
|
164
|
+
* class Breakers extends LayerMap.Service<Breakers>()("app/Breakers", {
|
|
165
|
+
* lookup: (dependency: "payments" | "search") =>
|
|
166
|
+
* dependency === "payments"
|
|
167
|
+
* ? CircuitBreaker.layer({ tripPolicy: CircuitBreaker.consecutive(5), openDuration: "30 seconds" })
|
|
168
|
+
* : CircuitBreaker.layer({
|
|
169
|
+
* tripPolicy: CircuitBreaker.rate({ threshold: 0.5, size: 20 }),
|
|
170
|
+
* openDuration: Schedule.exponential("1 second")
|
|
171
|
+
* }),
|
|
172
|
+
* // One store shared by every entry.
|
|
173
|
+
* dependencies: [CircuitBreaker.layerStoreMemory()],
|
|
174
|
+
* idleTimeToLive: "10 minutes"
|
|
175
|
+
* }) {}
|
|
176
|
+
*
|
|
177
|
+
* const search = Effect.gen(function*() {
|
|
178
|
+
* const breaker = yield* CircuitBreaker.CircuitBreaker
|
|
179
|
+
* return yield* Effect.succeed(["result"]).pipe(breaker.execute({ key: "search:eu" }))
|
|
180
|
+
* }).pipe(Effect.provide(Breakers.get("search")))
|
|
181
|
+
*
|
|
182
|
+
* await Effect.runPromise(search.pipe(Effect.provide(Breakers.layer))) // => ["result"]
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @stability experimental
|
|
186
|
+
* @category services
|
|
187
|
+
* @since 4.0.6
|
|
188
|
+
*/
|
|
189
|
+
export const CircuitBreaker = /*#__PURE__*/Context.Service(TypeId);
|
|
190
|
+
/**
|
|
191
|
+
* Creates a `CircuitBreaker` from the current `CircuitBreakerStore`.
|
|
192
|
+
*
|
|
193
|
+
* **Details**
|
|
194
|
+
*
|
|
195
|
+
* - `tripPolicy` decides when a Closed circuit opens.
|
|
196
|
+
* - `openDuration` is a duration or a `Schedule`. The n-th consecutive opening
|
|
197
|
+
* lasts the schedule's n-th delay, and the last delay repeats once the
|
|
198
|
+
* schedule ends. Elapsed-time combinators such as `upTo` and `during`
|
|
199
|
+
* measure the time spent open, not wall time. Schedules that read the
|
|
200
|
+
* current date, such as `cron`, are not supported.
|
|
201
|
+
* - `maxOpenDuration` (default one hour) caps a `Retry-After` hint. It never
|
|
202
|
+
* caps `openDuration`.
|
|
203
|
+
* - `onStoreError` decides what a call does when the store cannot admit it:
|
|
204
|
+
* `"allow"` (the default) runs it unrecorded, `"fail"` fails it with
|
|
205
|
+
* `CircuitBreakerStoreError`.
|
|
206
|
+
*
|
|
207
|
+
* The backoff count resets once the circuit has stayed closed as long as its
|
|
208
|
+
* last opening, `Retry-After` hint included.
|
|
209
|
+
*
|
|
210
|
+
* **Example** (Capping backoff growth by time spent open)
|
|
211
|
+
*
|
|
212
|
+
* ```ts import.meta.vitest
|
|
213
|
+
* import { Effect, Schedule } from "effect"
|
|
214
|
+
* import { CircuitBreaker } from "effect/persistence"
|
|
215
|
+
*
|
|
216
|
+
* // Openings last 1, 2, 4, 8, 16 and 32 seconds. By then 63 seconds have been
|
|
217
|
+
* // spent open, past the one-minute cap, so later openings stay at 32 seconds.
|
|
218
|
+
* const program = Effect.gen(function*() {
|
|
219
|
+
* const breaker = yield* CircuitBreaker.make({
|
|
220
|
+
* tripPolicy: CircuitBreaker.consecutive(5),
|
|
221
|
+
* openDuration: Schedule.exponential("1 second").pipe(
|
|
222
|
+
* Schedule.upTo({ duration: "1 minute" })
|
|
223
|
+
* )
|
|
224
|
+
* })
|
|
225
|
+
* return yield* Effect.succeed("ok").pipe(breaker.execute({ key: "payments" }))
|
|
226
|
+
* }).pipe(Effect.scoped, Effect.provide(CircuitBreaker.layerStoreMemory()))
|
|
227
|
+
*
|
|
228
|
+
* await Effect.runPromise(program) // => "ok"
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* @stability experimental
|
|
232
|
+
* @category constructors
|
|
233
|
+
* @since 4.0.6
|
|
234
|
+
*/
|
|
235
|
+
export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
236
|
+
const store = yield* CircuitBreakerStore;
|
|
237
|
+
const scope = yield* Effect.scope;
|
|
238
|
+
const pubsub = yield* PubSub.sliding(128);
|
|
239
|
+
yield* Scope.addFinalizer(scope, PubSub.end(pubsub, Exit.void));
|
|
240
|
+
const maxOpenDuration = Duration.fromInputUnsafe(options.maxOpenDuration ?? "1 hour");
|
|
241
|
+
const onStoreError = options.onStoreError ?? "allow";
|
|
242
|
+
const openDurationFor = makeOpenDuration(options.openDuration);
|
|
243
|
+
const publish = transition => transition === undefined ? Effect.void : PubSub.publish(pubsub, [transition]);
|
|
244
|
+
// Only an outcome that can open the circuit needs the schedule replayed.
|
|
245
|
+
// An Ignored outcome never opens it, and neither does a Success unless it
|
|
246
|
+
// can trip a rate policy: a probe's Success closes the circuit. Only a
|
|
247
|
+
// Failure carries a hint.
|
|
248
|
+
const openFor = (outcome, admission) => outcome._tag === "Ignored" || outcome._tag === "Success" && (admission.probe || options.tripPolicy._tag === "Consecutive") ? Effect.succeed(Duration.zero) : Effect.map(openDurationFor(admission.consecutiveOpenings + 1), delay => outcome._tag === "Failure" && outcome.retryAfter !== undefined ? Duration.max(delay, Duration.min(outcome.retryAfter, maxOpenDuration)) : delay);
|
|
249
|
+
const execute = (self, {
|
|
250
|
+
classify,
|
|
251
|
+
key
|
|
252
|
+
}) => Effect.uninterruptibleMask(restore => Effect.scopedWith(Effect.fnUntraced(function* (admitScope) {
|
|
253
|
+
const admitted = yield* Effect.exit(Scope.provide(store.admit(key), admitScope));
|
|
254
|
+
if (Exit.isFailure(admitted)) {
|
|
255
|
+
const error = Cause.findErrorOption(admitted.cause);
|
|
256
|
+
if (onStoreError === "fail" || error._tag === "None" || error.value.reason._tag !== "CircuitBreakerStoreError") {
|
|
257
|
+
return yield* Effect.failCause(admitted.cause);
|
|
258
|
+
}
|
|
259
|
+
yield* reportUnhandled("CircuitBreaker store unavailable, running the call unrecorded", admitted.cause);
|
|
260
|
+
return yield* restore(self);
|
|
261
|
+
}
|
|
262
|
+
const admission = admitted.value;
|
|
263
|
+
yield* publish(admission.transition);
|
|
264
|
+
const exit = yield* Effect.exit(restore(self));
|
|
265
|
+
const classified = yield* classifyExit(exit, classify, restore);
|
|
266
|
+
const outcome = Exit.isSuccess(classified) ? classified.value : ignored;
|
|
267
|
+
yield* store.record({
|
|
268
|
+
key,
|
|
269
|
+
generation: admission.generation,
|
|
270
|
+
outcome: outcome._tag,
|
|
271
|
+
tripPolicy: options.tripPolicy,
|
|
272
|
+
openFor: yield* openFor(outcome, admission)
|
|
273
|
+
}).pipe(Effect.flatMap(publish), Effect.catchCauseFilter(findRecoverableError, (_, cause) => reportUnhandled("CircuitBreaker failed to record an outcome", cause)));
|
|
274
|
+
if (Exit.isSuccess(classified)) return yield* exit;
|
|
275
|
+
return yield* Effect.failCause(Exit.isFailure(exit) ? Cause.combine(exit.cause, classified.cause) : classified.cause);
|
|
276
|
+
})));
|
|
277
|
+
return CircuitBreaker.of({
|
|
278
|
+
[TypeId]: TypeId,
|
|
279
|
+
execute: dual(2, execute),
|
|
280
|
+
state: store.state,
|
|
281
|
+
transitions: Stream.fromPubSubTake(pubsub)
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
/**
|
|
285
|
+
* Provides `CircuitBreaker` using the current `CircuitBreakerStore`.
|
|
286
|
+
*
|
|
287
|
+
* **Details**
|
|
288
|
+
*
|
|
289
|
+
* Takes the same options as `make`.
|
|
290
|
+
*
|
|
291
|
+
* @stability experimental
|
|
292
|
+
* @category layers
|
|
293
|
+
* @since 4.0.6
|
|
294
|
+
*/
|
|
295
|
+
export const layer = options => Layer.effect(CircuitBreaker, make(options));
|
|
296
|
+
// Reports an error that has no caller to receive it at `UnhandledLogLevel`, as
|
|
297
|
+
// `Pool` and `ScopedCache` do. An undefined level silences it. Callers recover
|
|
298
|
+
// with `findRecoverableError`, as `Effect.catch` does, so a cause that holds a
|
|
299
|
+
// defect still propagates.
|
|
300
|
+
const reportUnhandled = (message, cause) => Effect.withFiber(fiber => {
|
|
301
|
+
const level = fiber.getRef(UnhandledLogLevel);
|
|
302
|
+
return level === undefined ? Effect.void : Effect.logWithLevel(level)(message, cause);
|
|
303
|
+
});
|
|
304
|
+
const classifyExit = (exit, classify, restore) => {
|
|
305
|
+
if (Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)) {
|
|
306
|
+
return Effect.succeed(Exit.succeed(ignored));
|
|
307
|
+
}
|
|
308
|
+
if (classify === undefined) {
|
|
309
|
+
return Effect.succeed(Exit.succeed(Exit.isSuccess(exit) ? success : failure()));
|
|
310
|
+
}
|
|
311
|
+
return Effect.exit(restore(Effect.suspend(() => {
|
|
312
|
+
const outcome = classify(exit);
|
|
313
|
+
return Effect.isEffect(outcome) ? outcome : Effect.succeed(outcome);
|
|
314
|
+
})));
|
|
315
|
+
};
|
|
316
|
+
const makeOpenDuration = openDuration => {
|
|
317
|
+
if (!Schedule.isSchedule(openDuration)) {
|
|
318
|
+
const duration = Effect.succeed(Duration.fromInputUnsafe(openDuration));
|
|
319
|
+
return () => duration;
|
|
320
|
+
}
|
|
321
|
+
// Replays the schedule against a simulated clock, so `upTo` and `during`
|
|
322
|
+
// measure the time spent open rather than wall time.
|
|
323
|
+
return consecutiveOpenings => Effect.gen(function* () {
|
|
324
|
+
const step = yield* Schedule.toStep(openDuration);
|
|
325
|
+
let now = yield* Clock.currentTimeMillis;
|
|
326
|
+
let delay = Duration.zero;
|
|
327
|
+
for (let attempt = 1; attempt <= consecutiveOpenings; attempt++) {
|
|
328
|
+
const result = yield* Effect.exit(step(now, attempt));
|
|
329
|
+
if (Exit.isFailure(result)) break;
|
|
330
|
+
delay = result.value[1];
|
|
331
|
+
now += Duration.toMillis(delay);
|
|
332
|
+
}
|
|
333
|
+
return delay;
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Creates a trip policy that opens after `failures` consecutive failures.
|
|
338
|
+
*
|
|
339
|
+
* **Gotchas**
|
|
340
|
+
*
|
|
341
|
+
* Throws a `RangeError` when `failures` is not an integer of at least 1.
|
|
342
|
+
*
|
|
343
|
+
* @stability experimental
|
|
344
|
+
* @category constructors
|
|
345
|
+
* @since 4.0.6
|
|
346
|
+
*/
|
|
347
|
+
export const consecutive = failures => {
|
|
348
|
+
if (!Number.isInteger(failures) || failures < 1) {
|
|
349
|
+
throw new RangeError(`CircuitBreaker.consecutive: failures must be an integer >= 1, got ${failures}`);
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
_tag: "Consecutive",
|
|
353
|
+
failures
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* Creates a trip policy that opens when the failure rate over the last
|
|
358
|
+
* recorded calls reaches a threshold.
|
|
359
|
+
*
|
|
360
|
+
* **Details**
|
|
361
|
+
*
|
|
362
|
+
* `threshold` is a fraction in `[0, 1]` and defaults to `0.5`. `size` is the
|
|
363
|
+
* number of recorded calls kept and defaults to `100`. `minimumCalls`
|
|
364
|
+
* defaults to `size`. A ring with no failures never opens the circuit.
|
|
365
|
+
*
|
|
366
|
+
* **Gotchas**
|
|
367
|
+
*
|
|
368
|
+
* Throws a `RangeError` when `threshold` is outside `[0, 1]`, when `size` is
|
|
369
|
+
* not an integer of at least 1, or when `minimumCalls` is not an integer
|
|
370
|
+
* between 1 and `size`.
|
|
371
|
+
*
|
|
372
|
+
* @stability experimental
|
|
373
|
+
* @category constructors
|
|
374
|
+
* @since 4.0.6
|
|
375
|
+
*/
|
|
376
|
+
export const rate = options => {
|
|
377
|
+
const threshold = options.threshold ?? 0.5;
|
|
378
|
+
const size = options.size ?? 100;
|
|
379
|
+
const minimumCalls = options.minimumCalls ?? size;
|
|
380
|
+
if (!(threshold >= 0 && threshold <= 1)) {
|
|
381
|
+
throw new RangeError(`CircuitBreaker.rate: threshold must be in [0, 1], got ${threshold}`);
|
|
382
|
+
}
|
|
383
|
+
if (!Number.isInteger(size) || size < 1) {
|
|
384
|
+
throw new RangeError(`CircuitBreaker.rate: size must be an integer >= 1, got ${size}`);
|
|
385
|
+
}
|
|
386
|
+
if (!Number.isInteger(minimumCalls) || minimumCalls < 1 || minimumCalls > size) {
|
|
387
|
+
throw new RangeError(`CircuitBreaker.rate: minimumCalls must be an integer in [1, size], got ${minimumCalls}`);
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
_tag: "Rate",
|
|
391
|
+
threshold,
|
|
392
|
+
minimumCalls,
|
|
393
|
+
size
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
/**
|
|
397
|
+
* Outcome that records a success and resets a consecutive failure streak.
|
|
398
|
+
*
|
|
399
|
+
* @stability experimental
|
|
400
|
+
* @category constructors
|
|
401
|
+
* @since 4.0.6
|
|
402
|
+
*/
|
|
403
|
+
export const success = {
|
|
404
|
+
_tag: "Success"
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* Outcome that records nothing.
|
|
408
|
+
*
|
|
409
|
+
* @stability experimental
|
|
410
|
+
* @category constructors
|
|
411
|
+
* @since 4.0.6
|
|
412
|
+
*/
|
|
413
|
+
export const ignored = {
|
|
414
|
+
_tag: "Ignored"
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Creates an outcome that records a failure.
|
|
418
|
+
*
|
|
419
|
+
* **Details**
|
|
420
|
+
*
|
|
421
|
+
* `retryAfter` is a hint, such as an HTTP `Retry-After` header. It is used
|
|
422
|
+
* only when this failure opens the circuit, can only lengthen the opening,
|
|
423
|
+
* and is capped at `maxOpenDuration`.
|
|
424
|
+
*
|
|
425
|
+
* @stability experimental
|
|
426
|
+
* @category constructors
|
|
427
|
+
* @since 4.0.6
|
|
428
|
+
*/
|
|
429
|
+
export const failure = options => ({
|
|
430
|
+
_tag: "Failure",
|
|
431
|
+
retryAfter: options?.retryAfter === undefined ? undefined : Duration.fromInputUnsafe(options.retryAfter)
|
|
432
|
+
});
|
|
433
|
+
/**
|
|
434
|
+
* Error reason for a call rejected by an Open circuit.
|
|
435
|
+
*
|
|
436
|
+
* @stability experimental
|
|
437
|
+
* @category errors
|
|
438
|
+
* @since 4.0.6
|
|
439
|
+
*/
|
|
440
|
+
export class CircuitOpen extends /*#__PURE__*/Schema.Error("effect/persistence/CircuitBreaker/CircuitOpen")({
|
|
441
|
+
_tag: /*#__PURE__*/Schema.tag("CircuitOpen"),
|
|
442
|
+
key: Schema.String,
|
|
443
|
+
retryAfter: Schema.DurationFromMillis
|
|
444
|
+
}) {
|
|
445
|
+
/**
|
|
446
|
+
* Public message used when an Open circuit rejects a call.
|
|
447
|
+
*
|
|
448
|
+
* @stability experimental
|
|
449
|
+
* @since 4.0.6
|
|
450
|
+
*/
|
|
451
|
+
get message() {
|
|
452
|
+
return `Circuit open`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Error reason for a call rejected while a HalfOpen circuit's probe runs.
|
|
457
|
+
*
|
|
458
|
+
* @stability experimental
|
|
459
|
+
* @category errors
|
|
460
|
+
* @since 4.0.6
|
|
461
|
+
*/
|
|
462
|
+
export class CircuitHalfOpen extends /*#__PURE__*/Schema.Error("effect/persistence/CircuitBreaker/CircuitHalfOpen")({
|
|
463
|
+
_tag: /*#__PURE__*/Schema.tag("CircuitHalfOpen"),
|
|
464
|
+
key: Schema.String
|
|
465
|
+
}) {
|
|
466
|
+
/**
|
|
467
|
+
* Public message used when a HalfOpen circuit rejects a call.
|
|
468
|
+
*
|
|
469
|
+
* @stability experimental
|
|
470
|
+
* @since 4.0.6
|
|
471
|
+
*/
|
|
472
|
+
get message() {
|
|
473
|
+
return `Circuit half-open`;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Error reason for failures in the backing `CircuitBreakerStore`.
|
|
478
|
+
*
|
|
479
|
+
* @stability experimental
|
|
480
|
+
* @category errors
|
|
481
|
+
* @since 4.0.6
|
|
482
|
+
*/
|
|
483
|
+
export class CircuitBreakerStoreError extends /*#__PURE__*/Schema.Error("effect/persistence/CircuitBreaker/CircuitBreakerStoreError")({
|
|
484
|
+
_tag: /*#__PURE__*/Schema.tag("CircuitBreakerStoreError"),
|
|
485
|
+
message: Schema.String,
|
|
486
|
+
cause: /*#__PURE__*/Schema.optional(/*#__PURE__*/Schema.Defect())
|
|
487
|
+
}) {}
|
|
488
|
+
/**
|
|
489
|
+
* Schema for all reasons that can be carried by `CircuitBreakerError`.
|
|
490
|
+
*
|
|
491
|
+
* @stability experimental
|
|
492
|
+
* @category errors
|
|
493
|
+
* @since 4.0.6
|
|
494
|
+
*/
|
|
495
|
+
export const CircuitBreakerErrorReason = /*#__PURE__*/Schema.Union([CircuitOpen, CircuitHalfOpen, CircuitBreakerStoreError]);
|
|
496
|
+
/**
|
|
497
|
+
* Runtime type identifier for `CircuitBreakerError`.
|
|
498
|
+
*
|
|
499
|
+
* @stability experimental
|
|
500
|
+
* @category type IDs
|
|
501
|
+
* @since 4.0.6
|
|
502
|
+
*/
|
|
503
|
+
export const ErrorTypeId = "~effect/persistence/CircuitBreaker/CircuitBreakerError";
|
|
504
|
+
/**
|
|
505
|
+
* Error raised by circuit breaker operations, wrapping a concrete failure
|
|
506
|
+
* `reason`.
|
|
507
|
+
*
|
|
508
|
+
* @stability experimental
|
|
509
|
+
* @category errors
|
|
510
|
+
* @since 4.0.6
|
|
511
|
+
*/
|
|
512
|
+
export class CircuitBreakerError extends /*#__PURE__*/Schema.Error(ErrorTypeId)({
|
|
513
|
+
_tag: /*#__PURE__*/Schema.tag("CircuitBreakerError"),
|
|
514
|
+
reason: CircuitBreakerErrorReason
|
|
515
|
+
}) {
|
|
516
|
+
// @effect-diagnostics-next-line overriddenSchemaConstructor:off
|
|
517
|
+
constructor(props) {
|
|
518
|
+
if ("cause" in props.reason) {
|
|
519
|
+
super({
|
|
520
|
+
...props,
|
|
521
|
+
cause: props.reason.cause
|
|
522
|
+
});
|
|
523
|
+
} else {
|
|
524
|
+
super(props);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Marks this value as a circuit breaker error for runtime guards.
|
|
529
|
+
*
|
|
530
|
+
* @stability experimental
|
|
531
|
+
* @since 4.0.6
|
|
532
|
+
*/
|
|
533
|
+
[ErrorTypeId] = ErrorTypeId;
|
|
534
|
+
get message() {
|
|
535
|
+
return this.reason.message;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Store that holds circuit state for a `CircuitBreaker`.
|
|
540
|
+
*
|
|
541
|
+
* **Details**
|
|
542
|
+
*
|
|
543
|
+
* - `admit` returns an `Admission`, or fails with `CircuitOpen` or
|
|
544
|
+
* `CircuitHalfOpen`. Its scope stays open while the call runs.
|
|
545
|
+
* - `record` applies an outcome atomically and opens the circuit for
|
|
546
|
+
* `openFor` if it trips. An outcome stamped with an older generation
|
|
547
|
+
* changes nothing.
|
|
548
|
+
* - `state` reads a key without changing it.
|
|
549
|
+
*
|
|
550
|
+
* Each operation reads the current time inside the store. A close keeps
|
|
551
|
+
* `consecutiveOpenings` and sets a reset deadline of the close time plus the
|
|
552
|
+
* last `openFor`. The first `admit` at or after the deadline resets the count
|
|
553
|
+
* to 0. `record` never reads the deadline, so a call admitted before a reset
|
|
554
|
+
* still opens with the `openFor` it computed.
|
|
555
|
+
*
|
|
556
|
+
* @stability experimental
|
|
557
|
+
* @category services
|
|
558
|
+
* @since 4.0.6
|
|
559
|
+
*/
|
|
560
|
+
export class CircuitBreakerStore extends /*#__PURE__*/Context.Service()("effect/persistence/CircuitBreaker/CircuitBreakerStore") {}
|
|
561
|
+
/**
|
|
562
|
+
* Creates an in-memory `CircuitBreakerStore` layer.
|
|
563
|
+
*
|
|
564
|
+
* **Details**
|
|
565
|
+
*
|
|
566
|
+
* State is local to the layer and uses `Clock`. `idleTimeToLive` (default
|
|
567
|
+
* one hour) drops a key once it has been idle that long. An Open key is kept
|
|
568
|
+
* that long past its open time, and a key whose probe is running is never
|
|
569
|
+
* dropped. A dropped key starts again as Closed with a backoff count of 0.
|
|
570
|
+
*
|
|
571
|
+
* @stability experimental
|
|
572
|
+
* @category layers
|
|
573
|
+
* @since 4.0.6
|
|
574
|
+
*/
|
|
575
|
+
export const layerStoreMemory = options => Layer.sync(CircuitBreakerStore, () => {
|
|
576
|
+
const idleTimeToLive = Duration.toMillis(Duration.fromInputUnsafe(options?.idleTimeToLive ?? "1 hour"));
|
|
577
|
+
const entries = new Map();
|
|
578
|
+
let nextGeneration = 0;
|
|
579
|
+
let lastSweep = 0;
|
|
580
|
+
const isExpired = (entry, now) => {
|
|
581
|
+
switch (entry.state) {
|
|
582
|
+
case "Closed":
|
|
583
|
+
return now - entry.lastActivity >= idleTimeToLive;
|
|
584
|
+
case "Open":
|
|
585
|
+
return now - Math.max(entry.lastActivity, entry.openUntil) >= idleTimeToLive;
|
|
586
|
+
case "HalfOpen":
|
|
587
|
+
return !entry.probing && now - entry.lastActivity >= idleTimeToLive;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
const sweep = now => {
|
|
591
|
+
if (now - lastSweep < idleTimeToLive) return;
|
|
592
|
+
lastSweep = now;
|
|
593
|
+
for (const [key, entry] of entries) {
|
|
594
|
+
if (isExpired(entry, now)) entries.delete(key);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const lookup = (key, now) => {
|
|
598
|
+
const entry = entries.get(key);
|
|
599
|
+
return entry !== undefined && !isExpired(entry, now) ? entry : undefined;
|
|
600
|
+
};
|
|
601
|
+
const resetCounters = entry => {
|
|
602
|
+
entry.streak = 0;
|
|
603
|
+
entry.ring = [];
|
|
604
|
+
entry.ringFailures = 0;
|
|
605
|
+
};
|
|
606
|
+
const open = (key, entry, from, openFor, now) => {
|
|
607
|
+
entry.state = "Open";
|
|
608
|
+
entry.generation = nextGeneration++;
|
|
609
|
+
entry.lastOpenFor = Duration.toMillis(openFor);
|
|
610
|
+
entry.openUntil = now + entry.lastOpenFor;
|
|
611
|
+
entry.consecutiveOpenings++;
|
|
612
|
+
entry.probing = false;
|
|
613
|
+
resetCounters(entry);
|
|
614
|
+
return {
|
|
615
|
+
key,
|
|
616
|
+
from,
|
|
617
|
+
to: "Open",
|
|
618
|
+
retryAfter: openFor,
|
|
619
|
+
consecutiveOpenings: entry.consecutiveOpenings
|
|
620
|
+
};
|
|
621
|
+
};
|
|
622
|
+
const trips = (entry, policy) => {
|
|
623
|
+
if (policy._tag === "Consecutive") return entry.streak >= policy.failures;
|
|
624
|
+
return entry.ring.length >= policy.minimumCalls && entry.ringFailures > 0 && entry.ringFailures / entry.ring.length >= policy.threshold;
|
|
625
|
+
};
|
|
626
|
+
return CircuitBreakerStore.of({
|
|
627
|
+
admit: key => Effect.flatMap(Clock.currentTimeMillis, now => {
|
|
628
|
+
sweep(now);
|
|
629
|
+
let entry = lookup(key, now);
|
|
630
|
+
if (entry === undefined) {
|
|
631
|
+
entry = {
|
|
632
|
+
state: "Closed",
|
|
633
|
+
generation: nextGeneration++,
|
|
634
|
+
lastActivity: now,
|
|
635
|
+
streak: 0,
|
|
636
|
+
ring: [],
|
|
637
|
+
ringFailures: 0,
|
|
638
|
+
openUntil: 0,
|
|
639
|
+
consecutiveOpenings: 0,
|
|
640
|
+
lastOpenFor: 0,
|
|
641
|
+
resetAt: 0,
|
|
642
|
+
probing: false
|
|
643
|
+
};
|
|
644
|
+
entries.set(key, entry);
|
|
645
|
+
}
|
|
646
|
+
let transition;
|
|
647
|
+
switch (entry.state) {
|
|
648
|
+
case "Closed":
|
|
649
|
+
{
|
|
650
|
+
// A call admitted before this reset can still trip the circuit.
|
|
651
|
+
// It opens with the openFor it computed from its older count,
|
|
652
|
+
// and the stored count restarts at 1.
|
|
653
|
+
if (entry.consecutiveOpenings > 0 && now >= entry.resetAt) {
|
|
654
|
+
entry.consecutiveOpenings = 0;
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
case "Open":
|
|
659
|
+
{
|
|
660
|
+
if (now < entry.openUntil) {
|
|
661
|
+
return Effect.fail(new CircuitBreakerError({
|
|
662
|
+
reason: new CircuitOpen({
|
|
663
|
+
key,
|
|
664
|
+
retryAfter: Duration.millis(entry.openUntil - now)
|
|
665
|
+
})
|
|
666
|
+
}));
|
|
667
|
+
}
|
|
668
|
+
entry.state = "HalfOpen";
|
|
669
|
+
transition = {
|
|
670
|
+
key,
|
|
671
|
+
from: "Open",
|
|
672
|
+
to: "HalfOpen"
|
|
673
|
+
};
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
case "HalfOpen":
|
|
677
|
+
{
|
|
678
|
+
if (entry.probing) {
|
|
679
|
+
return Effect.fail(new CircuitBreakerError({
|
|
680
|
+
reason: new CircuitHalfOpen({
|
|
681
|
+
key
|
|
682
|
+
})
|
|
683
|
+
}));
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
entry.lastActivity = now;
|
|
689
|
+
if (entry.state === "HalfOpen") {
|
|
690
|
+
entry.probing = true;
|
|
691
|
+
entry.generation = nextGeneration++;
|
|
692
|
+
}
|
|
693
|
+
return Effect.succeed({
|
|
694
|
+
generation: entry.generation,
|
|
695
|
+
probe: entry.state === "HalfOpen",
|
|
696
|
+
consecutiveOpenings: entry.consecutiveOpenings,
|
|
697
|
+
transition
|
|
698
|
+
});
|
|
699
|
+
}),
|
|
700
|
+
record: ({
|
|
701
|
+
generation,
|
|
702
|
+
key,
|
|
703
|
+
openFor,
|
|
704
|
+
outcome,
|
|
705
|
+
tripPolicy
|
|
706
|
+
}) => Effect.map(Clock.currentTimeMillis, now => {
|
|
707
|
+
const entry = lookup(key, now);
|
|
708
|
+
if (entry === undefined || entry.generation !== generation) return undefined;
|
|
709
|
+
entry.lastActivity = now;
|
|
710
|
+
if (entry.state === "HalfOpen") {
|
|
711
|
+
switch (outcome) {
|
|
712
|
+
case "Ignored":
|
|
713
|
+
{
|
|
714
|
+
entry.probing = false;
|
|
715
|
+
return undefined;
|
|
716
|
+
}
|
|
717
|
+
case "Failure":
|
|
718
|
+
return open(key, entry, "HalfOpen", openFor, now);
|
|
719
|
+
case "Success":
|
|
720
|
+
{
|
|
721
|
+
entry.state = "Closed";
|
|
722
|
+
entry.generation = nextGeneration++;
|
|
723
|
+
entry.resetAt = now + entry.lastOpenFor;
|
|
724
|
+
entry.probing = false;
|
|
725
|
+
resetCounters(entry);
|
|
726
|
+
return {
|
|
727
|
+
key,
|
|
728
|
+
from: "HalfOpen",
|
|
729
|
+
to: "Closed"
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (entry.state !== "Closed" || outcome === "Ignored") return undefined;
|
|
735
|
+
const failed = outcome === "Failure";
|
|
736
|
+
entry.streak = failed ? entry.streak + 1 : 0;
|
|
737
|
+
if (tripPolicy._tag === "Rate") {
|
|
738
|
+
entry.ring.push(failed);
|
|
739
|
+
if (failed) entry.ringFailures++;
|
|
740
|
+
while (entry.ring.length > tripPolicy.size) {
|
|
741
|
+
if (entry.ring.shift()) entry.ringFailures--;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return trips(entry, tripPolicy) ? open(key, entry, "Closed", openFor, now) : undefined;
|
|
745
|
+
}),
|
|
746
|
+
state: key => Effect.map(Clock.currentTimeMillis, now => {
|
|
747
|
+
const entry = lookup(key, now);
|
|
748
|
+
if (entry === undefined) return {
|
|
749
|
+
_tag: "Closed",
|
|
750
|
+
key
|
|
751
|
+
};
|
|
752
|
+
switch (entry.state) {
|
|
753
|
+
case "Closed":
|
|
754
|
+
return {
|
|
755
|
+
_tag: "Closed",
|
|
756
|
+
key
|
|
757
|
+
};
|
|
758
|
+
case "Open":
|
|
759
|
+
return {
|
|
760
|
+
_tag: "Open",
|
|
761
|
+
key,
|
|
762
|
+
retryAfter: Duration.millis(Math.max(0, entry.openUntil - now)),
|
|
763
|
+
consecutiveOpenings: entry.consecutiveOpenings
|
|
764
|
+
};
|
|
765
|
+
case "HalfOpen":
|
|
766
|
+
return {
|
|
767
|
+
_tag: "HalfOpen",
|
|
768
|
+
key,
|
|
769
|
+
probing: entry.probing,
|
|
770
|
+
consecutiveOpenings: entry.consecutiveOpenings
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
})
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
/**
|
|
777
|
+
* Creates a Redis-backed `CircuitBreakerStore` layer.
|
|
778
|
+
*
|
|
779
|
+
* **Details**
|
|
780
|
+
*
|
|
781
|
+
* - Each circuit is one hash at `${prefix}${key}`, and `prefix` defaults to
|
|
782
|
+
* `"circuitbreaker:"`. Times come from Redis `TIME`, so client clock skew
|
|
783
|
+
* never matters. Requires Redis 5 or later.
|
|
784
|
+
* - The probe holds a lease of `probeLease` (default 10 seconds), renewed
|
|
785
|
+
* while it runs. If the lease lapses, another caller takes over and the old
|
|
786
|
+
* probe's outcome is discarded.
|
|
787
|
+
* - `idleTimeToLive` (default one hour) expires idle keys. An Open key, or one
|
|
788
|
+
* whose probe holds the lease, is kept that long past its open time or
|
|
789
|
+
* lease, so expiry never cuts a probe short or resets the backoff early. An
|
|
790
|
+
* expired key starts again as Closed with a backoff count of 0.
|
|
791
|
+
*
|
|
792
|
+
* **Gotchas**
|
|
793
|
+
*
|
|
794
|
+
* - If renewals cannot reach Redis, the lease lapses and a second probe can
|
|
795
|
+
* start.
|
|
796
|
+
* - Durations above 2^50 milliseconds cannot be stored. An operation that
|
|
797
|
+
* needs one fails with `CircuitBreakerStoreError`.
|
|
798
|
+
*
|
|
799
|
+
* @stability experimental
|
|
800
|
+
* @category layers
|
|
801
|
+
* @since 4.0.6
|
|
802
|
+
*/
|
|
803
|
+
export const layerStoreRedis = options => Layer.effect(CircuitBreakerStore, makeStoreRedis(options));
|
|
804
|
+
/**
|
|
805
|
+
* Creates a Redis-backed `CircuitBreakerStore` layer from wrapped
|
|
806
|
+
* configuration options.
|
|
807
|
+
*
|
|
808
|
+
* **Details**
|
|
809
|
+
*
|
|
810
|
+
* Takes the same options as `layerStoreRedis`.
|
|
811
|
+
*
|
|
812
|
+
* @stability experimental
|
|
813
|
+
* @category layers
|
|
814
|
+
* @since 4.0.6
|
|
815
|
+
*/
|
|
816
|
+
export const layerStoreRedisConfig = options => Layer.effect(CircuitBreakerStore, Effect.flatMap(Config.unwrap(options), makeStoreRedis));
|
|
817
|
+
const makeStoreRedis = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
818
|
+
const redis = yield* Redis.Redis;
|
|
819
|
+
const prefix = options?.prefix ?? "circuitbreaker:";
|
|
820
|
+
const leaseMillis = toRedisMillis(options?.probeLease ?? "10 seconds");
|
|
821
|
+
const idleMillis = toRedisMillis(options?.idleTimeToLive ?? "1 hour");
|
|
822
|
+
const renewInterval = Duration.millis(leaseMillis / 3);
|
|
823
|
+
const admitRedis = redis.eval(admitScript);
|
|
824
|
+
const recordRedis = redis.eval(recordScript);
|
|
825
|
+
const stateRedis = redis.eval(stateScript);
|
|
826
|
+
const renewRedis = redis.eval(renewScript);
|
|
827
|
+
const storeError = message => error => new CircuitBreakerError({
|
|
828
|
+
reason: new CircuitBreakerStoreError({
|
|
829
|
+
message,
|
|
830
|
+
cause: error.cause
|
|
831
|
+
})
|
|
832
|
+
});
|
|
833
|
+
// Stops once the lease is gone, because a renewal can never win it back.
|
|
834
|
+
const renew = Effect.fnUntraced(function* (redisKey, generation) {
|
|
835
|
+
while (true) {
|
|
836
|
+
yield* Effect.sleep(renewInterval);
|
|
837
|
+
const renewed = yield* renewRedis(redisKey, generation, leaseMillis, idleMillis).pipe(Effect.catchCauseFilter(findRecoverableError, (_, cause) => Effect.as(reportUnhandled("CircuitBreaker failed to renew a probe lease, retrying", cause), 1)));
|
|
838
|
+
if (renewed !== 1) return;
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
return CircuitBreakerStore.of({
|
|
842
|
+
admit: Effect.fnUntraced(function* (key) {
|
|
843
|
+
const redisKey = `${prefix}${key}`;
|
|
844
|
+
const result = yield* admitRedis(redisKey, leaseMillis, idleMillis).pipe(Effect.mapError(storeError("Failed to admit a call through the circuit")));
|
|
845
|
+
switch (result[0]) {
|
|
846
|
+
case "open":
|
|
847
|
+
return yield* new CircuitBreakerError({
|
|
848
|
+
reason: new CircuitOpen({
|
|
849
|
+
key,
|
|
850
|
+
retryAfter: Duration.millis(Math.max(0, Number(result[1])))
|
|
851
|
+
})
|
|
852
|
+
});
|
|
853
|
+
case "halfopen":
|
|
854
|
+
return yield* new CircuitBreakerError({
|
|
855
|
+
reason: new CircuitHalfOpen({
|
|
856
|
+
key
|
|
857
|
+
})
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
const [, generation, probe, consecutiveOpenings, moved] = result;
|
|
861
|
+
if (probe === 1) {
|
|
862
|
+
yield* renew(redisKey, generation).pipe(Effect.interruptible, Effect.forkScoped);
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
generation: Number(generation),
|
|
866
|
+
probe: probe === 1,
|
|
867
|
+
consecutiveOpenings: Number(consecutiveOpenings),
|
|
868
|
+
transition: moved === 1 ? {
|
|
869
|
+
key,
|
|
870
|
+
from: "Open",
|
|
871
|
+
to: "HalfOpen"
|
|
872
|
+
} : undefined
|
|
873
|
+
};
|
|
874
|
+
}),
|
|
875
|
+
record: ({
|
|
876
|
+
generation,
|
|
877
|
+
key,
|
|
878
|
+
openFor,
|
|
879
|
+
outcome,
|
|
880
|
+
tripPolicy
|
|
881
|
+
}) => recordRedis(`${prefix}${key}`, String(generation), outcome, tripPolicy, Math.max(0, Math.ceil(Duration.toMillis(openFor))), idleMillis).pipe(Effect.mapError(storeError("Failed to record an outcome")), Effect.map(([moved, consecutiveOpenings]) => {
|
|
882
|
+
switch (moved) {
|
|
883
|
+
case "closed-open":
|
|
884
|
+
case "halfopen-open":
|
|
885
|
+
return {
|
|
886
|
+
key,
|
|
887
|
+
from: moved === "closed-open" ? "Closed" : "HalfOpen",
|
|
888
|
+
to: "Open",
|
|
889
|
+
retryAfter: openFor,
|
|
890
|
+
consecutiveOpenings: Number(consecutiveOpenings)
|
|
891
|
+
};
|
|
892
|
+
case "halfopen-closed":
|
|
893
|
+
return {
|
|
894
|
+
key,
|
|
895
|
+
from: "HalfOpen",
|
|
896
|
+
to: "Closed"
|
|
897
|
+
};
|
|
898
|
+
default:
|
|
899
|
+
return undefined;
|
|
900
|
+
}
|
|
901
|
+
})),
|
|
902
|
+
state: key => stateRedis(`${prefix}${key}`).pipe(Effect.mapError(storeError("Failed to read the circuit state")), Effect.map(([state, remaining, consecutiveOpenings, probing]) => {
|
|
903
|
+
switch (state) {
|
|
904
|
+
case "Open":
|
|
905
|
+
return {
|
|
906
|
+
_tag: "Open",
|
|
907
|
+
key,
|
|
908
|
+
retryAfter: Duration.millis(Math.max(0, Number(remaining))),
|
|
909
|
+
consecutiveOpenings: Number(consecutiveOpenings)
|
|
910
|
+
};
|
|
911
|
+
case "HalfOpen":
|
|
912
|
+
return {
|
|
913
|
+
_tag: "HalfOpen",
|
|
914
|
+
key,
|
|
915
|
+
probing: probing === 1,
|
|
916
|
+
consecutiveOpenings: Number(consecutiveOpenings)
|
|
917
|
+
};
|
|
918
|
+
default:
|
|
919
|
+
return {
|
|
920
|
+
_tag: "Closed",
|
|
921
|
+
key
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
}))
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
const toRedisMillis = input => Math.max(1, Math.ceil(Duration.toMillis(Duration.fromInputUnsafe(input))));
|
|
928
|
+
// Shared Lua prelude.
|
|
929
|
+
//
|
|
930
|
+
// Numeric arguments are capped at 2^50 (about 35,000 years in milliseconds),
|
|
931
|
+
// so every TTL and timestamp derived from them stays an exact integer. Redis
|
|
932
|
+
// formats a Lua number above 1e17 with an exponent, which PEXPIRE rejects
|
|
933
|
+
// after earlier writes have already happened. `int` formats every number
|
|
934
|
+
// written to Redis without one.
|
|
935
|
+
//
|
|
936
|
+
// Generations are the larger of the previous generation plus one and the
|
|
937
|
+
// current Redis time in microseconds, so a key that expired and was recreated
|
|
938
|
+
// never reissues a generation an old permit still holds. They are kept as
|
|
939
|
+
// integer strings so they never pass through a float format.
|
|
940
|
+
const luaPrelude = `
|
|
941
|
+
local maxValue = 1125899906842624
|
|
942
|
+
local function bounded(value, min)
|
|
943
|
+
local n = tonumber(value)
|
|
944
|
+
if n == nil or n ~= n or n < min or n > maxValue then return nil end
|
|
945
|
+
return n
|
|
946
|
+
end
|
|
947
|
+
local function positive(value) return bounded(value, 1) end
|
|
948
|
+
local function nonNegative(value) return bounded(value, 0) end
|
|
949
|
+
local function int(n) return string.format("%.0f", n) end
|
|
950
|
+
local time = redis.call("TIME")
|
|
951
|
+
local nowMicros = tonumber(time[1]) * 1000000 + tonumber(time[2])
|
|
952
|
+
local now = math.floor(nowMicros / 1000)
|
|
953
|
+
local function nextGeneration(current)
|
|
954
|
+
return int(math.max((tonumber(current) or 0) + 1, nowMicros))
|
|
955
|
+
end
|
|
956
|
+
`;
|
|
957
|
+
// Returns { "ok", generation, probe, consecutiveOpenings, movedToHalfOpen },
|
|
958
|
+
// { "open", remainingMillis } or { "halfopen" }.
|
|
959
|
+
const admitScript = /*#__PURE__*/Redis.script((key, leaseMillis, idleMillis) => [key, leaseMillis, idleMillis], {
|
|
960
|
+
numberOfKeys: 1,
|
|
961
|
+
lua: `${luaPrelude}
|
|
962
|
+
local key = KEYS[1]
|
|
963
|
+
local lease = positive(ARGV[1])
|
|
964
|
+
local idle = positive(ARGV[2])
|
|
965
|
+
if not lease or not idle then
|
|
966
|
+
return redis.error_reply("CircuitBreaker admit: invalid arguments")
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
local fields = redis.call("HMGET", key, "state", "generation", "openUntil", "openings", "leaseUntil", "resetAt")
|
|
970
|
+
local state = fields[1]
|
|
971
|
+
local generation = fields[2]
|
|
972
|
+
local openings = tonumber(fields[4]) or 0
|
|
973
|
+
|
|
974
|
+
if not state then
|
|
975
|
+
generation = nextGeneration(0)
|
|
976
|
+
redis.call("HSET", key, "state", "Closed", "generation", generation)
|
|
977
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
978
|
+
return { "ok", generation, 0, 0, 0 }
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
if state == "Closed" then
|
|
982
|
+
-- A call admitted before this reset can still trip the circuit. It opens
|
|
983
|
+
-- with the openFor it computed from its older count, and the stored count
|
|
984
|
+
-- restarts at 1.
|
|
985
|
+
if openings > 0 and now >= (tonumber(fields[6]) or 0) then
|
|
986
|
+
openings = 0
|
|
987
|
+
redis.call("HSET", key, "openings", 0)
|
|
988
|
+
end
|
|
989
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
990
|
+
return { "ok", generation, 0, openings, 0 }
|
|
991
|
+
end
|
|
992
|
+
|
|
993
|
+
local moved = 0
|
|
994
|
+
if state == "Open" then
|
|
995
|
+
local openUntil = tonumber(fields[3]) or 0
|
|
996
|
+
if now < openUntil then
|
|
997
|
+
return { "open", openUntil - now }
|
|
998
|
+
end
|
|
999
|
+
moved = 1
|
|
1000
|
+
elseif (tonumber(fields[5]) or 0) > now then
|
|
1001
|
+
return { "halfopen" }
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
-- Every probe admission, including a takeover of a lapsed lease, starts a
|
|
1005
|
+
-- new generation.
|
|
1006
|
+
generation = nextGeneration(generation)
|
|
1007
|
+
redis.call("HSET", key, "state", "HalfOpen", "generation", generation, "leaseUntil", int(now + lease))
|
|
1008
|
+
redis.call("PEXPIRE", key, int(lease + idle))
|
|
1009
|
+
return { "ok", generation, 1, openings, moved }
|
|
1010
|
+
`
|
|
1011
|
+
}).withReturnType();
|
|
1012
|
+
// Returns { transition, consecutiveOpenings }, where transition is "none",
|
|
1013
|
+
// "closed-open", "halfopen-open" or "halfopen-closed".
|
|
1014
|
+
//
|
|
1015
|
+
// ARGV holds the generation, the outcome, the policy tag, the policy's
|
|
1016
|
+
// failures (Consecutive) or threshold (Rate), minimumCalls and size (0 under
|
|
1017
|
+
// Consecutive), then the open time and idle time in milliseconds.
|
|
1018
|
+
const recordScript = /*#__PURE__*/Redis.script((key, generation, outcome, tripPolicy, openForMillis, idleMillis) => [key, generation, outcome, tripPolicy._tag, ...(tripPolicy._tag === "Consecutive" ? [tripPolicy.failures, 0, 0] : [tripPolicy.threshold, tripPolicy.minimumCalls, tripPolicy.size]), openForMillis, idleMillis], {
|
|
1019
|
+
numberOfKeys: 1,
|
|
1020
|
+
lua: `${luaPrelude}
|
|
1021
|
+
local key = KEYS[1]
|
|
1022
|
+
local expected = ARGV[1]
|
|
1023
|
+
local outcome = ARGV[2]
|
|
1024
|
+
local policy = ARGV[3]
|
|
1025
|
+
local openFor = nonNegative(ARGV[7])
|
|
1026
|
+
local idle = positive(ARGV[8])
|
|
1027
|
+
local valid = openFor ~= nil and idle ~= nil and
|
|
1028
|
+
(outcome == "Success" or outcome == "Failure" or outcome == "Ignored")
|
|
1029
|
+
local failuresNeeded, threshold, minimumCalls, size
|
|
1030
|
+
if policy == "Consecutive" then
|
|
1031
|
+
failuresNeeded = positive(ARGV[4])
|
|
1032
|
+
valid = valid and failuresNeeded ~= nil
|
|
1033
|
+
elseif policy == "Rate" then
|
|
1034
|
+
threshold = nonNegative(ARGV[4])
|
|
1035
|
+
minimumCalls = positive(ARGV[5])
|
|
1036
|
+
size = positive(ARGV[6])
|
|
1037
|
+
valid = valid and threshold ~= nil and threshold <= 1 and minimumCalls ~= nil and size ~= nil and
|
|
1038
|
+
minimumCalls <= size
|
|
1039
|
+
else
|
|
1040
|
+
valid = false
|
|
1041
|
+
end
|
|
1042
|
+
if not valid then
|
|
1043
|
+
return redis.error_reply("CircuitBreaker record: invalid arguments")
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
local fields = redis.call("HMGET", key, "state", "generation", "streak", "ring", "failures", "openings", "lastOpenFor")
|
|
1047
|
+
local state = fields[1]
|
|
1048
|
+
if not state or fields[2] ~= expected then
|
|
1049
|
+
return { "none", 0 }
|
|
1050
|
+
end
|
|
1051
|
+
local openings = tonumber(fields[6]) or 0
|
|
1052
|
+
|
|
1053
|
+
local function open()
|
|
1054
|
+
openings = openings + 1
|
|
1055
|
+
redis.call(
|
|
1056
|
+
"HSET", key,
|
|
1057
|
+
"state", "Open",
|
|
1058
|
+
"generation", nextGeneration(fields[2]),
|
|
1059
|
+
"openUntil", int(now + openFor),
|
|
1060
|
+
"openings", openings,
|
|
1061
|
+
"lastOpenFor", int(openFor),
|
|
1062
|
+
"streak", 0,
|
|
1063
|
+
"ring", "",
|
|
1064
|
+
"failures", 0,
|
|
1065
|
+
"leaseUntil", 0
|
|
1066
|
+
)
|
|
1067
|
+
redis.call("PEXPIRE", key, int(openFor + idle))
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
if state == "HalfOpen" then
|
|
1071
|
+
if outcome == "Ignored" then
|
|
1072
|
+
-- A new generation fences a renewal already on its way, so the freed slot
|
|
1073
|
+
-- stays free.
|
|
1074
|
+
redis.call("HSET", key, "generation", nextGeneration(fields[2]), "leaseUntil", 0)
|
|
1075
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
1076
|
+
return { "none", openings }
|
|
1077
|
+
end
|
|
1078
|
+
if outcome == "Failure" then
|
|
1079
|
+
open()
|
|
1080
|
+
return { "halfopen-open", openings }
|
|
1081
|
+
end
|
|
1082
|
+
redis.call(
|
|
1083
|
+
"HSET", key,
|
|
1084
|
+
"state", "Closed",
|
|
1085
|
+
"generation", nextGeneration(fields[2]),
|
|
1086
|
+
"resetAt", int(now + (tonumber(fields[7]) or 0)),
|
|
1087
|
+
"streak", 0,
|
|
1088
|
+
"ring", "",
|
|
1089
|
+
"failures", 0,
|
|
1090
|
+
"leaseUntil", 0
|
|
1091
|
+
)
|
|
1092
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
1093
|
+
return { "halfopen-closed", openings }
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
if state ~= "Closed" then
|
|
1097
|
+
return { "none", openings }
|
|
1098
|
+
end
|
|
1099
|
+
if outcome == "Ignored" then
|
|
1100
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
1101
|
+
return { "none", openings }
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
local failed = outcome == "Failure"
|
|
1105
|
+
local streak = failed and (tonumber(fields[3]) or 0) + 1 or 0
|
|
1106
|
+
local trips
|
|
1107
|
+
local ring = fields[4] or ""
|
|
1108
|
+
local failures = tonumber(fields[5]) or 0
|
|
1109
|
+
if policy == "Consecutive" then
|
|
1110
|
+
trips = streak >= failuresNeeded
|
|
1111
|
+
else
|
|
1112
|
+
ring = ring .. (failed and "1" or "0")
|
|
1113
|
+
if failed then failures = failures + 1 end
|
|
1114
|
+
if #ring > size then
|
|
1115
|
+
local _, dropped = string.gsub(string.sub(ring, 1, #ring - size), "1", "")
|
|
1116
|
+
failures = failures - dropped
|
|
1117
|
+
ring = string.sub(ring, #ring - size + 1)
|
|
1118
|
+
end
|
|
1119
|
+
trips = #ring >= minimumCalls and failures > 0 and failures / #ring >= threshold
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
if trips then
|
|
1123
|
+
open()
|
|
1124
|
+
return { "closed-open", openings }
|
|
1125
|
+
end
|
|
1126
|
+
redis.call("HSET", key, "streak", streak, "ring", ring, "failures", failures)
|
|
1127
|
+
redis.call("PEXPIRE", key, int(idle))
|
|
1128
|
+
return { "none", openings }
|
|
1129
|
+
`
|
|
1130
|
+
}).withReturnType();
|
|
1131
|
+
// Read-only. Returns { state, remainingOpenMillis, consecutiveOpenings, probing }.
|
|
1132
|
+
const stateScript = /*#__PURE__*/Redis.script(key => [key], {
|
|
1133
|
+
numberOfKeys: 1,
|
|
1134
|
+
lua: `${luaPrelude}
|
|
1135
|
+
local fields = redis.call("HMGET", KEYS[1], "state", "openUntil", "openings", "leaseUntil")
|
|
1136
|
+
local state = fields[1]
|
|
1137
|
+
if not state then
|
|
1138
|
+
return { "Closed", 0, 0, 0 }
|
|
1139
|
+
end
|
|
1140
|
+
local remaining = math.max(0, (tonumber(fields[2]) or 0) - now)
|
|
1141
|
+
local probing = (tonumber(fields[4]) or 0) > now and 1 or 0
|
|
1142
|
+
return { state, remaining, tonumber(fields[3]) or 0, probing }
|
|
1143
|
+
`
|
|
1144
|
+
}).withReturnType();
|
|
1145
|
+
// Extends the probe lease only while the generation still holds it. Returns 1
|
|
1146
|
+
// when renewed, 0 when the lease is gone.
|
|
1147
|
+
const renewScript = /*#__PURE__*/Redis.script((key, generation, leaseMillis, idleMillis) => [key, generation, leaseMillis, idleMillis], {
|
|
1148
|
+
numberOfKeys: 1,
|
|
1149
|
+
lua: `${luaPrelude}
|
|
1150
|
+
local key = KEYS[1]
|
|
1151
|
+
local lease = positive(ARGV[2])
|
|
1152
|
+
local idle = positive(ARGV[3])
|
|
1153
|
+
if not lease or not idle then
|
|
1154
|
+
return redis.error_reply("CircuitBreaker renew: invalid arguments")
|
|
1155
|
+
end
|
|
1156
|
+
local fields = redis.call("HMGET", key, "state", "generation")
|
|
1157
|
+
if fields[1] ~= "HalfOpen" or fields[2] ~= ARGV[1] then
|
|
1158
|
+
return 0
|
|
1159
|
+
end
|
|
1160
|
+
redis.call("HSET", key, "leaseUntil", int(now + lease))
|
|
1161
|
+
redis.call("PEXPIRE", key, int(lease + idle))
|
|
1162
|
+
return 1
|
|
1163
|
+
`
|
|
1164
|
+
}).withReturnType();
|
|
1165
|
+
//# sourceMappingURL=CircuitBreaker.js.map
|