| author | |
| committer | |
| log | a143fa3c56be890346b8a7915a6a870529201dda |
| tree | d503bc2686e23a1b1e8f4656549c69a659ea06d3 |
| parent | df26ef99859ac757409f5332b185a89cc7f40267 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>3 files changed, 50 insertions(+), 0 deletions(-)
packages/client-runtime/src/connection/errors.test.ts+33| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import { EnvironmentAuthInvalidError } from "@t3tools/contracts"; | 1 | import { EnvironmentAuthInvalidError } from "@t3tools/contracts"; |
| 2 | import { | 2 | import { |
| 3 | RelayAuthInvalidError, | 3 | RelayAuthInvalidError, |
| 4 | RelayEnvironmentConnectNotAuthorizedError, | ||
| 4 | RelayEnvironmentEndpointTimedOutError, | 5 | RelayEnvironmentEndpointTimedOutError, |
| 5 | } from "@t3tools/contracts/relay"; | 6 | } from "@t3tools/contracts/relay"; |
| 6 | import { describe, expect, it } from "@effect/vitest"; | 7 | import { describe, expect, it } from "@effect/vitest"; |
| ... | @@ -35,6 +36,38 @@ describe("mapManagedRelayError", () => { | ... | @@ -35,6 +36,38 @@ describe("mapManagedRelayError", () => { |
| 35 | }); | 36 | }); |
| 36 | }); | 37 | }); |
| 37 | 38 | ||
| 39 | it("retries a tunnel that is still being set up, and blocks other refusals", () => { | ||
| 40 | const mapConnectRefusal = (reason: RelayEnvironmentConnectNotAuthorizedError["reason"]) => { | ||
| 41 | const relayError = new RelayEnvironmentConnectNotAuthorizedError({ | ||
| 42 | code: "environment_connect_not_authorized", | ||
| 43 | reason, | ||
| 44 | traceId: "trace-connect-refused", | ||
| 45 | }); | ||
| 46 | return mapManagedRelayError( | ||
| 47 | new ManagedRelayRequestFailedError({ | ||
| 48 | action: "connect relay environment", | ||
| 49 | cause: relayError, | ||
| 50 | relayError, | ||
| 51 | }), | ||
| 52 | ); | ||
| 53 | }; | ||
| 54 | |||
| 55 | expect(mapConnectRefusal("managed_endpoint_allocation_not_ready")).toMatchObject({ | ||
| 56 | _tag: "ConnectionTransientError", | ||
| 57 | reason: "endpoint-unavailable", | ||
| 58 | detail: "The environment's T3 Connect tunnel is still being set up.", | ||
| 59 | traceId: "trace-connect-refused", | ||
| 60 | }); | ||
| 61 | expect(mapConnectRefusal("endpoint_provider_not_managed")).toMatchObject({ | ||
| 62 | _tag: "ConnectionBlockedError", | ||
| 63 | reason: "permission", | ||
| 64 | }); | ||
| 65 | expect(mapConnectRefusal(undefined)).toMatchObject({ | ||
| 66 | _tag: "ConnectionBlockedError", | ||
| 67 | reason: "permission", | ||
| 68 | }); | ||
| 69 | }); | ||
| 70 | |||
| 38 | it("presents clock skew as one possible cause for a generic DPoP error", () => { | 71 | it("presents clock skew as one possible cause for a generic DPoP error", () => { |
| 39 | const mapped = mapManagedRelayError( | 72 | const mapped = mapManagedRelayError( |
| 40 | new ManagedRelayRequestFailedError({ | 73 | new ManagedRelayRequestFailedError({ |
packages/client-runtime/src/connection/errors.ts+14| ... | @@ -46,6 +46,20 @@ function relayProtectedError(error: RelayProtectedError): ConnectionAttemptError | ... | @@ -46,6 +46,20 @@ function relayProtectedError(error: RelayProtectedError): ConnectionAttemptError |
| 46 | traceId: error.traceId, | 46 | traceId: error.traceId, |
| 47 | }); | 47 | }); |
| 48 | case "RelayEnvironmentConnectNotAuthorizedError": | 48 | case "RelayEnvironmentConnectNotAuthorizedError": |
| 49 | // A host swapping in a new tunnel leaves its allocation unready until | ||
| 50 | // the tunnel is up, and nothing on the client changes when it is. | ||
| 51 | if (error.reason === "managed_endpoint_allocation_not_ready") { | ||
| 52 | return new ConnectionTransientError({ | ||
| 53 | reason: "endpoint-unavailable", | ||
| 54 | detail: relayProtectedErrorMessage(error), | ||
| 55 | traceId: error.traceId, | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | return new ConnectionBlockedError({ | ||
| 59 | reason: "permission", | ||
| 60 | detail: relayProtectedErrorMessage(error), | ||
| 61 | traceId: error.traceId, | ||
| 62 | }); | ||
| 49 | case "RelayEnvironmentLinkProofInvalidError": | 63 | case "RelayEnvironmentLinkProofInvalidError": |
| 50 | case "RelayEnvironmentLinkLimitExceededError": | 64 | case "RelayEnvironmentLinkLimitExceededError": |
| 51 | return new ConnectionBlockedError({ | 65 | return new ConnectionBlockedError({ |
packages/client-runtime/src/relay/errorPresentation.ts+3| ... | @@ -43,6 +43,9 @@ export function relayProtectedErrorMessage(error: RelayProtectedError): string { | ... | @@ -43,6 +43,9 @@ export function relayProtectedErrorMessage(error: RelayProtectedError): string { |
| 43 | if (error.reason === "environment_link_not_found") { | 43 | if (error.reason === "environment_link_not_found") { |
| 44 | return "Relay has no active link for this environment. The environment server may not have re-established its link yet."; | 44 | return "Relay has no active link for this environment. The environment server may not have re-established its link yet."; |
| 45 | } | 45 | } |
| 46 | if (error.reason === "managed_endpoint_allocation_not_ready") { | ||
| 47 | return "The environment's T3 Connect tunnel is still being set up."; | ||
| 48 | } | ||
| 46 | return error.reason | 49 | return error.reason |
| 47 | ? `Relay rejected the environment connection request (${error.reason}).` | 50 | ? `Relay rejected the environment connection request (${error.reason}).` |
| 48 | : "Relay rejected the environment connection request."; | 51 | : "Relay rejected the environment connection request."; |