authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 11:00:42-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 11:00:42-07:00
logcc69aef4dd140703762282ea207e0bcae7f9d9a1
tree2b1958308586e1fc2487ab9fe29257eb7f1328c2
parent71608142c27136a5ba0551fe9ed52ceeaad22ee4
signature Signed by PGP key B5690EEEBB952194

[codex] Structure relay domain label errors (#3347)

Co-authored-by: codex <codex@users.noreply.github.com>

2 files changed, 46 insertions(+), 1 deletions(-)

infra/relay/src/deploymentConfig.test.ts+27
......@@ -1,4 +1,5 @@
11import { describe, expect, it } from "vite-plus/test";
2import * as Schema from "effect/Schema";
23
34import {
45 managedEndpointDigestInput,
......@@ -7,11 +8,14 @@ import {
78 isManagedEndpointHostname,
89 managedEndpointTunnelName,
910 relayOwnsManagedEndpointZone,
11 RelayPublicDomainLabelTooLongError,
1012 relayPublicDomainForStage,
1113 relayResourceNameForStage,
1214 relayStageSlug,
1315} from "./deploymentConfig.ts";
1416
17const isRelayPublicDomainLabelTooLongError = Schema.is(RelayPublicDomainLabelTooLongError);
18
1519describe("relayStageSlug", () => {
1620 it("matches Alchemy physical-name sanitization for default developer stages", () => {
1721 expect(relayStageSlug("dev_julius")).toBe("dev-julius");
......@@ -28,6 +32,29 @@ describe("relayPublicDomainForStage", () => {
2832 "relay-dev-julius.example.com",
2933 );
3034 });
35
36 it("reports the stage and derived DNS label when the label is too long", () => {
37 const stage = `dev_${"x".repeat(60)}`;
38 let error: unknown;
39
40 try {
41 relayPublicDomainForStage(stage, "example.com");
42 } catch (cause) {
43 error = cause;
44 }
45
46 if (!isRelayPublicDomainLabelTooLongError(error)) {
47 throw error;
48 }
49 expect(error).toMatchObject({
50 stage,
51 label: `relay-dev-${"x".repeat(60)}`,
52 maxLength: 63,
53 });
54 expect(error.message).toBe(
55 `Relay stage '${stage}' produces custom domain label 'relay-dev-${"x".repeat(60)}' (70 characters), exceeding the DNS label limit of 63.`,
56 );
57 });
3158});
3259
3360describe("relayOwnsManagedEndpointZone", () => {
infra/relay/src/deploymentConfig.ts+19-1
......@@ -1,10 +1,24 @@
11import type { RelayManagedEndpoint } from "@t3tools/contracts/relay";
2import * as Schema from "effect/Schema";
23
34const DNS_LABEL_MAX_LENGTH = 63;
45const MANAGED_ENDPOINT_HASH_LENGTH = 16;
56const MANAGED_ENDPOINT_TUNNEL_PREFIX = "t3coderelay-managedendpoint";
67export const MANAGED_ENDPOINT_ZONE_OWNER_STAGE = "prod";
78
9export class RelayPublicDomainLabelTooLongError extends Schema.TaggedErrorClass<RelayPublicDomainLabelTooLongError>()(
10 "RelayPublicDomainLabelTooLongError",
11 {
12 stage: Schema.String,
13 label: Schema.String,
14 maxLength: Schema.Number,
15 },
16) {
17 override get message(): string {
18 return `Relay stage '${this.stage}' produces custom domain label '${this.label}' (${this.label.length} characters), exceeding the DNS label limit of ${this.maxLength}.`;
19 }
20}
21
822function normalizeZoneName(zoneName: string): string {
923 return zoneName
1024 .trim()
......@@ -62,7 +76,11 @@ export function relayPublicDomainForStage(stage: string, zoneName: string): stri
6276 const stageSlug = relayStageSlug(stage);
6377 const relayLabel = stage === "prod" ? "relay" : `relay-${stageSlug}`;
6478 if (relayLabel.length > DNS_LABEL_MAX_LENGTH) {
65 throw new Error(`Relay stage is too long for a custom domain: ${stage}`);
79 throw new RelayPublicDomainLabelTooLongError({
80 stage,
81 label: relayLabel,
82 maxLength: DNS_LABEL_MAX_LENGTH,
83 });
6684 }
6785 return `${relayLabel}.${normalizeZoneName(zoneName)}`;
6886}