import type { MapCacheIntent, MapGatewayError, MapProviderId, MapProviderState, MapRuntimeConfiguration, MapRuntimeState, } from "./contracts.js"; import { CESIUM_RENDERER_VERSION, MAP_PAGE_VERSION, MAP_RUNTIME_SCHEMA_VERSION, } from "./contracts.js"; const SAFE_ERROR_CODE = /^[a-z][a-z0-9_]{0,95}$/; const PROVIDER_IDS: readonly MapProviderId[] = ["imagery", "terrain", "buildings"]; const CREDENTIAL_QUERY_KEYS = new Set([ "access_token", "access-token", "authorization", "credential", "key", "signature", "sig", "token", ]); export class MapRuntimeError extends Error { readonly code: string; constructor(code: string) { super(code); this.name = "MapRuntimeError"; this.code = SAFE_ERROR_CODE.test(code) ? code : "map_runtime_error"; } } export function initialMapRuntimeState(): MapRuntimeState { return { phase: "loading", providers: { imagery: { phase: "idle" }, terrain: { phase: "idle" }, buildings: { phase: "idle" }, }, }; } export function withProviderState( current: MapRuntimeState, providerId: MapProviderId, providerState: MapProviderState, ): MapRuntimeState { const providers = { ...current.providers, [providerId]: providerState, }; const enabled = PROVIDER_IDS.map((id) => providers[id]).filter( (state) => state.phase !== "disabled", ); const readyCount = enabled.filter((state) => state.phase === "ready").length; const errorCount = enabled.filter((state) => state.phase === "error").length; const loadingCount = enabled.filter((state) => ["idle", "loading"].includes(state.phase), ).length; const phase = loadingCount > 0 ? "loading" : errorCount === 0 ? "ready" : readyCount > 0 ? "degraded" : "gateway-unavailable"; return { phase, providers, ...(errorCount > 0 ? { code: enabled.find((state) => state.phase === "error")?.code ?? "map_provider_unavailable", } : {}), }; } export async function fetchMapRuntimeConfiguration( path: string, signal: AbortSignal, ): Promise { const document = await fetchSameOriginJson(path, signal); if ( document.schema_version !== MAP_RUNTIME_SCHEMA_VERSION || document.map_page_version !== MAP_PAGE_VERSION || !isRecord(document.renderer) || document.renderer.id !== "cesium" || document.renderer.version !== CESIUM_RENDERER_VERSION || !isRecord(document.gateway) || document.gateway.configured !== true || !isSafeSameOriginPath(document.gateway.health_url) || !isSafeSameOriginPath(document.gateway.asset_endpoint_template) || !isSafeSameOriginPath(document.gateway.cache_proxy_prefix) || !isRecord(document.assets) || document.assets.imagery !== 2 || document.assets.terrain !== 1 || document.assets.buildings !== 96188 || (document.sandbox !== undefined && (!isRecord(document.sandbox) || typeof document.sandbox.hide_credit_overlay !== "boolean")) ) { throw new MapRuntimeError("map_runtime_contract_mismatch"); } return document as MapRuntimeConfiguration; } export async function fetchSameOriginJson( path: string, signal: AbortSignal, ): Promise> { if (!isSafeSameOriginPath(path)) { throw new MapRuntimeError("map_runtime_cross_origin_forbidden"); } const response = await fetch(path, { method: "GET", credentials: "same-origin", cache: "no-store", signal, headers: { Accept: "application/json" }, }); const document = await response.json().catch(() => null); if (!response.ok) { throw new MapRuntimeError(readSafeErrorCode(document)); } if (!isRecord(document)) { throw new MapRuntimeError("map_runtime_invalid_response"); } return document; } export function assetEndpointPath( configuration: MapRuntimeConfiguration, providerId: MapProviderId, ): string { const assetId = configuration.assets[providerId]; return configuration.gateway.asset_endpoint_template.replace( "{asset_id}", String(assetId), ); } export function applyCacheIntent( rawUrl: string, intent: MapCacheIntent, refresh: boolean, ): string { const url = new URL(rawUrl); if (url.protocol !== "https:" || url.username || url.password || url.hash) { throw new MapRuntimeError("map_provider_url_rejected"); } for (const key of url.searchParams.keys()) { if (CREDENTIAL_QUERY_KEYS.has(key.toLowerCase())) { throw new MapRuntimeError("map_provider_credential_rejected"); } } if (!intent.enabled) { url.searchParams.set("nodedc_cache_mode", "passthrough"); } else { url.searchParams.set("nodedc_cache_profile", "live"); if (!intent.no_overwrite || refresh) { url.searchParams.set("nodedc_cache_refresh", "1"); } } return url.toString(); } export function isSafeSameOriginPath(value: unknown): value is string { if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//")) { return false; } try { const resolved = new URL(value, window.location.origin); return resolved.origin === window.location.origin; } catch { return false; } } export function readSafeErrorCode(value: unknown): string { if (isRecord(value)) { const direct = value.code; if (typeof direct === "string" && SAFE_ERROR_CODE.test(direct)) { return direct; } const error = value.error; if (isRecord(error) && typeof error.code === "string" && SAFE_ERROR_CODE.test(error.code)) { return error.code; } if (typeof error === "string" && SAFE_ERROR_CODE.test(error)) { return error; } } return "map_gateway_request_failed"; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } export function asMapGatewayError(value: unknown): MapGatewayError | null { return isRecord(value) ? (value as MapGatewayError) : null; }