@@ -17,6 +17,7 @@ export interface BrowserPreviewNetworkPolicy {
1717 recordExternal : boolean
1818 stats : Map < string , { requests : number ; external : boolean ; blocked : number ; routed : number } >
1919 routedRedirectEscapes : Array < { rawOrigin : string ; effectiveOrigin : string ; reason : string } >
20+ preserveRoutedOrigin : boolean
2021}
2122
2223export interface BrowserPreviewNavigationDecision {
@@ -44,10 +45,20 @@ export interface BrowserPreviewTopology {
4445 preview : BrowserProbePreviewRouting
4546 networkPolicy : BrowserPreviewNetworkPolicy
4647 routedHosts : string [ ]
47- origins : { localPreviewOrigin : string ; requestedPreviewOrigin ?: string ; effectivePreviewOrigin : string }
48+ origins : BrowserPreviewOrigins
4849 navigationScope : BrowserPreviewNavigationScope
4950 resolveUrl ( pathOrUrl : string ) : string
5051 authCookieUrls ( targetUrls : string [ ] ) : string [ ]
52+ contextOptions ( ) : { proxy ?: { server : string } }
53+ }
54+
55+ export interface BrowserPreviewOrigins {
56+ localPreviewOrigin : string
57+ requestedPreviewOrigin ?: string
58+ effectivePreviewOrigin : string
59+ canonicalBrowserOrigin : string
60+ localProxyOrigin : string
61+ upstreamRuntimeOrigin ?: string
5162}
5263
5364export interface BrowserPreviewRouteTracker {
@@ -97,23 +108,27 @@ export function browserPreviewRouting(args: string[], runtimeSpec: RuntimeCreate
97108 }
98109}
99110
100- export function browserPreviewTopology ( args : string [ ] , runtimeSpec : RuntimeCreateSpec | undefined , localPreviewOrigin : string ) : BrowserPreviewTopology {
101- const preview = browserPreviewRouting ( args , runtimeSpec , localPreviewOrigin )
111+ export function browserPreviewTopology ( args : string [ ] , runtimeSpec : RuntimeCreateSpec | undefined , localPreviewOrigin : string , upstreamRuntimeOrigin ?: string ) : BrowserPreviewTopology {
102112 const routedHosts = commaListArg ( args , "route-host" )
113+ const preview = browserPreviewRouting ( args , runtimeSpec , localPreviewOrigin )
114+ applyCanonicalRoutedPreviewOrigin ( preview , runtimeSpec ?. preview ?. siteUrl , routedHosts )
103115 const networkPolicy = browserPreviewNetworkPolicy ( args , routedHosts , preview )
104116
105117 return {
106118 preview,
107119 networkPolicy,
108120 routedHosts,
109- origins : browserPreviewOrigins ( preview ) ,
121+ origins : browserPreviewOrigins ( preview , upstreamRuntimeOrigin ) ,
110122 navigationScope : browserPreviewNavigationScope ( preview . effectiveOrigin , networkPolicy ) ,
111123 resolveUrl ( pathOrUrl ) {
112124 return resolveBrowserPreviewUrl ( pathOrUrl , preview . effectiveOrigin )
113125 } ,
114126 authCookieUrls ( targetUrls ) {
115127 return browserPreviewAuthCookieUrls ( localPreviewOrigin , routedHosts , targetUrls )
116128 } ,
129+ contextOptions ( ) {
130+ return networkPolicy . preserveRoutedOrigin ? { proxy : { server : new URL ( localPreviewOrigin ) . origin } } : { }
131+ } ,
117132 }
118133}
119134
@@ -146,14 +161,52 @@ export function browserPreviewNavigationScope(effectivePreviewOrigin: string, po
146161 }
147162}
148163
149- export function browserPreviewOrigins ( preview : BrowserProbePreviewRouting ) : { localPreviewOrigin : string ; requestedPreviewOrigin ?: string ; effectivePreviewOrigin : string } {
164+ export function browserPreviewOrigins ( preview : BrowserProbePreviewRouting , upstreamRuntimeOrigin ?: string ) : BrowserPreviewOrigins {
150165 return {
151166 localPreviewOrigin : preview . localOrigin ,
152- requestedPreviewOrigin : preview . publicOrigin ,
167+ ... ( preview . publicOrigin ? { requestedPreviewOrigin : preview . publicOrigin } : { } ) ,
153168 effectivePreviewOrigin : preview . effectiveOrigin ,
169+ canonicalBrowserOrigin : new URL ( preview . effectiveOrigin ) . origin ,
170+ localProxyOrigin : new URL ( preview . localOrigin ) . origin ,
171+ ...( upstreamRuntimeOrigin ? { upstreamRuntimeOrigin : new URL ( upstreamRuntimeOrigin ) . origin } : { } ) ,
154172 }
155173}
156174
175+ function applyCanonicalRoutedPreviewOrigin ( preview : BrowserProbePreviewRouting , siteUrl : string | undefined , routedHosts : string [ ] ) : void {
176+ if ( preview . effectiveMode !== "local" || ! siteUrl ) {
177+ return
178+ }
179+
180+ let canonical : URL
181+ try {
182+ canonical = new URL ( siteUrl )
183+ } catch {
184+ return
185+ }
186+ const host = normalizeBrowserPreviewHost ( canonical . hostname )
187+ if ( ! routedHosts . map ( normalizeBrowserPreviewHost ) . includes ( host ) ) {
188+ return
189+ }
190+
191+ if ( canonical . protocol !== "http:" ) {
192+ preview . diagnostics . push ( {
193+ code : "preview-canonical-origin-preservation-inconclusive" ,
194+ severity : "error" ,
195+ message : "The local Playground provider cannot preserve this declared canonical preview protocol." ,
196+ details : { status : "inconclusive" , canonicalOrigin : canonical . origin , supportedProtocols : [ "http:" ] } ,
197+ } )
198+ return
199+ }
200+
201+ preview . effectiveOrigin = canonical . toString ( )
202+ preview . diagnostics . push ( {
203+ code : "preview-canonical-routed-origin" ,
204+ severity : "info" ,
205+ message : "The declared routed preview alias is the browser-visible origin." ,
206+ details : { canonicalOrigin : canonical . origin , localProxyOrigin : new URL ( preview . localOrigin ) . origin } ,
207+ } )
208+ }
209+
157210export function browserPreviewReadinessError ( preview : BrowserProbePreviewRouting ) : Error | undefined {
158211 const diagnostic = preview . diagnostics . find ( ( item ) => item . severity === "error" )
159212 if ( ! diagnostic ) {
@@ -218,6 +271,7 @@ export function browserPreviewNetworkPolicy(args: string[], routeHosts: string[]
218271 recordExternal : strictBooleanArg ( args , "record-external" , false ) ,
219272 stats : new Map ( ) ,
220273 routedRedirectEscapes : [ ] ,
274+ preserveRoutedOrigin : new URL ( preview . effectiveOrigin ) . origin !== new URL ( preview . localOrigin ) . origin && preview . effectiveMode === "local" ,
221275 }
222276}
223277
@@ -321,28 +375,38 @@ async function routeBrowserPreviewNetwork(routePattern: (url: string, handler: (
321375 stat . requests += 1
322376 stat . external = ! policy . firstPartyHosts . has ( host )
323377
324- if ( policy . blockHosts . has ( host ) || ( policy . mode === "block" && stat . external && ! policy . allowHosts . has ( host ) ) ) {
378+ if ( policy . blockHosts . has ( host ) ) {
325379 stat . blocked += 1
326380 await route . abort ( "blockedbyclient" )
327381 return
328382 }
329383
330- if ( ! policy . routeHosts . has ( host ) ) {
331- await route . continue ( )
384+ if ( policy . routeHosts . has ( host ) ) {
385+ stat . routed += 1
386+ if ( policy . preserveRoutedOrigin ) {
387+ await route . continue ( )
388+ return
389+ }
390+ const task = fulfillBrowserPreviewRoutedHost ( route , requestUrl , policy , origin )
391+ tracker ?. pending . add ( task )
392+ try {
393+ await task
394+ } catch ( error ) {
395+ tracker ?. errors . push ( sanitizeBrowserPreviewRouteError ( error ) )
396+ await route . abort ( "failed" ) . catch ( ( ) => undefined )
397+ } finally {
398+ tracker ?. pending . delete ( task )
399+ }
332400 return
333401 }
334402
335- stat . routed += 1
336- const task = fulfillBrowserPreviewRoutedHost ( route , requestUrl , policy , origin )
337- tracker ?. pending . add ( task )
338- try {
339- await task
340- } catch ( error ) {
341- tracker ?. errors . push ( sanitizeBrowserPreviewRouteError ( error ) )
342- await route . abort ( "failed" ) . catch ( ( ) => undefined )
343- } finally {
344- tracker ?. pending . delete ( task )
403+ if ( policy . preserveRoutedOrigin || ( policy . mode === "block" && stat . external && ! policy . allowHosts . has ( host ) ) || ( request . resourceType ( ) === "document" && stat . external ) ) {
404+ stat . blocked += 1
405+ await route . abort ( "blockedbyclient" )
406+ return
345407 }
408+
409+ await route . continue ( )
346410 } )
347411}
348412
0 commit comments