import { environment } from '../environments/environment'; import { demoTabAtLoad } from '../services/authStorage'; import { noteDemoRefusal } from '../services/demoTab'; declare const AKASHIC_CLIENT_VERSION: string | undefined; /** What this client is, as the API is told: the contract it speaks and the build it came from. */ export const CLIENT_MANIFEST = { apiContract: 'v1', clientVersion: typeof AKASHIC_CLIENT_VERSION === 'string' && AKASHIC_CLIENT_VERSION ? AKASHIC_CLIENT_VERSION : 'unknown', } as const; export const CLIENT_VERSION_HEADER = 'X-Akashic-Client-Version'; const API_BASES = [ 'apiUrl', 'fhirFoundationApi', 'fhirBaseApi', 'fhirIdentityApi', 'fhirClinicalApi', 'orgAuthorizationApi', 'fhirGuardianshipApi', 'liveFhirBaseApi', 'liveFhirFoundationApi', 'sandboxFhirFoundationApi', ] as const; const originOf = (url: string): string | undefined => { try { return new URL(url, typeof window === 'undefined' ? undefined : window.location.href).origin; } catch { return undefined; } }; /** Only our own REST API gets the header; S3, Stripe and Cognito would answer an unknown header with a CORS refusal. */ export const isAkashicApiUrl = (url: string): boolean => { const origin = originOf(url); if (!origin) return false; const env = environment as unknown as Record; return API_BASES.some((key) => env[key] && originOf(env[key] as string) === origin); }; const withHeader = (headers: HeadersInit | undefined): HeadersInit => { if (headers instanceof Headers) { const next = new Headers(headers); next.set(CLIENT_VERSION_HEADER, CLIENT_MANIFEST.clientVersion); return next; } if (Array.isArray(headers)) return [...headers, [CLIENT_VERSION_HEADER, CLIENT_MANIFEST.clientVersion]]; return { ...(headers ?? {}), [CLIENT_VERSION_HEADER]: CLIENT_MANIFEST.clientVersion }; }; /** The request options with the client version added when the URL is one of our API hosts. */ export const withClientVersion = (url: string | URL, init: T = {} as T): T => isAkashicApiUrl(String(url)) ? { ...init, headers: withHeader(init.headers) } : init; /** `fetch` for callers that cannot go through HttpService; it adds the client version on our API hosts. */ export const akashicFetch = async (url: string | URL, init?: RequestInit): Promise => { const response = await fetch(url, withClientVersion(url, init ?? {})); // A demo tab hears a refusal that ends its visit here too, not only through HttpService. if (demoTabAtLoad) void noteDemoRefusal(response.clone()); return response; };