/** * Resolve a package-app path against a relative data-base and the page origin. * `new URL(path, '/packages/lineage/')` throws in browsers. */ export function resolveAppUrl(base: string, path: string, origin: string): string { const root = new URL(base || '/', origin) if (!root.pathname.endsWith('/')) root.pathname += '/' return new URL(String(path).replace(/^\//, ''), root).toString() } export function scopedAssetPath(scope: string, path: string): string { const root = scope.endsWith('/') ? scope : `${scope}/` return `${root}${String(path).replace(/^\//, '')}` } /** Default max scope for `sw.js` at `/packages/foo/sw.js` — trailing slash, no host header required. */ export function serviceWorkerScope(base: string, origin: string): string { const root = new URL(base || '/', origin) if (!root.pathname.endsWith('/')) root.pathname += '/' return root.href } export function shouldCanonicalizeClientPath(pathname: string): boolean { return Boolean(pathname) && !pathname.endsWith('/') } export function serviceWorkerAllowedPath(base: string): string { const path = base.startsWith('/') ? base : `/${base}` return path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path } export function needsCanonicalAppRedirect(pathname: string, appBasePath: string): boolean { return pathname === appBasePath }