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 @@...@@ -1,4 +1,5 @@
1import { describe, expect, it } from "vite-plus/test";1import { describe, expect, it } from "vite-plus/test";
2import * as Schema from "effect/Schema";
23
3import {4import {
4 managedEndpointDigestInput,5 managedEndpointDigestInput,
...@@ -7,11 +8,14 @@ import {...@@ -7,11 +8,14 @@ import {
7 isManagedEndpointHostname,8 isManagedEndpointHostname,
8 managedEndpointTunnelName,9 managedEndpointTunnelName,
9 relayOwnsManagedEndpointZone,10 relayOwnsManagedEndpointZone,
11 RelayPublicDomainLabelTooLongError,
10 relayPublicDomainForStage,12 relayPublicDomainForStage,
11 relayResourceNameForStage,13 relayResourceNameForStage,
12 relayStageSlug,14 relayStageSlug,
13} from "./deploymentConfig.ts";15} from "./deploymentConfig.ts";
1416
17const isRelayPublicDomainLabelTooLongError = Schema.is(RelayPublicDomainLabelTooLongError);
18
15describe("relayStageSlug", () => {19describe("relayStageSlug", () => {
16 it("matches Alchemy physical-name sanitization for default developer stages", () => {20 it("matches Alchemy physical-name sanitization for default developer stages", () => {
17 expect(relayStageSlug("dev_julius")).toBe("dev-julius");21 expect(relayStageSlug("dev_julius")).toBe("dev-julius");
...@@ -28,6 +32,29 @@ describe("relayPublicDomainForStage", () => {...@@ -28,6 +32,29 @@ describe("relayPublicDomainForStage", () => {
28 "relay-dev-julius.example.com",32 "relay-dev-julius.example.com",
29 );33 );
30 });34 });
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 });
31});58});
3259
33describe("relayOwnsManagedEndpointZone", () => {60describe("relayOwnsManagedEndpointZone", () => {
infra/relay/src/deploymentConfig.ts+19-1
...@@ -1,10 +1,24 @@...@@ -1,10 +1,24 @@
1import type { RelayManagedEndpoint } from "@t3tools/contracts/relay";1import type { RelayManagedEndpoint } from "@t3tools/contracts/relay";
2import * as Schema from "effect/Schema";
23
3const DNS_LABEL_MAX_LENGTH = 63;4const DNS_LABEL_MAX_LENGTH = 63;
4const MANAGED_ENDPOINT_HASH_LENGTH = 16;5const MANAGED_ENDPOINT_HASH_LENGTH = 16;
5const MANAGED_ENDPOINT_TUNNEL_PREFIX = "t3coderelay-managedendpoint";6const MANAGED_ENDPOINT_TUNNEL_PREFIX = "t3coderelay-managedendpoint";
6export const MANAGED_ENDPOINT_ZONE_OWNER_STAGE = "prod";7export 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
8function normalizeZoneName(zoneName: string): string {22function normalizeZoneName(zoneName: string): string {
9 return zoneName23 return zoneName
10 .trim()24 .trim()
...@@ -62,7 +76,11 @@ export function relayPublicDomainForStage(stage: string, zoneName: string): stri...@@ -62,7 +76,11 @@ export function relayPublicDomainForStage(stage: string, zoneName: string): stri
62 const stageSlug = relayStageSlug(stage);76 const stageSlug = relayStageSlug(stage);
63 const relayLabel = stage === "prod" ? "relay" : `relay-${stageSlug}`;77 const relayLabel = stage === "prod" ? "relay" : `relay-${stageSlug}`;
64 if (relayLabel.length > DNS_LABEL_MAX_LENGTH) {78 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 });
66 }84 }
67 return `${relayLabel}.${normalizeZoneName(zoneName)}`;85 return `${relayLabel}.${normalizeZoneName(zoneName)}`;
68}86}