Skip to content
Source
Sign inSign up

publicConfig.ts

117 lines3.6 KB

1import {
2 clerkFrontendApiHostnameFromPublishableKey,
3 relayClerkTokenOptions,
4} from "@t3tools/shared/relayAuth";
5import { normalizeSecureRelayUrl } from "@t3tools/shared/relayUrl";
6import * as Schema from "effect/Schema";
7​
8export class CloudPublicConfigMissingError extends Schema.TaggedError<CloudPublicConfigMissingError>()(
9 "CloudPublicConfigMissingError",
10 {
11 key: Schema.Literal("T3CODE_CLERK_JWT_TEMPLATE"),
12 },
13) {
14 override get message(): string {
15 return `${this.key} is not configured.`;
16 }
18​
19export interface CloudPublicConfig {
20 readonly clerkPublishableKey: string | null;
21 readonly clerkJwtTemplate: string | null;
22 readonly relayUrl: string | null;
23 readonly relayTracing: {
24 readonly tracesUrl: string | null;
25 readonly tracesDataset: string | null;
26 readonly tracesToken: string | null;
27 };
29​
30export function trimNonEmpty(value: string | undefined): string | null {
31 return value?.trim() || null;
33​
34function normalizeSecureUrl(value: string): string | null {
35 try {
36 const url = new URL(value);
37 return url.protocol === "https:" ? url.toString() : null;
38 } catch {
39 return null;
40 }
42​
43export function resolveCloudPublicConfig(): CloudPublicConfig {
44 return {
45 clerkPublishableKey: trimNonEmpty(
46 import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string | undefined,
47 ),
48 clerkJwtTemplate: trimNonEmpty(import.meta.env.VITE_CLERK_JWT_TEMPLATE as string | undefined),
49 relayUrl: normalizeSecureRelayUrl(
50 (import.meta.env.VITE_T3CODE_RELAY_URL as string | undefined) ?? "",
51 ),
52 relayTracing: {
53 tracesUrl: normalizeSecureUrl(
54 (import.meta.env.VITE_RELAY_OTLP_TRACES_URL as string | undefined) ?? "",
55 ),
56 tracesDataset: trimNonEmpty(
57 import.meta.env.VITE_RELAY_OTLP_TRACES_DATASET as string | undefined,
58 ),
59 tracesToken: trimNonEmpty(import.meta.env.VITE_RELAY_OTLP_TRACES_TOKEN as string | undefined),
60 },
61 };
63​
64export function resolveRelayTracingConfig() {
65 // The relay exporter is created during module initialization, so this must be synchronous.
66 if (typeof window !== "undefined" && window.desktopBridge?.getRelayTelemetryEnabled() === false) {
67 return null;
68 }
69 const { relayTracing } = resolveCloudPublicConfig();
70 return relayTracing.tracesUrl && relayTracing.tracesDataset && relayTracing.tracesToken
71 ? {
72 tracesUrl: relayTracing.tracesUrl,
73 tracesDataset: relayTracing.tracesDataset,
74 tracesToken: relayTracing.tracesToken,
75 }
76 : null;
78​
79export function hasCloudPublicConfig(): boolean {
80 const config = resolveCloudPublicConfig();
81 if (!config.clerkPublishableKey || !config.clerkJwtTemplate || !config.relayUrl) {
82 return false;
83 }
84​
85 // Electron has its own auth runtime; development keys also support private browser origins.
86 if (
87 typeof window === "undefined" ||
88 window.desktopBridge !== undefined ||
89 !config.clerkPublishableKey.startsWith("pk_live_")
90 ) {
91 return true;
92 }
93​
94 try {
95 // Custom Frontend API hosts use clerk.<production domain>.
96 const domain = clerkFrontendApiHostnameFromPublishableKey(config.clerkPublishableKey).replace(
97 /^clerk\./u,
98 "",
99 );
100 const { hostname, protocol, port } = window.location;
101 return (
102 protocol === "https:" &&
103 port === "" &&
104 (hostname === domain || hostname.endsWith(`.${domain}`))
105 );
106 } catch {
107 return false;
108 }
110​
111export function resolveRelayClerkTokenOptions() {
112 const { clerkJwtTemplate } = resolveCloudPublicConfig();
113 if (!clerkJwtTemplate) {
114 throw new CloudPublicConfigMissingError({ key: "T3CODE_CLERK_JWT_TEMPLATE" });
115 }
116 return relayClerkTokenOptions(clerkJwtTemplate);