import { GetConfiguration } from '@nitrots/nitro-renderer'; import JSON5 from 'json5'; import { configFileUrl, getClientMode, installSecureFetch } from './secure-assets'; declare const __NITRO_JSON_MODE__: 'legacy' | 'json5' | 'auto' | undefined; const resolveJsonMode = (): 'legacy' | 'json5' | 'auto' => { try { if(typeof __NITRO_JSON_MODE__ !== 'undefined' && __NITRO_JSON_MODE__) { if(__NITRO_JSON_MODE__ === 'legacy' || __NITRO_JSON_MODE__ === 'json5' || __NITRO_JSON_MODE__ === 'auto') return __NITRO_JSON_MODE__; } } catch {} return 'auto'; }; const ensureMobileViewport = () => { let viewport = document.querySelector('meta[name="viewport"]'); if(!viewport) { viewport = document.createElement('meta'); viewport.name = 'viewport'; document.head.appendChild(viewport); } viewport.content = 'width=device-width, initial-scale=1, viewport-fit=cover'; }; ensureMobileViewport(); const setBootDebug = (message: string) => { try { (window as any).__nitroBootDebug = message; const secureNode = document.getElementById('nitro-secure-debug'); if(secureNode) secureNode.textContent = `${ secureNode.textContent }\n${ message }`; } catch {} }; const deployBaseUrl = (): string => { try { const loaderBase = (window as any).__nitroLoaderBase; if(typeof loaderBase === 'string' && loaderBase.length) return new URL('..', loaderBase).toString(); } catch {} try { const moduleUrl = (import.meta as any).url; if(typeof moduleUrl === 'string' && moduleUrl.length) return new URL('..', new URL('.', moduleUrl)).toString(); } catch {} try { const base = (import.meta as any).env?.BASE_URL; if(typeof base === 'string' && base.length) { const trimmed = base.replace(/^\/+/, '').replace(/\/+$/, ''); return trimmed ? `${ window.location.origin }/${ trimmed }/` : `${ window.location.origin }/`; } } catch {} return `${ window.location.origin }/`; }; const loadClientMode = async () => { try { if((window as any).__nitroClientMode) return; const url = new URL('configuration/client-mode.json', deployBaseUrl()); url.searchParams.set('v', Date.now().toString(36)); const response = await fetch(url.toString()); if(!response.ok) throw new Error(`HTTP ${ response.status }`); const text = await response.text(); const mode = resolveJsonMode(); if(mode === 'legacy') { (window as any).__nitroClientMode = JSON.parse(text); } else if(mode === 'json5') { (window as any).__nitroClientMode = JSON5.parse(text); } else { try { (window as any).__nitroClientMode = JSON.parse(text); } catch { (window as any).__nitroClientMode = JSON5.parse(text); } } setBootDebug(`boot: client-mode loaded (mode=${ mode })`); } catch(error) { setBootDebug(`boot: client-mode fallback ${ error?.message || error }`); } }; await loadClientMode(); installSecureFetch(); setBootDebug('boot: secure fetch installed'); const search = new URLSearchParams(window.location.search); const clientMode = getClientMode(); (window as any).NitroSecureApiUrl = clientMode.apiBaseUrl || window.location.origin; (window as any).NitroClientMode = clientMode; (window as any).NitroConfig = { 'config.urls': [ configFileUrl('renderer-config.json', true), configFileUrl('ui-config.json', true) ], 'sso.ticket': search.get('sso') || null, 'forward.type': search.get('room') ? 2 : -1, 'forward.id': search.get('room') || 0, 'friend.id': search.get('friend') || 0 }; setBootDebug('boot: NitroConfig assigned'); // Load renderer-config.json + ui-config.json BEFORE rendering React. Otherwise // the first paint triggers a flood of "Missing configuration key" warnings for // every key components read synchronously (asset.url, login.endpoint, …) until // prepare()'s deferred init() finally lands. Doing it here makes the config // already populated by the time index.tsx mounts . try { await GetConfiguration().init(); setBootDebug('boot: configuration init done'); } catch(error) { setBootDebug(`boot: configuration init failed ${ error?.message || error }`); } import('./index') .then(() => setBootDebug('boot: app bundle imported')) .catch(error => { setBootDebug(`boot: import failed ${ error?.message || error }`); throw error; });