import type { ApplicationList } from "../../core/applications/application-list"; import type { PayloadOf, NavigationTarget } from "../../runtime/topics"; type ResolvedNavigation = | { status: "navigable"; href: string } | { status: "pending" } | { status: "refused" }; const APPLICATION_ROUTE = /^\/(?:(?:media|canvas)(?=\/|$)|(?:studio|application|local)\/([^/]+))/; function applicationRoute(pathname: string) { const [base, id] = APPLICATION_ROUTE.exec(pathname) ?? []; return base === undefined ? null : { base, id }; } export function targetOf( applications: ApplicationList, href: string, ): NavigationTarget { const url = new URL(href, window.location.origin); const route = applicationRoute(url.pathname); const application = route && applications.findByPath(url.pathname); if (!route || !application) return { appId: null, path: hrefOf(url) }; return { appId: application.id, path: hrefOf(url).slice(route.base.length).replace(/^\/+/, ""), }; } export function resolveNavigation( applications: ApplicationList | null, { url }: PayloadOf<"navigation.location.update">, ): ResolvedNavigation { const target = sameOriginUrl(url); if (!target) return { status: "refused" }; if (!applications && applicationRoute(target.pathname)) { return { status: "pending" }; } return { status: "navigable", href: hrefOf(target) }; } export function normalizeHref(href: string): string { return hrefOf(new URL(href, window.location.origin)); } function sameOriginUrl(href: string): URL | null { try { const url = new URL(href, window.location.origin); return url.origin === window.location.origin ? url : null; } catch { return null; } } function hrefOf({ pathname, search, hash }: URL): string { return pathname + search + hash; }