From f34ad8d9a6e3b0f13120fd08ffde9ad959bc6cca Mon Sep 17 00:00:00 2001 From: Reina To Date: Tue, 17 Feb 2026 10:26:20 -0500 Subject: [PATCH 1/3] Revert "(#73) Fix IOS opening in safari on wrapped proxy errors" This reverts commit 58cfe1f6b0236e18ae597a6f59432f7f79958766. --- bifrost/lib/hardRedirect.ts | 24 ------------------- .../renderer/wrapped/onBeforeRender.client.ts | 18 ++++++-------- 2 files changed, 7 insertions(+), 35 deletions(-) delete mode 100644 bifrost/lib/hardRedirect.ts diff --git a/bifrost/lib/hardRedirect.ts b/bifrost/lib/hardRedirect.ts deleted file mode 100644 index a297af6..0000000 --- a/bifrost/lib/hardRedirect.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Hard redirect (full page reload) that converts same-origin URLs to relative paths. - * Turbolinks IOS requires relative URLs to open within the mobile app instead of safari. - */ -export async function hardRedirect(url: string) { - if (url.startsWith("/")) { - window.location.href = url; - await new Promise(() => {}); - return; - } - - const parsedUrl = new URL(url); - - if (window.location.origin === parsedUrl.origin) { - // Relative redirect - window.location.href = parsedUrl.pathname + parsedUrl.search + parsedUrl.hash; - } else { - // External redirect - window.location.href = url; - } - - // stop vike rendering to let navigation happen - await new Promise(() => {}); -} diff --git a/bifrost/renderer/wrapped/onBeforeRender.client.ts b/bifrost/renderer/wrapped/onBeforeRender.client.ts index 681d75f..929e845 100644 --- a/bifrost/renderer/wrapped/onBeforeRender.client.ts +++ b/bifrost/renderer/wrapped/onBeforeRender.client.ts @@ -1,7 +1,6 @@ import "../../lib/type"; import type { PageContextClient } from "vike/types"; import { redirect } from "vike/abort"; -import { hardRedirect } from "../../lib/hardRedirect"; import { getElementAttributes } from "../../lib/elementUtils"; // onBeforeRender runs before changing the browser location, so `throw redirect` works @@ -56,7 +55,9 @@ export default async function wrappedOnBeforeRender( if (!resp) { // hard reload. can happen on cors errors when redirected to external page - await hardRedirect(pageContext.urlParsed.href); + window.location.href = pageContext.urlParsed.href; + // stop vike rendering to let navigation happen + await new Promise(() => {}); return; } @@ -70,16 +71,12 @@ export default async function wrappedOnBeforeRender( throw redirect(parsedUrl.pathname + parsedUrl.search + parsedUrl.hash); } else { // external redirect - await hardRedirect(resp.url); - return; + throw redirect(resp.url); } } - if (!resp.ok) { - await hardRedirect(resp.url); - return; + throw redirect(resp.url); } - const html = await resp.text(); const layoutInfo = pageContext.config.getLayout!( Object.fromEntries(resp.headers.entries()) @@ -87,9 +84,8 @@ export default async function wrappedOnBeforeRender( if (!layoutInfo) { // Fallback to full reload if layout not found // window.location.href = resp.url; - await hardRedirect(resp.url); - return; - } + throw redirect(resp.url); + } const parsed = document.createElement("html"); parsed.innerHTML = html; From 934830c8dbde3582f0755f6443bb149b2f1c465a Mon Sep 17 00:00:00 2001 From: Reina To Date: Tue, 17 Feb 2026 11:21:33 -0500 Subject: [PATCH 2/3] Fix IOS opening in safari on wrapped proxy errors --- .../renderer/wrapped/onBeforeRender.client.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bifrost/renderer/wrapped/onBeforeRender.client.ts b/bifrost/renderer/wrapped/onBeforeRender.client.ts index 929e845..6dcfb6e 100644 --- a/bifrost/renderer/wrapped/onBeforeRender.client.ts +++ b/bifrost/renderer/wrapped/onBeforeRender.client.ts @@ -75,7 +75,12 @@ export default async function wrappedOnBeforeRender( } } if (!resp.ok) { - throw redirect(resp.url); + // A little hacky here but we cannot navigate with window.location.href = + // since the mobile apps will open that in a browser tab + history.pushState(null, "", resp.url); + window.Turbolinks.controller.viewInvalidated(); + // stop vike rendering to let navigation happen + await new Promise(() => {}); } const html = await resp.text(); const layoutInfo = pageContext.config.getLayout!( @@ -83,8 +88,13 @@ export default async function wrappedOnBeforeRender( ); if (!layoutInfo) { // Fallback to full reload if layout not found - // window.location.href = resp.url; - throw redirect(resp.url); + // A little hacky here but we cannot navigate with window.location.href = + // since the mobile apps will open that in a browser tab + history.pushState(null, "", resp.url); + window.Turbolinks.controller.viewInvalidated(); + // stop vike rendering to let navigation happen + await new Promise(() => {}); + return; } const parsed = document.createElement("html"); From 3df4d6e8ce6c848aa8c178344ff470172c2aa19e Mon Sep 17 00:00:00 2001 From: Reina To Date: Tue, 17 Feb 2026 11:34:53 -0500 Subject: [PATCH 3/3] hardNavigate helper --- bifrost/lib/hardNavigate.ts | 10 ++++++++++ bifrost/renderer/wrapped/onBeforeRender.client.ts | 15 +++------------ 2 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 bifrost/lib/hardNavigate.ts diff --git a/bifrost/lib/hardNavigate.ts b/bifrost/lib/hardNavigate.ts new file mode 100644 index 0000000..421cfc0 --- /dev/null +++ b/bifrost/lib/hardNavigate.ts @@ -0,0 +1,10 @@ +/** + * Hard navigate to a URL using `history.pushState` and `window.location.reload` instead of + * `window.location.href`. This prevents the mobile apps from opening the URL in a browser tab. + */ +export async function hardNavigate(url: string): Promise { + history.pushState(null, "", url); + window.Turbolinks.controller.viewInvalidated(); + // stop vike rendering to let navigation happen + await new Promise(() => {}); +} diff --git a/bifrost/renderer/wrapped/onBeforeRender.client.ts b/bifrost/renderer/wrapped/onBeforeRender.client.ts index 6dcfb6e..9c552af 100644 --- a/bifrost/renderer/wrapped/onBeforeRender.client.ts +++ b/bifrost/renderer/wrapped/onBeforeRender.client.ts @@ -2,6 +2,7 @@ import "../../lib/type"; import type { PageContextClient } from "vike/types"; import { redirect } from "vike/abort"; import { getElementAttributes } from "../../lib/elementUtils"; +import { hardNavigate } from "../../lib/hardNavigate"; // onBeforeRender runs before changing the browser location, so `throw redirect` works // we wait for onBeforeRenderClient to call mergeHead, which runs after browser location change @@ -75,12 +76,7 @@ export default async function wrappedOnBeforeRender( } } if (!resp.ok) { - // A little hacky here but we cannot navigate with window.location.href = - // since the mobile apps will open that in a browser tab - history.pushState(null, "", resp.url); - window.Turbolinks.controller.viewInvalidated(); - // stop vike rendering to let navigation happen - await new Promise(() => {}); + await hardNavigate(resp.url); } const html = await resp.text(); const layoutInfo = pageContext.config.getLayout!( @@ -88,12 +84,7 @@ export default async function wrappedOnBeforeRender( ); if (!layoutInfo) { // Fallback to full reload if layout not found - // A little hacky here but we cannot navigate with window.location.href = - // since the mobile apps will open that in a browser tab - history.pushState(null, "", resp.url); - window.Turbolinks.controller.viewInvalidated(); - // stop vike rendering to let navigation happen - await new Promise(() => {}); + await hardNavigate(resp.url); return; }