effect 4.1.0 → 4.1.2
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 +30 -0
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/ai-docs/package.json +2 -2
- package/dist/BigDecimal.d.ts +139 -7
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +83 -14
- package/dist/BigDecimal.js.map +1 -1
- package/dist/ByteSize.d.ts.map +1 -1
- package/dist/ByteSize.js +27 -14
- package/dist/ByteSize.js.map +1 -1
- package/dist/Equal.d.ts +2 -2
- package/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +3 -3
- package/dist/Equal.js.map +1 -1
- package/dist/Hash.d.ts +3 -2
- package/dist/Hash.d.ts.map +1 -1
- package/dist/Hash.js +5 -4
- package/dist/Hash.js.map +1 -1
- package/dist/Iterable.d.ts.map +1 -1
- package/dist/Iterable.js +38 -32
- package/dist/Iterable.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +27 -6
- package/dist/JsonSchema.js.map +1 -1
- package/dist/MutableRef.d.ts.map +1 -1
- package/dist/MutableRef.js +0 -7
- package/dist/MutableRef.js.map +1 -1
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +5 -1
- package/dist/Path.js.map +1 -1
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +2 -1
- package/dist/Pool.js.map +1 -1
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +0 -8
- package/dist/Ref.js.map +1 -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 +2 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2 -1
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +4 -2
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +4 -2
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +2 -2
- package/dist/SchemaTransformation.js +2 -2
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +6 -5
- package/dist/Stream.js.map +1 -1
- package/dist/ai/McpClient.d.ts +171 -19
- package/dist/ai/McpClient.d.ts.map +1 -1
- package/dist/ai/McpClient.js +277 -66
- package/dist/ai/McpClient.js.map +1 -1
- package/dist/ai/McpSchema.d.ts +108 -0
- package/dist/ai/McpSchema.d.ts.map +1 -1
- package/dist/ai/McpSchema.js +99 -0
- package/dist/ai/McpSchema.js.map +1 -1
- package/dist/ai/McpServer.d.ts +120 -6
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +258 -49
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +20 -11
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Tool.d.ts.map +1 -1
- package/dist/ai/Tool.js +5 -7
- package/dist/ai/Tool.js.map +1 -1
- package/dist/ai/internal/mcpCore.js +72 -3
- package/dist/ai/internal/mcpCore.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +44 -8
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js +12 -0
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +3 -3
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/ai/internal/mcpSkills.d.ts +2 -0
- package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
- package/dist/ai/internal/mcpSkills.js +428 -0
- package/dist/ai/internal/mcpSkills.js.map +1 -0
- package/dist/ai/internal/structured-output.js +11 -2
- package/dist/ai/internal/structured-output.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +30 -11
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/Entity.d.ts +26 -0
- package/dist/cluster/Entity.d.ts.map +1 -1
- package/dist/cluster/Entity.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +6 -6
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +1 -3
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Reply.d.ts +1 -1
- package/dist/cluster/Reply.d.ts.map +1 -1
- package/dist/cluster/Reply.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +13 -2
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts +13 -0
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +4 -1
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/internal/clusterAbandon.d.ts.map +1 -1
- package/dist/cluster/internal/clusterAbandon.js +6 -4
- package/dist/cluster/internal/clusterAbandon.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +63 -11
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/cluster/internal/transactionNotifications.d.ts +5 -0
- package/dist/cluster/internal/transactionNotifications.d.ts.map +1 -0
- package/dist/cluster/internal/transactionNotifications.js +5 -0
- package/dist/cluster/internal/transactionNotifications.js.map +1 -0
- package/dist/crypto/Aead.d.ts +2 -1
- package/dist/crypto/Aead.d.ts.map +1 -1
- package/dist/crypto/Aead.js +2 -2
- package/dist/crypto/Aead.js.map +1 -1
- package/dist/crypto/Cipher.d.ts +2 -1
- package/dist/crypto/Cipher.d.ts.map +1 -1
- package/dist/crypto/Cipher.js +2 -2
- package/dist/crypto/Cipher.js.map +1 -1
- package/dist/crypto/KeyAgreement.d.ts +3 -2
- package/dist/crypto/KeyAgreement.d.ts.map +1 -1
- package/dist/crypto/KeyAgreement.js +2 -2
- package/dist/crypto/KeyAgreement.js.map +1 -1
- package/dist/crypto/KeyFormat.d.ts +33 -1
- package/dist/crypto/KeyFormat.d.ts.map +1 -1
- package/dist/crypto/KeyFormat.js.map +1 -1
- package/dist/crypto/KeyWrap.d.ts +4 -3
- package/dist/crypto/KeyWrap.d.ts.map +1 -1
- package/dist/crypto/KeyWrap.js +2 -2
- package/dist/crypto/KeyWrap.js.map +1 -1
- package/dist/crypto/Mac.d.ts +2 -1
- package/dist/crypto/Mac.d.ts.map +1 -1
- package/dist/crypto/Mac.js +2 -2
- package/dist/crypto/Mac.js.map +1 -1
- package/dist/crypto/Signature.d.ts +3 -2
- package/dist/crypto/Signature.d.ts.map +1 -1
- package/dist/crypto/Signature.js +2 -2
- package/dist/crypto/Signature.js.map +1 -1
- package/dist/crypto/WebCrypto.d.ts +19 -0
- package/dist/crypto/WebCrypto.d.ts.map +1 -1
- package/dist/crypto/WebCrypto.js.map +1 -1
- package/dist/crypto/internal/asymmetric.js.map +1 -1
- package/dist/crypto/internal/key.js +3 -2
- package/dist/crypto/internal/key.js.map +1 -1
- package/dist/encoding/Base64Url.d.ts.map +1 -1
- package/dist/encoding/Base64Url.js +26 -3
- package/dist/encoding/Base64Url.js.map +1 -1
- package/dist/encoding/Json5.d.ts +44 -0
- package/dist/encoding/Json5.d.ts.map +1 -0
- package/dist/encoding/Json5.js +56 -0
- package/dist/encoding/Json5.js.map +1 -0
- package/dist/encoding/Jsonc.d.ts +47 -0
- package/dist/encoding/Jsonc.d.ts.map +1 -0
- package/dist/encoding/Jsonc.js +120 -0
- package/dist/encoding/Jsonc.js.map +1 -0
- package/dist/encoding/Ndjson.d.ts +5 -4
- package/dist/encoding/Ndjson.d.ts.map +1 -1
- package/dist/encoding/Ndjson.js +6 -5
- package/dist/encoding/Ndjson.js.map +1 -1
- package/dist/encoding/SchemaBinary.d.ts +94 -0
- package/dist/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/encoding/SchemaBinary.js +219 -6
- package/dist/encoding/SchemaBinary.js.map +1 -1
- package/dist/encoding/Sse.d.ts +3 -1
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +5 -3
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/encoding/index.d.ts +10 -0
- package/dist/encoding/index.d.ts.map +1 -1
- package/dist/encoding/index.js +10 -0
- package/dist/encoding/index.js.map +1 -1
- package/dist/eventlog/EventLog.d.ts +7 -0
- package/dist/eventlog/EventLog.d.ts.map +1 -1
- package/dist/eventlog/EventLog.js +7 -0
- package/dist/eventlog/EventLog.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts +7 -1
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +55 -24
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
- package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
- package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
- package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
- package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.js +22 -0
- package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
- package/dist/graphql/GraphQLClient.d.ts +3 -1
- package/dist/graphql/GraphQLClient.d.ts.map +1 -1
- package/dist/graphql/GraphQLClient.js.map +1 -1
- package/dist/graphql/GraphQLClientError.d.ts +14 -9
- package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
- package/dist/graphql/GraphQLClientError.js +14 -21
- package/dist/graphql/GraphQLClientError.js.map +1 -1
- package/dist/graphql/GraphQLProtocol.d.ts +12 -3
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
- package/dist/graphql/GraphQLProtocol.js +4 -1
- package/dist/graphql/GraphQLProtocol.js.map +1 -1
- package/dist/http/HttpClient.d.ts +204 -16
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +93 -13
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/http/HttpServerRequest.js +38 -22
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/internal/mimeTypes.d.ts +4 -1
- package/dist/http/internal/mimeTypes.d.ts.map +1 -1
- package/dist/http/internal/mimeTypes.js +4 -1
- package/dist/http/internal/mimeTypes.js.map +1 -1
- package/dist/http-api/HttpApiSchema.js +1 -1
- package/dist/http-api/HttpApiSchema.js.map +1 -1
- package/dist/internal/dateTime.js +66 -27
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +10 -0
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/glob.js +234 -56
- package/dist/internal/glob.js.map +1 -1
- package/dist/internal/graphql.js +21 -7
- package/dist/internal/graphql.js.map +1 -1
- package/dist/internal/graphqlHeaders.d.ts +7 -0
- package/dist/internal/graphqlHeaders.d.ts.map +1 -1
- package/dist/internal/graphqlHeaders.js +11 -0
- package/dist/internal/graphqlHeaders.js.map +1 -1
- package/dist/internal/jsonParser.d.ts +5 -0
- package/dist/internal/jsonParser.d.ts.map +1 -0
- package/dist/internal/jsonParser.js +280 -0
- package/dist/internal/jsonParser.js.map +1 -0
- package/dist/internal/persistence.js +8 -1
- package/dist/internal/persistence.js.map +1 -1
- package/dist/internal/request.js +29 -42
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/sql.js +16 -0
- package/dist/internal/sql.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/jose/JoseError.d.ts +285 -0
- package/dist/jose/JoseError.d.ts.map +1 -0
- package/dist/jose/JoseError.js +269 -0
- package/dist/jose/JoseError.js.map +1 -0
- package/dist/jose/Jwe.d.ts +128 -0
- package/dist/jose/Jwe.d.ts.map +1 -0
- package/dist/jose/Jwe.js +134 -0
- package/dist/jose/Jwe.js.map +1 -0
- package/dist/jose/Jwk.d.ts +597 -0
- package/dist/jose/Jwk.d.ts.map +1 -0
- package/dist/jose/Jwk.js +503 -0
- package/dist/jose/Jwk.js.map +1 -0
- package/dist/jose/Jwks.d.ts +236 -0
- package/dist/jose/Jwks.d.ts.map +1 -0
- package/dist/jose/Jwks.js +306 -0
- package/dist/jose/Jwks.js.map +1 -0
- package/dist/jose/Jws.d.ts +177 -0
- package/dist/jose/Jws.d.ts.map +1 -0
- package/dist/jose/Jws.js +183 -0
- package/dist/jose/Jws.js.map +1 -0
- package/dist/jose/Jwt.d.ts +270 -0
- package/dist/jose/Jwt.d.ts.map +1 -0
- package/dist/jose/Jwt.js +293 -0
- package/dist/jose/Jwt.js.map +1 -0
- package/dist/jose/index.d.ts +51 -0
- package/dist/jose/index.d.ts.map +1 -0
- package/dist/jose/index.js +52 -0
- package/dist/jose/index.js.map +1 -0
- package/dist/jose/internal/compact.d.ts +2 -0
- package/dist/jose/internal/compact.d.ts.map +1 -0
- package/dist/jose/internal/compact.js +179 -0
- package/dist/jose/internal/compact.js.map +1 -0
- package/dist/net/Host.d.ts.map +1 -1
- package/dist/net/Host.js +5 -10
- package/dist/net/Host.js.map +1 -1
- package/dist/observability/OtlpExporter.d.ts +4 -0
- package/dist/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/observability/OtlpExporter.js +40 -12
- package/dist/observability/OtlpExporter.js.map +1 -1
- package/dist/observability/OtlpSerialization.d.ts +1 -1
- package/dist/observability/OtlpSerialization.d.ts.map +1 -1
- package/dist/observability/OtlpSerialization.js +10 -4
- package/dist/observability/OtlpSerialization.js.map +1 -1
- package/dist/observability/internal/otlpProtobuf.js +3 -3
- package/dist/observability/internal/otlpProtobuf.js.map +1 -1
- package/dist/observability/internal/protobuf.js +6 -0
- package/dist/observability/internal/protobuf.js.map +1 -1
- package/dist/persistence/CircuitBreaker.d.ts +98 -38
- package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
- package/dist/persistence/CircuitBreaker.js +67 -29
- package/dist/persistence/CircuitBreaker.js.map +1 -1
- package/dist/persistence/RateLimiter.d.ts +15 -0
- package/dist/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/persistence/RateLimiter.js +76 -16
- package/dist/persistence/RateLimiter.js.map +1 -1
- package/dist/persistence/index.d.ts +1 -1
- package/dist/persistence/index.js +1 -1
- package/dist/reactivity/AtomRegistry.js +17 -7
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/Rpc.d.ts +1 -1
- package/dist/rpc/Rpc.d.ts.map +1 -1
- package/dist/rpc/Rpc.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +14 -2
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +38 -21
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcMessage.d.ts +3 -2
- package/dist/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/rpc/RpcMessage.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +10 -1
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/sql/Migrator.d.ts +9 -1
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +18 -6
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +46 -3
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +40 -15
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/Statement.d.ts +46 -3
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +32 -26
- package/dist/sql/Statement.js.map +1 -1
- package/dist/workflow/Workflow.d.ts +4 -0
- package/dist/workflow/Workflow.d.ts.map +1 -1
- package/dist/workflow/Workflow.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +5 -0
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +7 -3
- package/src/BigDecimal.ts +170 -16
- package/src/ByteSize.ts +29 -14
- package/src/Equal.ts +3 -3
- package/src/Hash.ts +5 -4
- package/src/Iterable.ts +28 -19
- package/src/JsonSchema.ts +45 -7
- package/src/MutableRef.ts +1 -8
- package/src/Path.ts +6 -1
- package/src/Pool.ts +2 -1
- package/src/Ref.ts +0 -8
- package/src/Schedule.ts +8 -6
- package/src/Schema.ts +3 -2
- package/src/SchemaRepresentation.ts +4 -2
- package/src/SchemaTransformation.ts +2 -2
- package/src/Stream.ts +6 -5
- package/src/ai/McpClient.ts +488 -91
- package/src/ai/McpSchema.ts +119 -0
- package/src/ai/McpServer.ts +302 -56
- package/src/ai/Prompt.ts +16 -11
- package/src/ai/Tool.ts +5 -10
- package/src/ai/internal/mcpCore.ts +165 -12
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +42 -9
- package/src/ai/internal/mcpProtocol.ts +15 -0
- package/src/ai/internal/mcpRuntime.ts +6 -3
- package/src/ai/internal/mcpSkills.ts +480 -0
- package/src/ai/internal/structured-output.ts +10 -4
- package/src/cluster/ClusterWorkflowEngine.ts +48 -33
- package/src/cluster/Entity.ts +52 -0
- package/src/cluster/MessageStorage.ts +3 -6
- package/src/cluster/Reply.ts +1 -1
- package/src/cluster/Runners.ts +16 -2
- package/src/cluster/Sharding.ts +17 -1
- package/src/cluster/internal/clusterAbandon.ts +7 -7
- package/src/cluster/internal/entityManager.ts +89 -15
- package/src/cluster/internal/transactionNotifications.ts +7 -0
- package/src/crypto/Aead.ts +3 -3
- package/src/crypto/Cipher.ts +3 -3
- package/src/crypto/KeyAgreement.ts +4 -4
- package/src/crypto/KeyFormat.ts +30 -1
- package/src/crypto/KeyWrap.ts +6 -6
- package/src/crypto/Mac.ts +3 -3
- package/src/crypto/Signature.ts +4 -4
- package/src/crypto/WebCrypto.ts +20 -0
- package/src/crypto/internal/asymmetric.ts +2 -1
- package/src/crypto/internal/key.ts +12 -5
- package/src/encoding/Base64Url.ts +27 -3
- package/src/encoding/Json5.ts +59 -0
- package/src/encoding/Jsonc.ts +126 -0
- package/src/encoding/Ndjson.ts +6 -5
- package/src/encoding/SchemaBinary.ts +228 -7
- package/src/encoding/Sse.ts +5 -3
- package/src/encoding/index.ts +12 -0
- package/src/eventlog/EventLog.ts +7 -0
- package/src/eventlog/EventLogServer.ts +67 -24
- package/src/eventlog/EventLogServerEncrypted.ts +5 -0
- package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
- package/src/eventlog/EventLogSessionAuth.ts +26 -0
- package/src/graphql/GraphQLClient.ts +3 -1
- package/src/graphql/GraphQLClientError.ts +15 -22
- package/src/graphql/GraphQLProtocol.ts +12 -3
- package/src/http/HttpClient.ts +244 -33
- package/src/http/HttpServerRequest.ts +61 -23
- package/src/http/internal/mimeTypes.ts +4 -1
- package/src/http-api/HttpApiSchema.ts +1 -1
- package/src/internal/dateTime.ts +80 -27
- package/src/internal/effect.ts +6 -0
- package/src/internal/glob.ts +279 -63
- package/src/internal/graphql.ts +27 -9
- package/src/internal/graphqlHeaders.ts +13 -0
- package/src/internal/jsonParser.ts +307 -0
- package/src/internal/persistence.ts +9 -1
- package/src/internal/request.ts +51 -58
- package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
- package/src/internal/sql.ts +25 -0
- package/src/internal/version.ts +1 -1
- package/src/jose/JoseError.ts +329 -0
- package/src/jose/Jwe.ts +196 -0
- package/src/jose/Jwk.ts +777 -0
- package/src/jose/Jwks.ts +418 -0
- package/src/jose/Jws.ts +312 -0
- package/src/jose/Jwt.ts +493 -0
- package/src/jose/index.ts +58 -0
- package/src/jose/internal/compact.ts +195 -0
- package/src/net/Host.ts +5 -10
- package/src/observability/OtlpExporter.ts +42 -18
- package/src/observability/OtlpSerialization.ts +13 -5
- package/src/observability/internal/otlpProtobuf.ts +6 -6
- package/src/observability/internal/protobuf.ts +10 -0
- package/src/persistence/CircuitBreaker.ts +113 -48
- package/src/persistence/RateLimiter.ts +97 -19
- package/src/persistence/index.ts +1 -1
- package/src/reactivity/AtomRegistry.ts +19 -8
- package/src/rpc/Rpc.ts +3 -1
- package/src/rpc/RpcClient.ts +44 -24
- package/src/rpc/RpcMessage.ts +3 -2
- package/src/rpc/RpcServer.ts +12 -1
- package/src/sql/Migrator.ts +35 -18
- package/src/sql/SqlClient.ts +91 -22
- package/src/sql/Statement.ts +60 -35
- package/src/workflow/Workflow.ts +4 -0
- package/src/workflow/WorkflowEngine.ts +5 -0
|
@@ -16,8 +16,11 @@ import * as Context from "../Context.ts"
|
|
|
16
16
|
import * as Duration from "../Duration.ts"
|
|
17
17
|
import * as Effect from "../Effect.ts"
|
|
18
18
|
import { flow, identity } from "../Function.ts"
|
|
19
|
+
import { escapeKeySegment } from "../internal/persistence.ts"
|
|
19
20
|
import * as Layer from "../Layer.ts"
|
|
21
|
+
import * as Schedule from "../Schedule.ts"
|
|
20
22
|
import * as Schema from "../Schema.ts"
|
|
23
|
+
import type * as Scope from "../Scope.ts"
|
|
21
24
|
import * as Redis from "./Redis.ts"
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -85,6 +88,9 @@ export const RateLimiter: Context.Service<RateLimiter, RateLimiter> = Context.Se
|
|
|
85
88
|
* The limiter supports fixed-window and token-bucket algorithms and either
|
|
86
89
|
* fails or returns a delay when a limit is exceeded.
|
|
87
90
|
*
|
|
91
|
+
* A nonpositive `limit` admits no positive token requests: fail mode rejects
|
|
92
|
+
* them and delay mode returns an infinite delay without touching the store.
|
|
93
|
+
*
|
|
88
94
|
* @stability unstable
|
|
89
95
|
* @category constructors
|
|
90
96
|
* @since 4.0.0
|
|
@@ -106,10 +112,9 @@ export const make: Effect.Effect<
|
|
|
106
112
|
const algorithm = options.algorithm ?? "fixed-window"
|
|
107
113
|
const window = Duration.max(Duration.fromInputUnsafe(options.window), Duration.millis(1))
|
|
108
114
|
const windowMillis = Duration.toMillis(window)
|
|
109
|
-
const
|
|
110
|
-
const refillRateMillis = Duration.toMillis(refillRate)
|
|
115
|
+
const exceeded = tokens > options.limit
|
|
111
116
|
|
|
112
|
-
if (onExceeded === "fail" &&
|
|
117
|
+
if (onExceeded === "fail" && exceeded) {
|
|
113
118
|
return Effect.fail(
|
|
114
119
|
new RateLimiterError({
|
|
115
120
|
reason: new RateLimitExceeded({
|
|
@@ -122,6 +127,19 @@ export const make: Effect.Effect<
|
|
|
122
127
|
)
|
|
123
128
|
}
|
|
124
129
|
|
|
130
|
+
// A nonpositive limit never refills, so skip the store and its infinite refill rate.
|
|
131
|
+
if (options.limit <= 0) {
|
|
132
|
+
return Effect.succeed<ConsumeResult>({
|
|
133
|
+
delay: exceeded ? Duration.infinity : Duration.zero,
|
|
134
|
+
limit: options.limit,
|
|
135
|
+
remaining: options.limit - tokens,
|
|
136
|
+
resetAfter: exceeded ? Duration.infinity : Duration.zero
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const refillRate = Duration.divideUnsafe(window, options.limit)
|
|
141
|
+
const refillRateMillis = Duration.toMillis(refillRate)
|
|
142
|
+
|
|
125
143
|
if (algorithm === "fixed-window") {
|
|
126
144
|
return Effect.flatMap(
|
|
127
145
|
store.fixedWindow({
|
|
@@ -706,18 +724,25 @@ interface AdaptiveState {
|
|
|
706
724
|
expiresAt: number
|
|
707
725
|
}
|
|
708
726
|
|
|
727
|
+
const memoryStoreSweepInterval = Duration.minutes(1)
|
|
728
|
+
|
|
709
729
|
/**
|
|
710
|
-
*
|
|
730
|
+
* Creates a process-local `RateLimiterStore`. Every entry expires once its
|
|
731
|
+
* state is indistinguishable from a missing key, and a background sweep tied to
|
|
732
|
+
* the scope removes expired entries, so an idle store drains to empty.
|
|
711
733
|
*
|
|
712
|
-
* @
|
|
713
|
-
* @category layers
|
|
714
|
-
* @since 4.0.0
|
|
734
|
+
* @internal
|
|
715
735
|
*/
|
|
716
|
-
export const
|
|
717
|
-
|
|
718
|
-
|
|
736
|
+
export const makeMemoryStore: Effect.Effect<
|
|
737
|
+
{
|
|
738
|
+
readonly store: RateLimiterStore["Service"]
|
|
739
|
+
readonly size: number
|
|
740
|
+
},
|
|
741
|
+
never,
|
|
742
|
+
Scope.Scope
|
|
743
|
+
> = Effect.gen(function*() {
|
|
719
744
|
const fixedCounters = new Map<string, { count: number; expiresAt: number }>()
|
|
720
|
-
const tokenBuckets = new Map<string, { tokens: number; lastRefill: number }>()
|
|
745
|
+
const tokenBuckets = new Map<string, { tokens: number; lastRefill: number; expiresAt: number }>()
|
|
721
746
|
const adaptiveStates = new Map<string, AdaptiveState>()
|
|
722
747
|
|
|
723
748
|
const getAdaptiveState = (key: string, now: number): AdaptiveState | undefined => {
|
|
@@ -738,7 +763,7 @@ export const layerStoreMemory: Layer.Layer<
|
|
|
738
763
|
const learnedExpiresAt = (now: number, learnedWindowMillis: number): number =>
|
|
739
764
|
now + learnedWindowMillis + adaptiveStateTtlGraceMillis
|
|
740
765
|
|
|
741
|
-
|
|
766
|
+
const store = RateLimiterStore.of({
|
|
742
767
|
fixedWindow: (options) =>
|
|
743
768
|
Effect.clockWith((clock) =>
|
|
744
769
|
Effect.sync(() => {
|
|
@@ -749,7 +774,7 @@ export const layerStoreMemory: Layer.Layer<
|
|
|
749
774
|
counter = { count: 0, expiresAt: now }
|
|
750
775
|
fixedCounters.set(options.key, counter)
|
|
751
776
|
}
|
|
752
|
-
if (options.limit && counter.count + options.tokens > options.limit) {
|
|
777
|
+
if (options.limit !== undefined && counter.count + options.tokens > options.limit) {
|
|
753
778
|
return [counter.count + options.tokens, counter.expiresAt - now] as const
|
|
754
779
|
}
|
|
755
780
|
counter.count += options.tokens
|
|
@@ -763,8 +788,8 @@ export const layerStoreMemory: Layer.Layer<
|
|
|
763
788
|
const refillRateMillis = Duration.toMillis(options.refillRate)
|
|
764
789
|
const now = clock.currentTimeMillisUnsafe()
|
|
765
790
|
let bucket = tokenBuckets.get(options.key)
|
|
766
|
-
if (!bucket) {
|
|
767
|
-
bucket = { tokens: options.limit, lastRefill: now }
|
|
791
|
+
if (!bucket || bucket.expiresAt <= now) {
|
|
792
|
+
bucket = { tokens: options.limit, lastRefill: now, expiresAt: now }
|
|
768
793
|
tokenBuckets.set(options.key, bucket)
|
|
769
794
|
}
|
|
770
795
|
const tokensToAdd = Math.floor((now - bucket.lastRefill) / refillRateMillis)
|
|
@@ -780,6 +805,8 @@ export const layerStoreMemory: Layer.Layer<
|
|
|
780
805
|
if (options.allowOverflow || newTokenCount >= 0) {
|
|
781
806
|
bucket.tokens = newTokenCount
|
|
782
807
|
}
|
|
808
|
+
// The bucket is indistinguishable from a fresh one once fully refilled
|
|
809
|
+
bucket.expiresAt = bucket.lastRefill + Math.ceil(options.limit - bucket.tokens) * refillRateMillis
|
|
783
810
|
return [newTokenCount, Math.max(0, now - bucket.lastRefill)] as const
|
|
784
811
|
})
|
|
785
812
|
),
|
|
@@ -924,12 +951,59 @@ export const layerStoreMemory: Layer.Layer<
|
|
|
924
951
|
})
|
|
925
952
|
)
|
|
926
953
|
})
|
|
954
|
+
|
|
955
|
+
const sweep = Effect.clockWith((clock) =>
|
|
956
|
+
Effect.sync(() => {
|
|
957
|
+
const now = clock.currentTimeMillisUnsafe()
|
|
958
|
+
for (const map of [fixedCounters, tokenBuckets, adaptiveStates]) {
|
|
959
|
+
for (const [key, entry] of map) {
|
|
960
|
+
if (entry.expiresAt <= now) {
|
|
961
|
+
map.delete(key)
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
})
|
|
966
|
+
)
|
|
967
|
+
yield* sweep.pipe(
|
|
968
|
+
Effect.repeat(Schedule.spaced(memoryStoreSweepInterval)),
|
|
969
|
+
Effect.forkScoped
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
return {
|
|
973
|
+
store,
|
|
974
|
+
get size() {
|
|
975
|
+
return fixedCounters.size + tokenBuckets.size + adaptiveStates.size
|
|
976
|
+
}
|
|
977
|
+
}
|
|
927
978
|
})
|
|
928
979
|
|
|
980
|
+
/**
|
|
981
|
+
* Provides a process-local in-memory `RateLimiterStore`.
|
|
982
|
+
*
|
|
983
|
+
* **Details**
|
|
984
|
+
*
|
|
985
|
+
* Entries expire once their state is indistinguishable from an unseen key, and
|
|
986
|
+
* a background sweep scoped to the layer removes expired entries periodically.
|
|
987
|
+
*
|
|
988
|
+
* @stability unstable
|
|
989
|
+
* @category layers
|
|
990
|
+
* @since 4.0.0
|
|
991
|
+
*/
|
|
992
|
+
export const layerStoreMemory: Layer.Layer<
|
|
993
|
+
RateLimiterStore
|
|
994
|
+
> = Layer.effect(RateLimiterStore)(Effect.map(makeMemoryStore, (memory) => memory.store))
|
|
995
|
+
|
|
929
996
|
/**
|
|
930
997
|
* Creates a Redis-backed `RateLimiterStore` using Lua scripts and the
|
|
931
998
|
* configured key prefix.
|
|
932
999
|
*
|
|
1000
|
+
* **Details**
|
|
1001
|
+
*
|
|
1002
|
+
* State for a rate-limit key is stored under `<prefix><key>`, with
|
|
1003
|
+
* `<prefix><key>:refill` holding token-bucket refill times and
|
|
1004
|
+
* `<prefix><key>:adaptive` holding adaptive state. `%` and `:` in the key are
|
|
1005
|
+
* percent-encoded so different keys never share Redis state.
|
|
1006
|
+
*
|
|
933
1007
|
* @stability unstable
|
|
934
1008
|
* @category constructors
|
|
935
1009
|
* @since 4.0.0
|
|
@@ -941,6 +1015,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
941
1015
|
) {
|
|
942
1016
|
const prefix = options?.prefix ?? "ratelimiter:"
|
|
943
1017
|
const redis = yield* Redis.Redis
|
|
1018
|
+
const redisKey = (key: string) => `${prefix}${escapeKeySegment(key)}`
|
|
944
1019
|
|
|
945
1020
|
const fixedWindow = redis.eval(fixedWindowScript)
|
|
946
1021
|
const tokenBucket = redis.eval(tokenBucketScript)
|
|
@@ -949,7 +1024,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
949
1024
|
|
|
950
1025
|
return RateLimiterStore.of({
|
|
951
1026
|
fixedWindow(options) {
|
|
952
|
-
const key =
|
|
1027
|
+
const key = redisKey(options.key)
|
|
953
1028
|
const refillMillis = Duration.toMillis(options.refillRate)
|
|
954
1029
|
return Effect.mapError(
|
|
955
1030
|
Effect.map(
|
|
@@ -966,7 +1041,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
966
1041
|
)
|
|
967
1042
|
},
|
|
968
1043
|
tokenBucket(options) {
|
|
969
|
-
const key =
|
|
1044
|
+
const key = redisKey(options.key)
|
|
970
1045
|
const lastRefillKey = `${key}:refill`
|
|
971
1046
|
const refillMillis = Duration.toMillis(options.refillRate)
|
|
972
1047
|
return Effect.clockWith((clock) =>
|
|
@@ -994,7 +1069,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
994
1069
|
)
|
|
995
1070
|
},
|
|
996
1071
|
adaptiveConsume(options) {
|
|
997
|
-
const key = `${
|
|
1072
|
+
const key = `${redisKey(options.key)}:adaptive`
|
|
998
1073
|
return Effect.map(
|
|
999
1074
|
Effect.mapError(
|
|
1000
1075
|
adaptiveConsume(
|
|
@@ -1021,7 +1096,7 @@ export const makeStoreRedis = Effect.fnUntraced(function*(
|
|
|
1021
1096
|
adaptiveFeedback(options) {
|
|
1022
1097
|
if (options.status !== 429 || options.retryAfter === undefined) return Effect.void
|
|
1023
1098
|
const retryAfterMillis = clampAdaptiveDurationMillis(Duration.toMillis(options.retryAfter))
|
|
1024
|
-
const key = `${
|
|
1099
|
+
const key = `${redisKey(options.key)}:adaptive`
|
|
1025
1100
|
return Effect.asVoid(
|
|
1026
1101
|
Effect.mapError(
|
|
1027
1102
|
adaptiveFeedback(
|
|
@@ -1057,6 +1132,9 @@ local limit = tonumber(ARGV[3])
|
|
|
1057
1132
|
local current = tonumber(redis.call("GET", key))
|
|
1058
1133
|
|
|
1059
1134
|
if not current then
|
|
1135
|
+
if limit and tokens > limit then
|
|
1136
|
+
return { string.format("%.17g", tokens), 0 }
|
|
1137
|
+
end
|
|
1060
1138
|
local nextpttl = math.max(1, math.ceil(refillms * tokens))
|
|
1061
1139
|
redis.call("SET", key, tokens, "PX", nextpttl)
|
|
1062
1140
|
return { string.format("%.17g", tokens), nextpttl }
|
package/src/persistence/index.ts
CHANGED
|
@@ -553,9 +553,15 @@ class RegistryImpl implements AtomRegistry {
|
|
|
553
553
|
const node = this.ensureNode(atom)
|
|
554
554
|
if (options?.immediate) {
|
|
555
555
|
f(node.value())
|
|
556
|
-
} else
|
|
557
|
-
//
|
|
558
|
-
|
|
556
|
+
} else {
|
|
557
|
+
// build the node so the listener hears its next change. A failed build
|
|
558
|
+
// is left for the next read to report; an error raised after the value
|
|
559
|
+
// is committed has no later read to report it, so it is rethrown
|
|
560
|
+
try {
|
|
561
|
+
node.value()
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if ((node.state & NodeFlags.waitingForValue) === 0) throw error
|
|
564
|
+
}
|
|
559
565
|
}
|
|
560
566
|
const remove = node.subscribe(function() {
|
|
561
567
|
f(node._value)
|
|
@@ -842,11 +848,15 @@ class NodeImpl<A> {
|
|
|
842
848
|
}
|
|
843
849
|
|
|
844
850
|
build(): void {
|
|
845
|
-
// a lifetime still attached to a node that needs a value belongs to a
|
|
846
|
-
// build that failed, or to one that is running and is now superseded
|
|
847
|
-
this.disposeLifetime()
|
|
848
|
-
this.invalidatedDuringBuild = false
|
|
849
851
|
const lifetime = makeLifetime(this)
|
|
852
|
+
// release a failed or superseded build; its finalizers run only once, so
|
|
853
|
+
// their failure is rethrown once this build commits
|
|
854
|
+
try {
|
|
855
|
+
this.disposeLifetime()
|
|
856
|
+
} catch (error) {
|
|
857
|
+
lifetime.failure = error
|
|
858
|
+
}
|
|
859
|
+
this.invalidatedDuringBuild = false
|
|
850
860
|
this.lifetime = lifetime
|
|
851
861
|
let value: A
|
|
852
862
|
let read = false
|
|
@@ -1126,7 +1136,8 @@ interface Lifetime<A> extends Atom.AtomContext {
|
|
|
1126
1136
|
readonly node: NodeImpl<A>
|
|
1127
1137
|
// the parents read so far by the build; `undefined` once the build has returned
|
|
1128
1138
|
reads: Set<NodeImpl<any>> | undefined
|
|
1129
|
-
//
|
|
1139
|
+
// rethrown once the build commits: a failure from releasing the previous
|
|
1140
|
+
// lifetime, or from reading a parent that still produced its value
|
|
1130
1141
|
failure: unknown
|
|
1131
1142
|
finalizers: Array<() => void> | undefined
|
|
1132
1143
|
disposed: boolean
|
package/src/rpc/Rpc.ts
CHANGED
|
@@ -363,7 +363,9 @@ export type SuccessExit<R> = Success<R> extends infer T ? T extends Stream<infer
|
|
|
363
363
|
* @category utility types
|
|
364
364
|
* @since 4.0.0
|
|
365
365
|
*/
|
|
366
|
-
export type SuccessChunk<R> =
|
|
366
|
+
export type SuccessChunk<R> = R extends Any ?
|
|
367
|
+
SuccessSchema<R> extends RpcSchema.Stream<infer _A, infer _E> ? _A["Type"] : never
|
|
368
|
+
: never
|
|
367
369
|
|
|
368
370
|
/**
|
|
369
371
|
* Extracts the RPC error schema, including error schemas contributed by
|
package/src/rpc/RpcClient.ts
CHANGED
|
@@ -113,7 +113,8 @@ export declare namespace RpcClient {
|
|
|
113
113
|
* handler's result and error are not returned, but transport errors,
|
|
114
114
|
* middleware errors and failures before the handler starts still fail the
|
|
115
115
|
* call. Over HTTP and workers the call completes after the handler has
|
|
116
|
-
* finished.
|
|
116
|
+
* finished. A protocol that only delivers requests acknowledges the call
|
|
117
|
+
* itself once it has delivered the request.
|
|
117
118
|
*/
|
|
118
119
|
readonly discard?: Discard | undefined
|
|
119
120
|
}
|
|
@@ -188,7 +189,8 @@ export declare namespace RpcClient {
|
|
|
188
189
|
* handler's result and error are not returned, but transport errors,
|
|
189
190
|
* middleware errors and failures before the handler starts still fail the
|
|
190
191
|
* call. Over HTTP and workers the call completes after the handler has
|
|
191
|
-
* finished.
|
|
192
|
+
* finished. A protocol that only delivers requests acknowledges the call
|
|
193
|
+
* itself once it has delivered the request.
|
|
192
194
|
*/
|
|
193
195
|
readonly discard?: Discard | undefined
|
|
194
196
|
}
|
|
@@ -418,19 +420,18 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
|
|
|
418
420
|
let fiber: Fiber.Fiber<any, any>
|
|
419
421
|
return Effect.onInterrupt(
|
|
420
422
|
Effect.callback<any, any>((resume) => {
|
|
421
|
-
|
|
422
|
-
let ack: Exit.Exit<any, any> | undefined
|
|
423
|
+
let acked = false
|
|
423
424
|
const entry: ClientEntry = {
|
|
424
425
|
_tag: "Effect",
|
|
425
426
|
rpc,
|
|
426
427
|
context,
|
|
427
428
|
resume(exit) {
|
|
428
|
-
if (discard) {
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
if (fiber?.pollUnsafe()) resume(
|
|
429
|
+
if (discard && exit._tag === "Success") {
|
|
430
|
+
// A successful acknowledgement waits for the send to finish, as
|
|
431
|
+
// interrupting the send could cancel the call: an HTTP handler
|
|
432
|
+
// is scoped to its request. Failures complete the call at once.
|
|
433
|
+
acked = true
|
|
434
|
+
if (fiber?.pollUnsafe()) resume(Exit.void)
|
|
434
435
|
return
|
|
435
436
|
}
|
|
436
437
|
resume(exit)
|
|
@@ -450,8 +451,8 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
|
|
|
450
451
|
if (exit._tag === "Failure") {
|
|
451
452
|
entries.delete(id)
|
|
452
453
|
return resume(exit)
|
|
453
|
-
} else if (
|
|
454
|
-
return resume(
|
|
454
|
+
} else if (acked) {
|
|
455
|
+
return resume(Exit.void)
|
|
455
456
|
}
|
|
456
457
|
})
|
|
457
458
|
}),
|
|
@@ -604,8 +605,10 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
|
|
|
604
605
|
if (!entry || entry._tag !== "Queue") return Effect.void
|
|
605
606
|
return Queue.offerAll(entry.queue, message.values).pipe(
|
|
606
607
|
supportsAck
|
|
607
|
-
? Effect.flatMap(() =>
|
|
608
|
-
|
|
608
|
+
? Effect.flatMap((dropped) =>
|
|
609
|
+
// values are dropped when the queue was shutdown by an interrupted
|
|
610
|
+
// consumer, so the server should not be asked for more
|
|
611
|
+
dropped.length > 0 ? Effect.void : options.onFromClient({
|
|
609
612
|
message: { _tag: "Ack", requestId: message.requestId },
|
|
610
613
|
context: entry.context
|
|
611
614
|
})
|
|
@@ -831,8 +834,10 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
|
|
|
831
834
|
}
|
|
832
835
|
case "ClientProtocolError": {
|
|
833
836
|
const exit = Exit.fail(message.error)
|
|
834
|
-
const requestIds =
|
|
835
|
-
|
|
837
|
+
const requestIds = message.requestIds === undefined
|
|
838
|
+
? Array.from(entries.keys())
|
|
839
|
+
: message.requestIds.map(RequestId).filter((requestId) => entries.has(requestId))
|
|
840
|
+
for (const requestId of requestIds) entries.delete(requestId)
|
|
836
841
|
return Effect.forEach(
|
|
837
842
|
requestIds,
|
|
838
843
|
(requestId) => write({ _tag: "Exit", clientId: 0, requestId, exit: exit as any })
|
|
@@ -953,6 +958,16 @@ export const withHeaders: {
|
|
|
953
958
|
* Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
|
|
954
959
|
* workers, sockets, or custom protocols.
|
|
955
960
|
*
|
|
961
|
+
* **Details**
|
|
962
|
+
*
|
|
963
|
+
* A `Request` that `send` accepts stays pending until a reply names its id: an
|
|
964
|
+
* `Exit`, or a `ClientProtocolError` that lists it in `requestIds`. Until then
|
|
965
|
+
* the client keeps the request and the caller's context, unless the caller is
|
|
966
|
+
* interrupted, the client closes or the connection fails. A transport that
|
|
967
|
+
* only delivers requests, such as a message queue, writes a void `Exit` for a
|
|
968
|
+
* `discard` request once the request is delivered, and fails `send` with an
|
|
969
|
+
* `RpcClientError` for any other request.
|
|
970
|
+
*
|
|
956
971
|
* @stability unstable
|
|
957
972
|
* @category services
|
|
958
973
|
* @since 4.0.0
|
|
@@ -1599,21 +1614,26 @@ export const makeProtocolWorker = (
|
|
|
1599
1614
|
undefined
|
|
1600
1615
|
}).pipe(
|
|
1601
1616
|
Effect.tapCause((cause) => {
|
|
1617
|
+
const requestIdsByClient = new Map<number, Array<string | number>>()
|
|
1602
1618
|
for (const [requestId, entry] of entries) {
|
|
1603
1619
|
if (entry.worker !== backing) continue
|
|
1604
1620
|
entries.delete(requestId)
|
|
1605
1621
|
entry.latch.openUnsafe()
|
|
1622
|
+
const requestIds = requestIdsByClient.get(entry.clientId)
|
|
1623
|
+
if (requestIds) requestIds.push(requestId)
|
|
1624
|
+
else requestIdsByClient.set(entry.clientId, [requestId])
|
|
1606
1625
|
}
|
|
1607
|
-
const
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
message: "Error in worker",
|
|
1613
|
-
cause: Cause.squash(cause)
|
|
1614
|
-
})
|
|
1626
|
+
const found = Cause.findError(cause)
|
|
1627
|
+
const error = new RpcClientError({
|
|
1628
|
+
reason: Result.isSuccess(found) ? found.success.reason : new RpcClientDefect({
|
|
1629
|
+
message: "Error in worker",
|
|
1630
|
+
cause: Cause.squash(cause)
|
|
1615
1631
|
})
|
|
1616
1632
|
})
|
|
1633
|
+
return Effect.forEach(
|
|
1634
|
+
requestIdsByClient,
|
|
1635
|
+
([clientId, requestIds]) => writeResponse(clientId, { _tag: "ClientProtocolError", error, requestIds })
|
|
1636
|
+
)
|
|
1617
1637
|
}),
|
|
1618
1638
|
Effect.retry(Schedule.spaced(1000)),
|
|
1619
1639
|
Effect.annotateLogs({
|
package/src/rpc/RpcMessage.ts
CHANGED
|
@@ -325,8 +325,8 @@ export interface ResponseExitEncoded {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
/**
|
|
328
|
-
* A server-to-client protocol message
|
|
329
|
-
*
|
|
328
|
+
* A server-to-client protocol message failing in-flight requests with a client
|
|
329
|
+
* protocol error: those in `requestIds`, or all of them when it is omitted.
|
|
330
330
|
*
|
|
331
331
|
* @stability unstable
|
|
332
332
|
* @category models
|
|
@@ -335,6 +335,7 @@ export interface ResponseExitEncoded {
|
|
|
335
335
|
export interface ClientProtocolError {
|
|
336
336
|
readonly _tag: "ClientProtocolError"
|
|
337
337
|
readonly error: RpcClientError
|
|
338
|
+
readonly requestIds?: ReadonlyArray<string | number>
|
|
338
339
|
}
|
|
339
340
|
|
|
340
341
|
/**
|
package/src/rpc/RpcServer.ts
CHANGED
|
@@ -902,6 +902,17 @@ export const make: <Rpcs extends Rpc.Any>(
|
|
|
902
902
|
request: RequestEncoded,
|
|
903
903
|
payload: unknown
|
|
904
904
|
) => {
|
|
905
|
+
// the headers come from the wire unvalidated, and malformed input makes
|
|
906
|
+
// `Headers.fromInput` throw
|
|
907
|
+
let headers: Headers.Headers
|
|
908
|
+
try {
|
|
909
|
+
headers = Headers.fromInput(request.headers)
|
|
910
|
+
} catch {
|
|
911
|
+
const reason = "Invalid request headers"
|
|
912
|
+
return request.isNotification === true
|
|
913
|
+
? dropNotification(reason)
|
|
914
|
+
: sendRequestDefect(client, requestId, schemas.encodeExit, reason)
|
|
915
|
+
}
|
|
905
916
|
// nothing is encoded for a notification, and only a reply would remove its schemas
|
|
906
917
|
if (request.isNotification !== true) {
|
|
907
918
|
client.schemas.set(
|
|
@@ -919,7 +930,7 @@ export const make: <Rpcs extends Rpc.Any>(
|
|
|
919
930
|
const decoded = request as Types.Mutable<RequestEncoded>
|
|
920
931
|
decoded.id = requestId
|
|
921
932
|
decoded.payload = payload
|
|
922
|
-
decoded.headers =
|
|
933
|
+
decoded.headers = headers as any
|
|
923
934
|
return server.write(client.id, decoded as any)
|
|
924
935
|
}
|
|
925
936
|
|
package/src/sql/Migrator.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as Arr from "../Array.ts"
|
|
13
13
|
import * as Data from "../Data.ts"
|
|
14
14
|
import * as Effect from "../Effect.ts"
|
|
15
|
-
import { pipe } from "../Function.ts"
|
|
15
|
+
import { identity, pipe } from "../Function.ts"
|
|
16
16
|
import * as Option from "../Option.ts"
|
|
17
17
|
import * as Order from "../Order.ts"
|
|
18
18
|
import * as Client from "./SqlClient.ts"
|
|
@@ -99,13 +99,22 @@ export class MigrationError extends Data.TaggedError("MigrationError")<{
|
|
|
99
99
|
* migrations. ClickHouse runs without a transaction and records each migration
|
|
100
100
|
* only after it succeeds.
|
|
101
101
|
*
|
|
102
|
+
* **Details**
|
|
103
|
+
*
|
|
104
|
+
* `transformWrites` transforms only the effects that create the migrations
|
|
105
|
+
* table and insert into it, for clients such as ClickHouse that run row-less
|
|
106
|
+
* statements differently from queries. Reads of the migrations table and the
|
|
107
|
+
* migrations themselves are not transformed. It defaults to `identity`.
|
|
108
|
+
*
|
|
102
109
|
* @stability unstable
|
|
103
110
|
* @category constructors
|
|
104
111
|
* @since 4.0.0
|
|
105
112
|
*/
|
|
106
113
|
export const make = <RD = never>({
|
|
114
|
+
transformWrites = identity,
|
|
107
115
|
dumpSchema = () => Effect.void
|
|
108
116
|
}: {
|
|
117
|
+
transformWrites?: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
109
118
|
dumpSchema?: (
|
|
110
119
|
path: string,
|
|
111
120
|
migrationsTable: string
|
|
@@ -123,7 +132,7 @@ export const make = <RD = never>({
|
|
|
123
132
|
Effect.gen(function*() {
|
|
124
133
|
const sql = yield* Client.SqlClient
|
|
125
134
|
|
|
126
|
-
const
|
|
135
|
+
const createMigrationsTable = transformWrites(sql.onDialectOrElse({
|
|
127
136
|
mssql: () =>
|
|
128
137
|
sql`IF OBJECT_ID(N'${sql.literal(table)}', N'U') IS NULL
|
|
129
138
|
CREATE TABLE ${sql(table)} (
|
|
@@ -147,14 +156,25 @@ export const make = <RD = never>({
|
|
|
147
156
|
) ENGINE = MergeTree
|
|
148
157
|
ORDER BY migration_id`,
|
|
149
158
|
pg: () =>
|
|
150
|
-
|
|
151
|
-
migrationsTableExists(sql, table),
|
|
152
|
-
(exists) =>
|
|
153
|
-
exists ? Effect.void : Effect.asVoid(sql`CREATE TABLE ${sql(table)} (
|
|
159
|
+
sql`CREATE TABLE ${sql(table)} (
|
|
154
160
|
migration_id integer primary key,
|
|
155
161
|
created_at timestamp with time zone not null default now(),
|
|
156
162
|
name text not null
|
|
157
|
-
)
|
|
163
|
+
)`,
|
|
164
|
+
orElse: () =>
|
|
165
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql(table)} (
|
|
166
|
+
migration_id integer PRIMARY KEY NOT NULL,
|
|
167
|
+
created_at datetime NOT NULL DEFAULT current_timestamp,
|
|
168
|
+
name VARCHAR(255) NOT NULL
|
|
169
|
+
)`
|
|
170
|
+
}))
|
|
171
|
+
|
|
172
|
+
const ensureMigrationsTable = sql.onDialectOrElse({
|
|
173
|
+
pg: () =>
|
|
174
|
+
Effect.flatMap(
|
|
175
|
+
migrationsTableExists(sql, table),
|
|
176
|
+
(exists) =>
|
|
177
|
+
exists ? Effect.void : Effect.asVoid(createMigrationsTable).pipe(
|
|
158
178
|
Effect.catch((error) =>
|
|
159
179
|
Effect.flatMap(
|
|
160
180
|
migrationsTableExists(sql, table),
|
|
@@ -163,22 +183,19 @@ ORDER BY migration_id`,
|
|
|
163
183
|
)
|
|
164
184
|
)
|
|
165
185
|
),
|
|
166
|
-
orElse: () =>
|
|
167
|
-
sql`CREATE TABLE IF NOT EXISTS ${sql(table)} (
|
|
168
|
-
migration_id integer PRIMARY KEY NOT NULL,
|
|
169
|
-
created_at datetime NOT NULL DEFAULT current_timestamp,
|
|
170
|
-
name VARCHAR(255) NOT NULL
|
|
171
|
-
)`
|
|
186
|
+
orElse: () => createMigrationsTable
|
|
172
187
|
})
|
|
173
188
|
|
|
174
189
|
const insertMigrations = (
|
|
175
190
|
rows: Arr.NonEmptyReadonlyArray<[id: number, name: string]>
|
|
176
191
|
) =>
|
|
177
|
-
|
|
178
|
-
sql
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
192
|
+
transformWrites(
|
|
193
|
+
sql`INSERT INTO ${sql(table)} ${
|
|
194
|
+
sql.insert(
|
|
195
|
+
Arr.map(rows, ([migration_id, name]) => ({ migration_id, name }))
|
|
196
|
+
)
|
|
197
|
+
}`.withoutTransform
|
|
198
|
+
)
|
|
182
199
|
|
|
183
200
|
const latestMigration = Effect.map(
|
|
184
201
|
sql<{ migration_id: number; name: string; created_at: Date }>`SELECT migration_id, name, created_at FROM ${
|