Open History to see the last commit
1import type {
2 AdvertisedEndpoint,
3 AdvertisedEndpointHostedHttpsCompatibility,
4 AdvertisedEndpointProvider,
5 AdvertisedEndpointReachability,
6 AdvertisedEndpointSource,
7 AdvertisedEndpointStatus,
8} from "@t3tools/contracts";
9
10export interface CreateAdvertisedEndpointInput {
11 readonly id: string;
12 readonly label: string;
13 readonly provider: AdvertisedEndpointProvider;
14 readonly httpBaseUrl: string;
15 readonly reachability: AdvertisedEndpointReachability;
16 readonly hostedHttpsCompatibility?: AdvertisedEndpointHostedHttpsCompatibility;
17 readonly source: AdvertisedEndpointSource;
18 readonly status?: AdvertisedEndpointStatus;
19 readonly isDefault?: boolean;
20 readonly description?: string;
21}
22
23export function normalizeHttpBaseUrl(rawValue: string): string {
24 const url = new URL(rawValue);
25 if (url.protocol === "ws:") {
26 url.protocol = "http:";
27 } else if (url.protocol === "wss:") {
28 url.protocol = "https:";
29 }
30
31 if (url.protocol !== "http:" && url.protocol !== "https:") {
32 throw new Error(`Endpoint must use HTTP or HTTPS. Received ${url.protocol}`);
33 }
34
35 url.pathname = "/";
36 url.search = "";
37 url.hash = "";
38 return url.toString();
39}
40
41export function deriveWsBaseUrl(httpBaseUrl: string): string {
42 const url = new URL(normalizeHttpBaseUrl(httpBaseUrl));
43 url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
44 return url.toString();
45}
46
47function classifyHostedHttpsCompatibility(
48 httpBaseUrl: string,
49 fallback: AdvertisedEndpointHostedHttpsCompatibility = "unknown",
50): AdvertisedEndpointHostedHttpsCompatibility {
51 const url = new URL(normalizeHttpBaseUrl(httpBaseUrl));
52 if (url.protocol === "http:") {
53 return "mixed-content-blocked";
54 }
55 return fallback === "mixed-content-blocked" ? "unknown" : fallback;
56}
57
58export function createAdvertisedEndpoint(input: CreateAdvertisedEndpointInput): AdvertisedEndpoint {
59 const httpBaseUrl = normalizeHttpBaseUrl(input.httpBaseUrl);
60 return {
61 id: input.id,
62 label: input.label,
63 provider: input.provider,
64 httpBaseUrl,
65 wsBaseUrl: deriveWsBaseUrl(httpBaseUrl),
66 reachability: input.reachability,
67 compatibility: {
68 hostedHttpsApp:
69 input.hostedHttpsCompatibility ?? classifyHostedHttpsCompatibility(httpBaseUrl),
70 desktopApp: "compatible",
71 },
72 source: input.source,
73 status: input.status ?? "available",
74 ...(input.isDefault === undefined ? {} : { isDefault: input.isDefault }),
75 ...(input.description === undefined ? {} : { description: input.description }),
76 };
77}