1// Active visit state only. No localStorage, sessionStorage, Cache API or tile cache. 2const SESSION_LIFETIME = 3 * 60 * 60 * 1000 - 60_000; 3const TIMEOUT = 15_000; 4 5export function createVisitSessions({ fetcher = globalThis.fetch.bind(globalThis), now = Date.now } = {}) { 6 const slots = new Map(); 7 const counters = { imagerySessions: 0, tilesSessions: 0 }; 8 let sequence = 0; 9 10 async function request(url, options = {}) { 11 const signals = [AbortSignal.timeout(TIMEOUT)]; 12 if (options.signal) signals.push(options.signal); 13 return fetcher(url, { ...options, credentials: 'omit', referrerPolicy: 'strict-origin', signal: AbortSignal.any(signals) }); 14 } 15 16 async function endpoint(asset, token) { 17 if (!token) throw new Error('Cesium browser token is missing'); 18 const response = await request(`https://api.cesium.com/v1/assets/${asset}/endpoint`, { 19 headers: { Authorization: `Bearer ${token}` }, 20 }); 21 if (!response.ok) throw new Error(`Cesium session unavailable (${response.status})`); 22 return response.json(); 23 } 24 25 function acquire(kind, token, failedId, load) { 26 const key = `${kind}:${token}`; 27 let slot = slots.get(key); 28 if (slot?.value && (slot.value.expiresAt <= now() || slot.value.id === failedId)) { 29 slots.delete(key); 30 slot = undefined; 31 } 32 if (slot?.value) return Promise.resolve(slot.value); 33 if (slot?.pending) return slot.pending; 34 slot = {}; 35 slots.set(key, slot); 36 const started = now(); 37 slot.pending = load().then(value => { 38 // Bound opaque credentials by a visit window; honor explicit earlier expiry. 39 const expiry = Number(value.endpoint?.options?.expiry ?? value.endpoint?.expiry); 40 const expiresAt = Math.min(started + SESSION_LIFETIME, expiry > 0 ? expiry * 1000 - 30_000 : Infinity); 41 slot.value = { ...value, id: `${kind}-${++sequence}`, expiresAt }; 42 return slot.value; 43 }).catch(error => { 44 if (slots.get(key) === slot) slots.delete(key); 45 throw error; 46 }).finally(() => { slot.pending = null; }); 47 return slot.pending; 48 } 49 50 function get2D(token, failedId) { 51 return acquire('2d', token, failedId, async () => { 52 const data = await endpoint(3830184, token); 53 const url = new URL(data.options?.url); 54 if (data.type !== 'IMAGERY' || data.externalType !== 'GOOGLE_2D_MAPS' || 55 url.origin !== 'https://assets.ion.cesium.com' || !url.pathname.startsWith('/proxy/') || 56 !data.options.session || !data.options.key || data.options.tileWidth !== 256 || data.options.tileHeight !== 256) { 57 throw new Error('Unexpected Google imagery endpoint'); 58 } 59 counters.imagerySessions++; 60 return { endpoint: data }; 61 }); 62 } 63 64 function get3D(token, failedId) { 65 return acquire('3d', token, failedId, async () => { 66 const data = await endpoint(2275207, token); 67 const rootUrl = data.options?.url; 68 const url = new URL(rootUrl); 69 if (data.type !== '3DTILES' || url.origin !== 'https://tile.googleapis.com' || 70 !/^\/v1\/3dtiles\/root(?:\.json)?$/.test(url.pathname) || !url.searchParams.get('key')) { 71 throw new Error('Unexpected Google 3D endpoint'); 72 } 73 const response = await request(rootUrl); 74 if (!response.ok) throw new Error(`Google 3D session unavailable (${response.status})`); 75 const root = await response.json(); 76 let session; 77 const walk = tile => { 78 const uri = tile.content?.uri ?? tile.content?.url; 79 if (uri) session ||= new URL(uri, rootUrl).searchParams.get('session'); 80 for (const child of tile.children || []) walk(child); 81 }; 82 if (!root.root || !root.asset) throw new Error('Invalid Google 3D root'); 83 walk(root.root); 84 if (!session) throw new Error('Google 3D root did not include a session'); 85 counters.tilesSessions++; 86 // Retain the live tileset descriptor for this app visit, never tile payloads. 87 return { endpoint: data, root, rootUrl, session, key: url.searchParams.get('key') }; 88 }); 89 } 90 91 async function fetch3D(token, input, options = {}) { 92 options.signal?.throwIfAborted(); 93 let lease = await get3D(token); 94 for (let attempt = 0; attempt < 2; attempt++) { 95 options.signal?.throwIfAborted(); 96 const url = new URL(input, lease.rootUrl); 97 if (url.origin !== 'https://tile.googleapis.com' || !url.pathname.startsWith('/v1/3dtiles/')) { 98 throw new Error('Unexpected Google tile origin'); 99 } 100 url.searchParams.set('key', lease.key); 101 url.searchParams.set('session', lease.session); 102 const headers = new Headers(options.headers); 103 headers.delete('Authorization'); 104 const response = await request(url.href, { ...options, headers }); 105 if (response.ok || attempt || ![400, 401, 403].includes(response.status)) return response; 106 // Quota/permission failures are not session expiry. Do not create a storm 107 // of roots for 403/429 or missing tiles; refresh only explicit expiry. 108 let expired = response.status === 401;
109 try { 110 const error = await response.clone().json(); 111 expired ||= /session.*(?:expir|invalid)|(?:expir|invalid).*session/i.test(error.error?.message || ''); 112 } catch { /* Non-JSON failures follow the normal renderer error path. */ } 113 if (!expired) return response; 114 lease = await get3D(token, lease.id); 115 } 116 } 117 118 return { get2D, get3D, fetch3D, stats: () => ({ ...counters }) }; 119}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.