@@ -67,6 +67,35 @@ function isGoogleConsentHost(hostname: string): boolean {
6767 return normalizeHost ( hostname ) === "consent.google.com" ;
6868}
6969
70+ function extractPlaceIdFromDataParam ( dataParam : string ) : string | null {
71+ // Extract place ID from Google Maps data parameter
72+ // Format: data=!4m2!3m1!1s<PLACE_ID>!...
73+ const match = dataParam . match ( / ! 1 s ( [ 0 - 9 a - f x : ] + ) / i) ;
74+ return match ?. [ 1 ] ?? null ;
75+ }
76+
77+ function simplifyGoogleMapsUrl ( mapsUrl : string ) : string | null {
78+ try {
79+ const parsed = new URL ( mapsUrl ) ;
80+ if ( ! parsed . hostname . includes ( "google.com" ) ) return null ;
81+
82+ // Try to extract place ID from data parameter
83+ const dataParam = parsed . pathname . match ( / \/ d a t a = ( [ ^ / ] + ) / ) ?. [ 1 ] ;
84+ if ( dataParam ) {
85+ const placeId = extractPlaceIdFromDataParam ( decodeURIComponent ( dataParam ) ) ;
86+ if ( placeId ) {
87+ // Construct minimal URL with just place ID
88+ return `https://www.google.com/maps/search/?api=1&query=${ encodeURIComponent ( placeId ) } &query_place_id=${ encodeURIComponent ( placeId ) } ` ;
89+ }
90+ }
91+
92+ // Fallback: strip query params and keep just the path
93+ return `https://www.google.com${ parsed . pathname } ` ;
94+ } catch {
95+ return null ;
96+ }
97+ }
98+
7099function providerFromHost ( hostname : string ) : ParsedSharedMapLink [ "provider" ] | null {
71100 const host = normalizeHost ( hostname ) ;
72101 if ( host === "maps.apple.com" ) return "apple" ;
@@ -324,15 +353,49 @@ async function fetchMapPageText(
324353
325354 // Check if we got redirected to a consent page
326355 const responseUrl = typeof response . url === "string" ? parseUrl ( response . url ) : null ;
327- if ( responseUrl && isGoogleConsentHost ( responseUrl . hostname ) && maxConsentRedirects > 0 ) {
356+ if ( responseUrl && isGoogleConsentHost ( responseUrl . hostname ) ) {
328357 console . info ( "[share.debug] fetch_hit_consent_page" , { url, responseUrl : response . url } ) ;
329- // Extract the real Maps URL from the consent page
330- const extractedUrl =
331- typeof response . url === "string" ? extractUrlFromGoogleConsent ( response . url ) : null ;
332- if ( extractedUrl ) {
333- console . info ( "[share.debug] fetch_extracted_from_consent" , extractedUrl ) ;
334- // Recursively fetch the extracted URL, but limit redirects to prevent infinite loops
335- return await fetchMapPageText ( extractedUrl , options , maxConsentRedirects - 1 ) ;
358+
359+ if ( maxConsentRedirects > 0 ) {
360+ // Extract the real Maps URL from the consent page
361+ const extractedUrl =
362+ typeof response . url === "string" ? extractUrlFromGoogleConsent ( response . url ) : null ;
363+ if ( extractedUrl ) {
364+ console . info ( "[share.debug] fetch_extracted_from_consent" , extractedUrl ) ;
365+ // Recursively fetch the extracted URL, but limit redirects to prevent infinite loops
366+ return await fetchMapPageText ( extractedUrl , options , maxConsentRedirects - 1 ) ;
367+ }
368+ } else {
369+ // Out of retries, try one last thing: simplify the URL
370+ console . info ( "[share.debug] fetch_consent_retries_exhausted_trying_simplified" ) ;
371+ const simplifiedUrl = simplifyGoogleMapsUrl ( url ) ;
372+ if ( simplifiedUrl && simplifiedUrl !== url ) {
373+ console . info ( "[share.debug] fetch_trying_simplified_url" , simplifiedUrl ) ;
374+ try {
375+ const simpleResponse = await withTimeout (
376+ fetchImpl ( simplifiedUrl , {
377+ method : "GET" ,
378+ redirect : "follow" ,
379+ } ) ,
380+ timeoutMs ,
381+ ) ;
382+ if ( typeof simpleResponse . text === "function" ) {
383+ const simpleText = await withTimeout (
384+ Promise . resolve ( simpleResponse . text ( ) ) ,
385+ timeoutMs ,
386+ ) ;
387+ console . info ( "[share.debug] fetch_simplified_result_length" , simpleText ?. length ?? 0 ) ;
388+ // Check if we still hit consent
389+ const simpleUrl =
390+ typeof simpleResponse . url === "string" ? parseUrl ( simpleResponse . url ) : null ;
391+ if ( simpleUrl && ! isGoogleConsentHost ( simpleUrl . hostname ) ) {
392+ return simpleText ;
393+ }
394+ }
395+ } catch ( err ) {
396+ console . info ( "[share.debug] fetch_simplified_failed" , err ) ;
397+ }
398+ }
336399 }
337400 }
338401
@@ -379,7 +442,20 @@ function extractUrlFromGoogleConsent(consentUrl: string): string | null {
379442 const decodedUrl = decodeURIComponent ( continueParam ) ;
380443 const continueUrlParsed = new URL ( decodedUrl ) ;
381444 if ( isSupportedMapHost ( continueUrlParsed ) ) {
382- return decodedUrl ;
445+ // Strip tracking parameters that might trigger consent loops
446+ const paramsToRemove = [
447+ "utm_source" ,
448+ "utm_medium" ,
449+ "utm_campaign" ,
450+ "entry" ,
451+ "coh" ,
452+ "g_ep" ,
453+ "skid" ,
454+ ] ;
455+ for ( const param of paramsToRemove ) {
456+ continueUrlParsed . searchParams . delete ( param ) ;
457+ }
458+ return continueUrlParsed . toString ( ) ;
383459 }
384460 } catch {
385461 // If continue param isn't a valid URL, return null
0 commit comments