From 7e22831d08b2b5f0a67982de67b41c866b3391ee Mon Sep 17 00:00:00 2001 From: Char Date: Tue, 19 May 2026 23:22:11 +0100 Subject: [PATCH] v26.3 --- API/server.js | 243 +++++++-- Configs/api.yml.example | 11 + Configs/supportbot.yml | 2 +- .../1505240742992548034-transcript.html | 488 ------------------ .../1505247252829180018-transcript.html | 426 --------------- Data/transcript-settings.json | 9 + Logs/Error/Error-2026-05-16.log | 4 - Logs/Output/Output-2026-05-16.log | 323 ------------ README.md | 4 +- Structures/DashboardBranding.js | 168 ++++++ Structures/DashboardSession.js | 8 +- Structures/DiscordOAuth.js | 139 ++++- Structures/TranscriptStore.js | 223 ++++++++ package-lock.json | 4 +- package.json | 2 +- public/dashboard/assets/index-DPWGusls.js | 187 ------- public/dashboard/assets/index-DUq0PrN_.css | 1 + public/dashboard/assets/index-DxUg_xTw.css | 1 - public/dashboard/assets/index-WkaLPu9d.js | 187 +++++++ public/dashboard/index.html | 4 +- 20 files changed, 945 insertions(+), 1489 deletions(-) delete mode 100644 Data/Transcripts/1505240742992548034-transcript.html delete mode 100644 Data/Transcripts/1505247252829180018-transcript.html create mode 100644 Data/transcript-settings.json delete mode 100644 Logs/Error/Error-2026-05-16.log delete mode 100644 Logs/Output/Output-2026-05-16.log create mode 100644 Structures/DashboardBranding.js create mode 100644 Structures/TranscriptStore.js delete mode 100644 public/dashboard/assets/index-DPWGusls.js create mode 100644 public/dashboard/assets/index-DUq0PrN_.css delete mode 100644 public/dashboard/assets/index-DxUg_xTw.css create mode 100644 public/dashboard/assets/index-WkaLPu9d.js diff --git a/API/server.js b/API/server.js index b58bc4e..29b12aa 100644 --- a/API/server.js +++ b/API/server.js @@ -27,11 +27,16 @@ const { } = require('../Structures/DashboardSession.js'); const { OAUTH_STATE_COOKIE, + OAUTH_REDIRECT_COOKIE, + getTrustProxySetting, + resolveOAuthRedirectUri, + isSecureRequest, buildAuthorizeUrl, exchangeCode, fetchDiscordUser, createOAuthState, oauthStateCookieOptions, + oauthRedirectCookieOptions, isOAuthConfigured, discordAvatarUrl, } = require('../Structures/DiscordOAuth.js'); @@ -64,6 +69,8 @@ const { } = require('../Structures/GuildManager.js'); const { withTimeout } = require('../Structures/asyncTimeout.js'); const transcriptTemplate = require('../Structures/TranscriptTemplate.js'); +const transcriptStore = require('../Structures/TranscriptStore.js'); +const dashboardBranding = require('../Structures/DashboardBranding.js'); const { listCatalogAddons, listInstalledAddons, @@ -96,9 +103,40 @@ class APIServer { this.oauth = this.config.OAuth || {}; bootstrapFromOAuth(this.oauth); + const trustProxy = getTrustProxySetting(this.config); + if (trustProxy !== false) { + this.app.set('trust proxy', trustProxy); + } + this.app.use(cors({ origin: true, credentials: true })); this.app.use(cookieParser()); - this.app.use(express.json()); + this.app.use(express.json({ limit: '2mb' })); + + this.app.get('/api/branding', (req, res) => { + try { + const branding = dashboardBranding.readBranding(); + res.json({ + success: true, + data: dashboardBranding.publicPayload(branding), + }); + } catch (err) { + res.status(500).json({ success: false, error: 'Failed to load branding' }); + } + }); + + this.app.get('/api/branding/favicon', (req, res) => { + try { + const favicon = dashboardBranding.getFaviconPath(); + if (!favicon) { + return res.status(404).end(); + } + res.setHeader('Cache-Control', 'public, max-age=300'); + res.type(favicon.mime); + return res.sendFile(favicon.filePath); + } catch (err) { + return res.status(500).end(); + } + }); this.app.get('/api/health', (req, res) => { const guild = @@ -115,6 +153,10 @@ class APIServer { this.setupAuthRoutes(); + this.app.get('/transcripts/:id', (req, res) => { + transcriptStore.servePublicTranscript(req, res); + }); + const api = express.Router(); api.use((req, res, next) => this.authenticate(req, res, next)); this.apiRouter = api; @@ -203,15 +245,20 @@ class APIServer { setupAuthRoutes() { const secret = this.config.SecretKey; const oauth = this.oauth; + const apiConfig = this.config; this.app.get('/api/auth/discord', (req, res) => { if (!isOAuthConfigured(oauth)) { return res.redirect('/login?error=oauth_not_configured'); } + const redirectUri = resolveOAuthRedirectUri(req, oauth, apiConfig); const state = createOAuthState(); - res.cookie(OAUTH_STATE_COOKIE, state, oauthStateCookieOptions()); - res.redirect(buildAuthorizeUrl(oauth, state)); + const cookieOpts = oauthStateCookieOptions(req, oauth, apiConfig); + + res.cookie(OAUTH_STATE_COOKIE, state, cookieOpts); + res.cookie(OAUTH_REDIRECT_COOKIE, redirectUri, oauthRedirectCookieOptions(req, oauth, apiConfig)); + res.redirect(buildAuthorizeUrl(oauth, state, redirectUri)); }); this.app.get('/api/auth/discord/callback', async (req, res) => { @@ -228,14 +275,21 @@ class APIServer { } const savedState = req.cookies?.[OAUTH_STATE_COOKIE]; - res.clearCookie(OAUTH_STATE_COOKIE, { path: '/' }); + const savedRedirect = req.cookies?.[OAUTH_REDIRECT_COOKIE]; + const cookieOpts = oauthStateCookieOptions(req, oauth, apiConfig); + + res.clearCookie(OAUTH_STATE_COOKIE, { path: '/', secure: cookieOpts.secure }); + res.clearCookie(OAUTH_REDIRECT_COOKIE, { path: '/', secure: cookieOpts.secure }); if (!savedState || savedState !== state) { return res.redirect('/login?error=invalid_state'); } + const redirectUri = + savedRedirect || resolveOAuthRedirectUri(req, oauth, apiConfig); + try { - const accessToken = await exchangeCode(oauth, String(code)); + const accessToken = await exchangeCode(oauth, String(code), redirectUri); const user = await fetchDiscordUser(accessToken); if (!canLogin(user.id, oauth)) { @@ -250,7 +304,13 @@ class APIServer { const payload = createSessionPayload(user); const token = signSession(payload, secret); - res.cookie(SESSION_COOKIE, token, sessionCookieOptions()); + res.cookie( + SESSION_COOKIE, + token, + sessionCookieOptions(undefined, { + secure: isSecureRequest(req, oauth, apiConfig), + }), + ); res.redirect('/'); } catch (err) { console.error('[API] Discord OAuth callback failed:', err.message); @@ -333,6 +393,7 @@ h1{font-size:1.25rem}a{color:#a78bfa} } const listenPort = port || this.config.Port || 3000; + transcriptStore.startAutoDeleteScheduler(); this._httpServer = this.app.listen(listenPort, () => { console.log(`[API] Server running on port ${listenPort}`); this.startNotificationJobs(); @@ -403,6 +464,61 @@ h1{font-size:1.25rem}a{color:#a78bfa} const p = (permission) => this.requirePermission(permission); const cv = (mode) => this.requireConfigAccess(mode); + app.put('/branding', p('settings.update'), (req, res) => { + try { + const branding = dashboardBranding.updateBranding({ + title: req.body?.title, + pageTitle: req.body?.pageTitle, + }); + res.json({ + success: true, + data: dashboardBranding.publicPayload(branding), + message: 'Branding saved.', + }); + } catch (err) { + res.status(400).json({ success: false, error: err.message }); + } + }); + + app.post('/branding/favicon', p('settings.update'), (req, res) => { + try { + const branding = dashboardBranding.saveFaviconFromBase64(req.body?.dataUrl); + res.json({ + success: true, + data: dashboardBranding.publicPayload(branding), + message: 'Favicon updated.', + }); + } catch (err) { + res.status(400).json({ success: false, error: err.message }); + } + }); + + app.delete('/branding/favicon', p('settings.update'), (req, res) => { + try { + const branding = dashboardBranding.clearFavicon(); + res.json({ + success: true, + data: dashboardBranding.publicPayload(branding), + message: 'Favicon removed.', + }); + } catch (err) { + res.status(400).json({ success: false, error: err.message }); + } + }); + + app.post('/branding/reset', p('settings.update'), (req, res) => { + try { + const branding = dashboardBranding.resetBranding(); + res.json({ + success: true, + data: dashboardBranding.publicPayload(branding), + message: 'Branding reset to defaults.', + }); + } catch (err) { + res.status(400).json({ success: false, error: err.message }); + } + }); + app.get('/dashboard-users', p('users.view'), (req, res) => { try { res.json({ @@ -1079,22 +1195,11 @@ h1{font-size:1.25rem}a{color:#a78bfa} }); // --- Transcript API --- - const transcriptDir = path.join(__dirname, '../Data/Transcripts'); - - const readTranscriptFile = (rawId) => { - const id = String(rawId).replace(/[^0-9]/g, ''); - if (!id) return null; - const filePath = path.join(transcriptDir, `${id}-transcript.html`); - if (!fs.existsSync(filePath)) return null; - const content = fs.readFileSync(filePath, 'utf8'); - const titleMatch = content.match(/\s*Transcript\s*-\s*([^<]+)\s*<\/title>/i); - return { - id, - filePath, - filename: `${id}-transcript.html`, - content, - ticketName: titleMatch ? titleMatch[1].trim() : null, - }; + const canManageTranscripts = (req) => { + if (req.dashboardService) return true; + if (!hasPermission(req.dashboardPermissions, 'transcripts')) return false; + if (req.dashboardRole === 'viewer') return false; + return true; }; const canEditTranscriptTemplate = (req) => { @@ -1139,36 +1244,86 @@ h1{font-size:1.25rem}a{color:#a78bfa}</style></head><body> } }); + app.get('/system/transcript-settings', p('transcripts'), (req, res) => { + try { + const settings = transcriptStore.getSettings(); + res.json({ + success: true, + data: { + ...settings, + publicBasePath: '/transcripts', + }, + }); + } catch (err) { + res.status(500).json({ success: false, error: 'Failed to load transcript settings' }); + } + }); + + app.put('/system/transcript-settings', p('transcripts'), (req, res) => { + if (!canManageTranscripts(req)) { + return forbid(res); + } + try { + const saved = transcriptStore.saveSettings(req.body); + res.json({ + success: true, + data: { + ...saved, + publicBasePath: '/transcripts', + }, + message: 'Transcript settings saved.', + }); + } catch (err) { + res.status(500).json({ success: false, error: err.message || 'Failed to save settings' }); + } + }); + + app.post('/system/transcripts/purge', p('transcripts'), (req, res) => { + if (!canManageTranscripts(req)) { + return forbid(res); + } + try { + const result = transcriptStore.purgeExpiredTranscripts(); + res.json({ + success: true, + data: result, + message: result.skipped + ? 'Auto-delete is disabled. Enable it in settings to purge old transcripts.' + : `Removed ${result.deleted} transcript(s).`, + }); + } catch (err) { + res.status(500).json({ success: false, error: err.message || 'Purge failed' }); + } + }); + app.get('/system/transcripts', p('transcripts'), (req, res) => { try { - if (!fs.existsSync(transcriptDir)) { - return res.json({ success: true, data: [] }); - } - const files = fs.readdirSync(transcriptDir); - const transcripts = files - .filter((f) => f.endsWith('-transcript.html')) - .map((f) => { - const id = f.replace('-transcript.html', ''); - const stats = fs.statSync(path.join(transcriptDir, f)); - const parsed = readTranscriptFile(id); - return { - id, - filename: f, - createdAt: stats.mtime.toISOString(), - size: stats.size, - ticketName: parsed?.ticketName ?? null, - }; - }) - .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + transcriptStore.purgeExpiredTranscripts(); + const transcripts = transcriptStore.listTranscripts(); res.json({ success: true, data: transcripts }); } catch (err) { res.status(500).json({ success: false, error: 'Failed to list transcripts' }); } }); + app.delete('/system/transcripts/:id', p('transcripts'), (req, res) => { + if (!canManageTranscripts(req)) { + return forbid(res); + } + try { + const removed = transcriptStore.deleteTranscript(req.params.id); + if (!removed) { + return res.status(404).json({ success: false, error: 'Transcript not found' }); + } + res.json({ success: true, message: 'Transcript deleted.' }); + } catch (err) { + res.status(500).json({ success: false, error: 'Failed to delete transcript' }); + } + }); + app.get('/system/transcripts/:id', p('transcripts'), (req, res) => { try { - const transcript = readTranscriptFile(req.params.id); + const transcript = transcriptStore.readTranscriptFile(req.params.id); if (!transcript) { return res.status(404).json({ success: false, error: 'Transcript not found' }); } @@ -1180,7 +1335,7 @@ h1{font-size:1.25rem}a{color:#a78bfa}</style></head><body> app.get('/system/transcripts/:id/view', p('transcripts'), (req, res) => { try { - const transcript = readTranscriptFile(req.params.id); + const transcript = transcriptStore.readTranscriptFile(req.params.id); if (!transcript) { return res.status(404).type('html').send( '<!DOCTYPE html><body style="font-family:sans-serif;padding:2rem;background:#0b0f14;color:#e6edf3"><h1>Transcript not found</h1></body>', @@ -1198,7 +1353,7 @@ h1{font-size:1.25rem}a{color:#a78bfa}</style></head><body> app.get('/system/transcripts/:id/download', p('transcripts'), (req, res) => { try { - const transcript = readTranscriptFile(req.params.id); + const transcript = transcriptStore.readTranscriptFile(req.params.id); if (!transcript) { return res.status(404).json({ success: false, error: 'Transcript not found' }); } diff --git a/Configs/api.yml.example b/Configs/api.yml.example index 17a4097..0a35d20 100644 --- a/Configs/api.yml.example +++ b/Configs/api.yml.example @@ -9,11 +9,22 @@ API: SecretKey: "CHANGE_ME" + # Set to 1 when the dashboard is behind nginx, Cloudflare, Pterodactyl proxy, etc. + # so http/https is detected from X-Forwarded-Proto. + TrustProxy: false + OAuth: Enabled: true ClientId: "DISCORD_CLIENT_ID" ClientSecret: "DISCORD_CLIENT_SECRET" + # Canonical callback URL (host + path). Protocol (http/https) can follow the + # browser when UseRequestOrigin is true (default). RedirectUri: "http://localhost:3000/api/auth/discord/callback" + # Optional: register extra callback URLs in Discord and list them here. + # RedirectUris: + # - "https://bot.example.com/api/auth/discord/callback" + # UseRequestOrigin: true + # CookieSecure: false OwnerUserIds: - "YOUR_DISCORD_USER_ID" AllowedUserIds: [] diff --git a/Configs/supportbot.yml b/Configs/supportbot.yml index 448eef0..4fe2310 100644 --- a/Configs/supportbot.yml +++ b/Configs/supportbot.yml @@ -12,7 +12,7 @@ # # SupportBot Configuration (supportbot.yml) -SupportBot_Version: "v26.2.0" +SupportBot_Version: "v26.3.0" # ------------------------- # General diff --git a/Data/Transcripts/1505240742992548034-transcript.html b/Data/Transcripts/1505240742992548034-transcript.html deleted file mode 100644 index b661cc6..0000000 --- a/Data/Transcripts/1505240742992548034-transcript.html +++ /dev/null @@ -1,488 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Transcript - ticket-0004 - - - -
-
-
-
-

Ticket Transcript

-

Exported conversation log for support ticket ticket-0004

-
-
Closed Transcript
-
- -
-
-
Channel
-
ticket-0004
-
-
-
Ticket ID
-
1505240742992548034
-
-
-
Message Count
-
8
-
-
-
Close Reason
-
No Reason Provided.
-
-
-
- -
-
Messages
- -
- Avatar -
-
- BLITZ - 1289713673123922032 - 16 May 2026, 17:09 -
- -
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
sd
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
sdaf
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
dsf
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
eefw
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
dsf
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
ef
- - - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:09 -
- -
dsf
- - - -
-
- -
-
- - \ No newline at end of file diff --git a/Data/Transcripts/1505247252829180018-transcript.html b/Data/Transcripts/1505247252829180018-transcript.html deleted file mode 100644 index 76aa008..0000000 --- a/Data/Transcripts/1505247252829180018-transcript.html +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - Transcript - ticket-0005 - - - - -
-
-
-
-

Ticket Transcript

-

Exported conversation log for support ticket ticket-0005

-
-
Closed Transcript
-
- -
-
-
Ticket Name
-
ticket-0005
-
-
-
Ticket ID
-
1505247252829180018
-
-
-
Message Count
-
8
-
-
-
Close Reason
-
No Reason Provided.
-
-
- -
- -
-
Messages
- -
- Avatar -
-
- BLITZ - 1289713673123922032 - 16 May 2026, 17:35 -
-
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
fdg
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
fdg
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
df
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
gfdg
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
df
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
gdfg
- - -
-
- -
- Avatar -
-
- ch_ar. - 829112572816130058 - 16 May 2026, 17:35 -
-
fdg
- - -
-
- -
-
- - \ No newline at end of file diff --git a/Data/transcript-settings.json b/Data/transcript-settings.json new file mode 100644 index 0000000..f42e896 --- /dev/null +++ b/Data/transcript-settings.json @@ -0,0 +1,9 @@ +{ + "autoDelete": { + "enabled": false, + "afterDays": 30 + }, + "publicAccess": { + "enabled": false + } +} \ No newline at end of file diff --git a/Logs/Error/Error-2026-05-16.log b/Logs/Error/Error-2026-05-16.log deleted file mode 100644 index c326e32..0000000 --- a/Logs/Error/Error-2026-05-16.log +++ /dev/null @@ -1,4 +0,0 @@ -[2026-05-16T16:08:27.722Z] Error handling ticket close: Error: Transcript log channel missing or inaccessible. -[2026-05-16T16:09:37.993Z] Error handling ticket close: Error: Transcript log channel missing or inaccessible. -[2026-05-16T16:10:44.033Z] Error handling ticket close: Error: Transcript log channel missing or inaccessible. -[2026-05-16T16:34:05.434Z] Error handling ticket close: Error: Transcript log channel missing or inaccessible. diff --git a/Logs/Output/Output-2026-05-16.log b/Logs/Output/Output-2026-05-16.log deleted file mode 100644 index 49037d8..0000000 --- a/Logs/Output/Output-2026-05-16.log +++ /dev/null @@ -1,323 +0,0 @@ -[2026-05-16T16:05:18.835Z] [API] Server running on port 3000 -[2026-05-16T16:05:19.572Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:05:19.573Z] SupportBot -[2026-05-16T16:05:19.573Z] Emerald Services -[2026-05-16T16:05:19.574Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:05:19.574Z] -System -[2026-05-16T16:05:19.575Z] ------ -[2026-05-16T16:05:19.576Z] Bot Name  SupportBo -[2026-05-16T16:05:19.576Z] Version  v26.1.1 -[2026-05-16T16:05:19.576Z] Node.js  v24.14.1 -[2026-05-16T16:05:19.577Z] Discord.js  v14.26.4 -[2026-05-16T16:05:19.577Z] Logged in as  BLITZ#7183 -[2026-05-16T16:05:19.578Z] Guilds  1 -[2026-05-16T16:05:19.578Z] -Links -[2026-05-16T16:05:19.579Z] ----- -[2026-05-16T16:05:19.580Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:05:19.580Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:05:19.581Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:05:19.581Z] -[2026-05-16T16:05:19.582Z] [Dashboard] Discord bot is ready — full stats and guild tools are available -[2026-05-16T16:05:19.584Z] -Config Check -[2026-05-16T16:05:19.585Z] ------------ -[2026-05-16T16:05:19.587Z] ! Missing roles (ROLE_ID, ROLE_ID, ROLE_ID) -[2026-05-16T16:05:19.587Z] ! Missing channels (1469028381638000725) -[2026-05-16T16:05:19.588Z] -[2026-05-16T16:05:19.975Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:06:05.176Z]  [Executed]  ch_ar. has executed ticketpanel -[2026-05-16T16:06:05.178Z] [TICKET PANEL] TicketHome channel not found. Please check your config file. -[2026-05-16T16:06:40.773Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:06:41.788Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:06:41.788Z] SupportBot -[2026-05-16T16:06:41.789Z] Emerald Services -[2026-05-16T16:06:41.789Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:06:41.789Z] -System -[2026-05-16T16:06:41.789Z] ------ -[2026-05-16T16:06:41.790Z] Bot Name  SupportBo -[2026-05-16T16:06:41.790Z] Version  v26.1.1 -[2026-05-16T16:06:41.790Z] Node.js  v24.14.1 -[2026-05-16T16:06:41.791Z] Discord.js  v14.26.4 -[2026-05-16T16:06:41.791Z] Logged in as  BLITZ#7183 -[2026-05-16T16:06:41.792Z] Guilds  1 -[2026-05-16T16:06:41.792Z] -Links -[2026-05-16T16:06:41.792Z] ----- -[2026-05-16T16:06:41.793Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:06:41.794Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:06:41.796Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:06:41.797Z] -[2026-05-16T16:06:41.798Z] -Config Check -[2026-05-16T16:06:41.799Z] ------------ -[2026-05-16T16:06:41.799Z] ! Missing roles (ROLE_ID, ROLE_ID, ROLE_ID) -[2026-05-16T16:06:41.800Z] -[2026-05-16T16:06:42.207Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:06:42.208Z] [BotReload] Bot restarted successfully -[2026-05-16T16:06:46.079Z]  [Executed]  ch_ar. has executed ticketpanel -[2026-05-16T16:07:13.971Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:07:14.625Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:07:14.626Z] SupportBot -[2026-05-16T16:07:14.626Z] Emerald Services -[2026-05-16T16:07:14.627Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:07:14.627Z] -System -[2026-05-16T16:07:14.629Z] ------ -[2026-05-16T16:07:14.629Z] Bot Name  SupportBo -[2026-05-16T16:07:14.630Z] Version  v26.1.1 -[2026-05-16T16:07:14.630Z] Node.js  v24.14.1 -[2026-05-16T16:07:14.630Z] Discord.js  v14.26.4 -[2026-05-16T16:07:14.631Z] Logged in as  BLITZ#7183 -[2026-05-16T16:07:14.631Z] Guilds  1 -[2026-05-16T16:07:14.632Z] -Links -[2026-05-16T16:07:14.632Z] ----- -[2026-05-16T16:07:14.633Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:07:14.634Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:07:14.634Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:07:14.635Z] -[2026-05-16T16:07:14.636Z] -Config Check -[2026-05-16T16:07:14.637Z] ------------ -[2026-05-16T16:07:14.638Z] ✓ All configurations validated successfully -[2026-05-16T16:07:14.639Z] -[2026-05-16T16:07:14.949Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:07:14.950Z] [BotReload] Bot restarted successfully -[2026-05-16T16:07:53.691Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:07:54.472Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:07:54.473Z] SupportBot -[2026-05-16T16:07:54.473Z] Emerald Services -[2026-05-16T16:07:54.473Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:07:54.474Z] -System -[2026-05-16T16:07:54.474Z] ------ -[2026-05-16T16:07:54.474Z] Bot Name  SupportBo -[2026-05-16T16:07:54.475Z] Version  v26.1.1 -[2026-05-16T16:07:54.475Z] Node.js  v24.14.1 -[2026-05-16T16:07:54.476Z] Discord.js  v14.26.4 -[2026-05-16T16:07:54.476Z] Logged in as  BLITZ#7183 -[2026-05-16T16:07:54.476Z] Guilds  1 -[2026-05-16T16:07:54.477Z] -Links -[2026-05-16T16:07:54.477Z] ----- -[2026-05-16T16:07:54.478Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:07:54.479Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:07:54.479Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:07:54.480Z] -[2026-05-16T16:07:54.480Z] -Config Check -[2026-05-16T16:07:54.481Z] ------------ -[2026-05-16T16:07:54.481Z] ✓ All configurations validated successfully -[2026-05-16T16:07:54.482Z] -[2026-05-16T16:07:54.774Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:07:54.774Z] [BotReload] Bot restarted successfully -[2026-05-16T16:09:08.559Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:09:09.098Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:09:09.099Z] SupportBot -[2026-05-16T16:09:09.101Z] Emerald Services -[2026-05-16T16:09:09.101Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:09:09.102Z] -System -[2026-05-16T16:09:09.102Z] ------ -[2026-05-16T16:09:09.103Z] Bot Name  SupportBo -[2026-05-16T16:09:09.103Z] Version  v26.1.1 -[2026-05-16T16:09:09.104Z] Node.js  v24.14.1 -[2026-05-16T16:09:09.104Z] Discord.js  v14.26.4 -[2026-05-16T16:09:09.105Z] Logged in as  BLITZ#7183 -[2026-05-16T16:09:09.105Z] Guilds  1 -[2026-05-16T16:09:09.105Z] -Links -[2026-05-16T16:09:09.106Z] ----- -[2026-05-16T16:09:09.107Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:09:09.107Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:09:09.108Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:09:09.109Z] -[2026-05-16T16:09:09.110Z] -Config Check -[2026-05-16T16:09:09.111Z] ------------ -[2026-05-16T16:09:09.112Z] ✓ All configurations validated successfully -[2026-05-16T16:09:09.112Z] -[2026-05-16T16:09:09.434Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:09:09.435Z] [BotReload] Bot restarted successfully -[2026-05-16T16:10:24.089Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:10:24.662Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:10:24.662Z] SupportBot -[2026-05-16T16:10:24.663Z] Emerald Services -[2026-05-16T16:10:24.663Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:10:24.664Z] -System -[2026-05-16T16:10:24.664Z] ------ -[2026-05-16T16:10:24.664Z] Bot Name  SupportBo -[2026-05-16T16:10:24.665Z] Version  v26.1.1 -[2026-05-16T16:10:24.665Z] Node.js  v24.14.1 -[2026-05-16T16:10:24.666Z] Discord.js  v14.26.4 -[2026-05-16T16:10:24.666Z] Logged in as  BLITZ#7183 -[2026-05-16T16:10:24.666Z] Guilds  1 -[2026-05-16T16:10:24.667Z] -Links -[2026-05-16T16:10:24.667Z] ----- -[2026-05-16T16:10:24.668Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:10:24.670Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:10:24.671Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:10:24.671Z] -[2026-05-16T16:10:24.672Z] -Config Check -[2026-05-16T16:10:24.673Z] ------------ -[2026-05-16T16:10:24.674Z] ✓ All configurations validated successfully -[2026-05-16T16:10:24.675Z] -[2026-05-16T16:10:24.951Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:10:24.952Z] [BotReload] Bot restarted successfully -[2026-05-16T16:21:37.767Z] [API] Server running on port 3000 -[2026-05-16T16:21:38.731Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:21:38.731Z] SupportBot -[2026-05-16T16:21:38.732Z] Emerald Services -[2026-05-16T16:21:38.732Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:21:38.733Z] -System -[2026-05-16T16:21:38.733Z] ------ -[2026-05-16T16:21:38.733Z] Bot Name  SupportBo -[2026-05-16T16:21:38.734Z] Version  v26.1.1 -[2026-05-16T16:21:38.734Z] Node.js  v24.14.1 -[2026-05-16T16:21:38.735Z] Discord.js  v14.26.4 -[2026-05-16T16:21:38.735Z] Logged in as  BLITZ#7183 -[2026-05-16T16:21:38.736Z] Guilds  1 -[2026-05-16T16:21:38.736Z] -Links -[2026-05-16T16:21:38.737Z] ----- -[2026-05-16T16:21:38.737Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:21:38.738Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:21:38.738Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:21:38.739Z] -[2026-05-16T16:21:38.740Z] [Dashboard] Discord bot is ready — full stats and guild tools are available -[2026-05-16T16:21:38.741Z] -Config Check -[2026-05-16T16:21:38.741Z] ------------ -[2026-05-16T16:21:38.743Z] ✓ All configurations validated successfully -[2026-05-16T16:21:38.745Z] -[2026-05-16T16:21:38.986Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:22:10.150Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:22:10.821Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:22:10.821Z] SupportBot -[2026-05-16T16:22:10.822Z] Emerald Services -[2026-05-16T16:22:10.822Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:22:10.822Z] -System -[2026-05-16T16:22:10.823Z] ------ -[2026-05-16T16:22:10.823Z] Bot Name  SupportBo -[2026-05-16T16:22:10.823Z] Version  v26.1.1 -[2026-05-16T16:22:10.824Z] Node.js  v24.14.1 -[2026-05-16T16:22:10.825Z] Discord.js  v14.26.4 -[2026-05-16T16:22:10.826Z] Logged in as  BLITZ#7183 -[2026-05-16T16:22:10.827Z] Guilds  1 -[2026-05-16T16:22:10.827Z] -Links -[2026-05-16T16:22:10.828Z] ----- -[2026-05-16T16:22:10.828Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:22:10.829Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:22:10.829Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:22:10.830Z] -[2026-05-16T16:22:10.831Z] -Config Check -[2026-05-16T16:22:10.831Z] ------------ -[2026-05-16T16:22:10.832Z] ✓ All configurations validated successfully -[2026-05-16T16:22:10.832Z] -[2026-05-16T16:22:11.140Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:22:11.140Z] [BotReload] Bot restarted successfully -[2026-05-16T16:28:25.190Z] [API] Server running on port 3000 -[2026-05-16T16:28:26.233Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:28:26.234Z] SupportBot -[2026-05-16T16:28:26.234Z] Emerald Services -[2026-05-16T16:28:26.235Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:28:26.235Z] -System -[2026-05-16T16:28:26.236Z] ------ -[2026-05-16T16:28:26.236Z] Bot Name  SupportBo -[2026-05-16T16:28:26.237Z] Version  v26.1.1 -[2026-05-16T16:28:26.237Z] Node.js  v24.14.1 -[2026-05-16T16:28:26.238Z] Discord.js  v14.26.4 -[2026-05-16T16:28:26.238Z] Logged in as  BLITZ#7183 -[2026-05-16T16:28:26.240Z] Guilds  1 -[2026-05-16T16:28:26.241Z] -Links -[2026-05-16T16:28:26.241Z] ----- -[2026-05-16T16:28:26.242Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:28:26.243Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:28:26.243Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:28:26.244Z] -[2026-05-16T16:28:26.244Z] [Dashboard] Discord bot is ready — full stats and guild tools are available -[2026-05-16T16:28:26.248Z] -Config Check -[2026-05-16T16:28:26.250Z] ------------ -[2026-05-16T16:28:26.252Z] ✓ All configurations validated successfully -[2026-05-16T16:28:26.252Z] -[2026-05-16T16:28:26.661Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:32:06.353Z] [API] Server running on port 3000 -[2026-05-16T16:32:07.061Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:32:07.062Z] SupportBot -[2026-05-16T16:32:07.063Z] Emerald Services -[2026-05-16T16:32:07.063Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:32:07.064Z] -System -[2026-05-16T16:32:07.065Z] ------ -[2026-05-16T16:32:07.065Z] Bot Name  SupportBo -[2026-05-16T16:32:07.065Z] Version  v26.1.1 -[2026-05-16T16:32:07.066Z] Node.js  v24.14.1 -[2026-05-16T16:32:07.066Z] Discord.js  v14.26.4 -[2026-05-16T16:32:07.067Z] Logged in as  BLITZ#7183 -[2026-05-16T16:32:07.067Z] Guilds  1 -[2026-05-16T16:32:07.067Z] -Links -[2026-05-16T16:32:07.068Z] ----- -[2026-05-16T16:32:07.068Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:32:07.069Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:32:07.070Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:32:07.070Z] -[2026-05-16T16:32:07.071Z] [Dashboard] Discord bot is ready — full stats and guild tools are available -[2026-05-16T16:32:07.073Z] -Config Check -[2026-05-16T16:32:07.074Z] ------------ -[2026-05-16T16:32:07.076Z] ✓ All configurations validated successfully -[2026-05-16T16:32:07.076Z] -[2026-05-16T16:32:07.631Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:34:35.063Z] [BotReload] Restarting Discord bot after config change… -[2026-05-16T16:34:35.633Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:34:35.633Z] SupportBot -[2026-05-16T16:34:35.634Z] Emerald Services -[2026-05-16T16:34:35.634Z] ──────────────────────────────────────────────────────── -[2026-05-16T16:34:35.634Z] -System -[2026-05-16T16:34:35.635Z] ------ -[2026-05-16T16:34:35.635Z] Bot Name  SupportBo -[2026-05-16T16:34:35.636Z] Version  v26.1.1 -[2026-05-16T16:34:35.636Z] Node.js  v24.14.1 -[2026-05-16T16:34:35.636Z] Discord.js  v14.26.4 -[2026-05-16T16:34:35.637Z] Logged in as  BLITZ#7183 -[2026-05-16T16:34:35.637Z] Guilds  1 -[2026-05-16T16:34:35.637Z] -Links -[2026-05-16T16:34:35.638Z] ----- -[2026-05-16T16:34:35.638Z] Docs  https://emerald-services.gitbook.io/ -[2026-05-16T16:34:35.639Z] Discord  https://dsc.gg/emerald-dev -[2026-05-16T16:34:35.639Z] Invite  https://discord.com/api/oauth2/authorize?client_id=1289713673123922032&permissions=8&scope=bot%20applications.commands -[2026-05-16T16:34:35.639Z] -[2026-05-16T16:34:35.640Z] -Config Check -[2026-05-16T16:34:35.641Z] ------------ -[2026-05-16T16:34:35.642Z] ✓ All configurations validated successfully -[2026-05-16T16:34:35.642Z] -[2026-05-16T16:34:35.923Z] -✓ Slash Commands Registered for Char's Test Server -[2026-05-16T16:34:35.924Z] [BotReload] Bot restarted successfully diff --git a/README.md b/README.md index 4630bb3..a398996 100644 --- a/README.md +++ b/README.md @@ -125,11 +125,11 @@ API: **Discord Developer Portal** 1. Create an application at [discord.com/developers/applications](https://discord.com/developers/applications) -2. Under **OAuth2 → Redirects**, add the exact `RedirectUri` (e.g. `http://localhost:3000/api/auth/discord/callback`) +2. Under **OAuth2 → Redirects**, add your callback URL(s). If users may sign in over **both** HTTP and HTTPS, register **both** (same host/path, different protocol), e.g. `http://bot.example.com/api/auth/discord/callback` and `https://bot.example.com/api/auth/discord/callback`. 3. Use the same app’s **Client ID** and **Client Secret** in `api.yml` 4. Put your Discord user ID in `OwnerUserIds` for full dashboard access -For production, change `RedirectUri` to your public URL (e.g. `https://bot.example.com/api/auth/discord/callback`) and use HTTPS. +Set `RedirectUri` to your usual URL (host and path). By default, `UseRequestOrigin` keeps the **same protocol** as the page you opened (http vs https). Behind a reverse proxy, set `TrustProxy: 1` so forwarded `X-Forwarded-Proto` is respected. ### 4. Start the bot diff --git a/Structures/DashboardBranding.js b/Structures/DashboardBranding.js new file mode 100644 index 0000000..5db07e7 --- /dev/null +++ b/Structures/DashboardBranding.js @@ -0,0 +1,168 @@ +const fs = require('fs'); +const path = require('path'); + +const BRANDING_DIR = path.join(__dirname, '../Data/branding'); +const BRANDING_FILE = path.join(BRANDING_DIR, 'branding.json'); +const FAVICON_BASENAME = 'favicon'; + +const DEFAULTS = { + title: 'SupportBot', + pageTitle: 'SupportBot Dashboard', + faviconFile: null, + faviconMime: null, + updatedAt: null, +}; + +const ALLOWED_MIME = new Set([ + 'image/png', + 'image/jpeg', + 'image/x-icon', + 'image/vnd.microsoft.icon', + 'image/svg+xml', + 'image/webp', +]); + +const MIME_EXT = { + 'image/png': '.png', + 'image/jpeg': '.jpg', + 'image/x-icon': '.ico', + 'image/vnd.microsoft.icon': '.ico', + 'image/svg+xml': '.svg', + 'image/webp': '.webp', +}; + +function ensureDir() { + if (!fs.existsSync(BRANDING_DIR)) { + fs.mkdirSync(BRANDING_DIR, { recursive: true }); + } +} + +function readBranding() { + ensureDir(); + if (!fs.existsSync(BRANDING_FILE)) { + return { ...DEFAULTS }; + } + try { + const raw = JSON.parse(fs.readFileSync(BRANDING_FILE, 'utf8')); + return { + title: String(raw.title || DEFAULTS.title).slice(0, 64), + pageTitle: String(raw.pageTitle || DEFAULTS.pageTitle).slice(0, 80), + faviconFile: raw.faviconFile || null, + faviconMime: raw.faviconMime || null, + updatedAt: raw.updatedAt || null, + }; + } catch { + return { ...DEFAULTS }; + } +} + +function writeBranding(data) { + ensureDir(); + const next = { + title: String(data.title || DEFAULTS.title).slice(0, 64), + pageTitle: String(data.pageTitle || DEFAULTS.pageTitle).slice(0, 80), + faviconFile: data.faviconFile ?? null, + faviconMime: data.faviconMime ?? null, + updatedAt: Date.now(), + }; + fs.writeFileSync(BRANDING_FILE, JSON.stringify(next, null, 2), 'utf8'); + return next; +} + +function publicPayload(branding) { + const hasFavicon = Boolean(branding.faviconFile); + return { + title: branding.title, + pageTitle: branding.pageTitle, + hasFavicon, + faviconUrl: hasFavicon ? '/api/branding/favicon' : null, + updatedAt: branding.updatedAt, + }; +} + +function updateBranding({ title, pageTitle }) { + const current = readBranding(); + return writeBranding({ + ...current, + title: title !== undefined ? title : current.title, + pageTitle: pageTitle !== undefined ? pageTitle : current.pageTitle, + }); +} + +function removeExistingFavicons() { + if (!fs.existsSync(BRANDING_DIR)) return; + for (const file of fs.readdirSync(BRANDING_DIR)) { + if (file.startsWith(`${FAVICON_BASENAME}.`)) { + fs.unlinkSync(path.join(BRANDING_DIR, file)); + } + } +} + +function saveFaviconFromBase64(dataUrl) { + const match = String(dataUrl || '').match( + /^data:([\w/+.-]+);base64,([A-Za-z0-9+/=]+)$/, + ); + if (!match) { + throw new Error('Invalid image data. Upload a PNG, ICO, SVG, or WebP file.'); + } + + const mime = match[1]; + if (!ALLOWED_MIME.has(mime)) { + throw new Error('Unsupported image type. Use PNG, ICO, SVG, JPEG, or WebP.'); + } + + const buffer = Buffer.from(match[2], 'base64'); + if (buffer.length > 512 * 1024) { + throw new Error('Favicon must be 512 KB or smaller.'); + } + + ensureDir(); + removeExistingFavicons(); + + const ext = MIME_EXT[mime] || '.png'; + const filename = `${FAVICON_BASENAME}${ext}`; + const filePath = path.join(BRANDING_DIR, filename); + fs.writeFileSync(filePath, buffer); + + const current = readBranding(); + return writeBranding({ + ...current, + faviconFile: filename, + faviconMime: mime, + }); +} + +function clearFavicon() { + removeExistingFavicons(); + const current = readBranding(); + return writeBranding({ + ...current, + faviconFile: null, + faviconMime: null, + }); +} + +function getFaviconPath() { + const branding = readBranding(); + if (!branding.faviconFile) return null; + const filePath = path.join(BRANDING_DIR, branding.faviconFile); + if (!fs.existsSync(filePath)) return null; + return { filePath, mime: branding.faviconMime || 'image/png' }; +} + +function resetBranding() { + clearFavicon(); + return writeBranding({ ...DEFAULTS }); +} + +module.exports = { + readBranding, + writeBranding, + publicPayload, + updateBranding, + saveFaviconFromBase64, + clearFavicon, + getFaviconPath, + resetBranding, + DEFAULTS, +}; diff --git a/Structures/DashboardSession.js b/Structures/DashboardSession.js index 0363701..a6e12d8 100644 --- a/Structures/DashboardSession.js +++ b/Structures/DashboardSession.js @@ -51,10 +51,14 @@ function createSessionPayload(user) { }; } -function sessionCookieOptions(maxAgeMs = SESSION_MAX_AGE_MS) { +function sessionCookieOptions(maxAgeMs = SESSION_MAX_AGE_MS, opts = {}) { + const secure = + opts.secure !== undefined + ? Boolean(opts.secure) + : process.env.NODE_ENV === "production"; return { httpOnly: true, - secure: process.env.NODE_ENV === "production", + secure, sameSite: "lax", maxAge: maxAgeMs, path: "/", diff --git a/Structures/DiscordOAuth.js b/Structures/DiscordOAuth.js index 70e5bcb..36e1175 100644 --- a/Structures/DiscordOAuth.js +++ b/Structures/DiscordOAuth.js @@ -3,7 +3,9 @@ const axios = require("axios"); const DISCORD_API = "https://discord.com/api"; const OAUTH_STATE_COOKIE = "sb_oauth_state"; +const OAUTH_REDIRECT_COOKIE = "sb_oauth_redirect"; const OAUTH_STATE_MAX_AGE_MS = 10 * 60 * 1000; +const CALLBACK_PATH = "/api/auth/discord/callback"; function discordAvatarUrl(user) { if (user.avatar) { @@ -14,10 +16,125 @@ function discordAvatarUrl(user) { return `https://cdn.discordapp.com/embed/avatars/${index}.png`; } -function buildAuthorizeUrl(oauth, state) { +function getTrustProxySetting(apiConfig) { + if (apiConfig?.TrustProxy === false) return false; + if (typeof apiConfig?.TrustProxy === "number") return apiConfig.TrustProxy; + if (apiConfig?.TrustProxy === true) return 1; + return false; +} + +function getRequestProto(req, apiConfig) { + if (apiConfig?.TrustProxy !== false) { + const forwarded = req.get("x-forwarded-proto"); + if (forwarded) { + return forwarded.split(",")[0].trim().toLowerCase(); + } + } + return (req.protocol || "http").toLowerCase(); +} + +function getRequestHost(req) { + const forwarded = req.get("x-forwarded-host"); + if (forwarded) { + return forwarded.split(",")[0].trim(); + } + return req.get("host") || ""; +} + +function getRequestOrigin(req, apiConfig) { + const proto = getRequestProto(req, apiConfig); + const host = getRequestHost(req); + if (!host) return null; + return `${proto}://${host}`; +} + +function normalizeRedirectUri(uri) { + try { + const u = new URL(uri); + u.hash = ""; + u.search = ""; + return u.toString(); + } catch { + return null; + } +} + +function redirectUriMatchesHostPath(a, b) { + try { + const ua = new URL(a); + const ub = new URL(b); + return ua.host === ub.host && ua.pathname === ub.pathname; + } catch { + return false; + } +} + +function collectConfiguredRedirectUris(oauth) { + const list = []; + if (oauth?.RedirectUri) { + const normalized = normalizeRedirectUri(oauth.RedirectUri); + if (normalized) list.push(normalized); + } + if (Array.isArray(oauth?.RedirectUris)) { + for (const entry of oauth.RedirectUris) { + const normalized = normalizeRedirectUri(entry); + if (normalized && !list.includes(normalized)) { + list.push(normalized); + } + } + } + return list; +} + +/** + * Pick the OAuth redirect_uri for this request. When UseRequestOrigin is enabled + * (default), uses the same host/path as RedirectUri but the protocol the user + * actually used (http vs https). Register every variant in the Discord portal. + */ +function resolveOAuthRedirectUri(req, oauth, apiConfig) { + const configured = collectConfiguredRedirectUris(oauth); + const fallback = configured[0] || oauth?.RedirectUri || ""; + + const useRequestOrigin = oauth?.UseRequestOrigin !== false; + if (!useRequestOrigin || !req) { + return fallback; + } + + const origin = getRequestOrigin(req, apiConfig); + if (!origin) return fallback; + + const dynamic = `${origin}${CALLBACK_PATH}`; + const normalizedDynamic = normalizeRedirectUri(dynamic); + if (!normalizedDynamic) return fallback; + + if (configured.includes(normalizedDynamic)) { + return normalizedDynamic; + } + + const matchesConfigured = configured.some((uri) => + redirectUriMatchesHostPath(uri, normalizedDynamic), + ); + if (matchesConfigured) { + return normalizedDynamic; + } + + return fallback; +} + +function isSecureRequest(req, oauth, apiConfig) { + if (oauth?.CookieSecure === true) return true; + if (oauth?.CookieSecure === false) return false; + if (req) { + if (typeof req.secure === "boolean") return req.secure; + return getRequestProto(req, apiConfig) === "https"; + } + return process.env.NODE_ENV === "production"; +} + +function buildAuthorizeUrl(oauth, state, redirectUri) { const params = new URLSearchParams({ client_id: oauth.ClientId, - redirect_uri: oauth.RedirectUri, + redirect_uri: redirectUri, response_type: "code", scope: "identify", state, @@ -26,13 +143,13 @@ function buildAuthorizeUrl(oauth, state) { return `${DISCORD_API}/oauth2/authorize?${params.toString()}`; } -async function exchangeCode(oauth, code) { +async function exchangeCode(oauth, code, redirectUri) { const body = new URLSearchParams({ client_id: oauth.ClientId, client_secret: oauth.ClientSecret, grant_type: "authorization_code", code, - redirect_uri: oauth.RedirectUri, + redirect_uri: redirectUri, }); const { data } = await axios.post(`${DISCORD_API}/oauth2/token`, body, { @@ -55,16 +172,20 @@ function createOAuthState() { return crypto.randomBytes(24).toString("hex"); } -function oauthStateCookieOptions() { +function oauthStateCookieOptions(req, oauth, apiConfig) { return { httpOnly: true, - secure: process.env.NODE_ENV === "production", + secure: isSecureRequest(req, oauth, apiConfig), sameSite: "lax", maxAge: OAUTH_STATE_MAX_AGE_MS, path: "/", }; } +function oauthRedirectCookieOptions(req, oauth, apiConfig) { + return oauthStateCookieOptions(req, oauth, apiConfig); +} + function isOAuthConfigured(oauth) { return Boolean( oauth?.Enabled && @@ -93,12 +214,18 @@ function formatPublicUser(user) { module.exports = { OAUTH_STATE_COOKIE, + OAUTH_REDIRECT_COOKIE, OAUTH_STATE_MAX_AGE_MS, + CALLBACK_PATH, + getTrustProxySetting, + resolveOAuthRedirectUri, + isSecureRequest, buildAuthorizeUrl, exchangeCode, fetchDiscordUser, createOAuthState, oauthStateCookieOptions, + oauthRedirectCookieOptions, isOAuthConfigured, isUserAllowed, formatPublicUser, diff --git a/Structures/TranscriptStore.js b/Structures/TranscriptStore.js new file mode 100644 index 0000000..ed337ec --- /dev/null +++ b/Structures/TranscriptStore.js @@ -0,0 +1,223 @@ +const fs = require('fs'); +const path = require('path'); + +const TRANSCRIPT_DIR = path.join(__dirname, '../Data/Transcripts'); +const SETTINGS_FILE = path.join(__dirname, '../Data/transcript-settings.json'); + +const DEFAULT_SETTINGS = { + autoDelete: { + enabled: false, + afterDays: 30, + }, + publicAccess: { + enabled: false, + }, +}; + +let purgeTimer = null; + +function ensureTranscriptDir() { + if (!fs.existsSync(TRANSCRIPT_DIR)) { + fs.mkdirSync(TRANSCRIPT_DIR, { recursive: true }); + } +} + +function getSettings() { + try { + if (!fs.existsSync(SETTINGS_FILE)) { + return JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); + } + const raw = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8')); + const days = Number(raw?.autoDelete?.afterDays); + return { + autoDelete: { + enabled: Boolean(raw?.autoDelete?.enabled), + afterDays: + Number.isFinite(days) && days >= 1 + ? Math.min(Math.floor(days), 3650) + : DEFAULT_SETTINGS.autoDelete.afterDays, + }, + publicAccess: { + enabled: Boolean(raw?.publicAccess?.enabled), + }, + }; + } catch { + return JSON.parse(JSON.stringify(DEFAULT_SETTINGS)); + } +} + +function saveSettings(patch) { + const current = getSettings(); + const next = { + autoDelete: { + enabled: + patch?.autoDelete?.enabled !== undefined + ? Boolean(patch.autoDelete.enabled) + : current.autoDelete.enabled, + afterDays: + patch?.autoDelete?.afterDays !== undefined + ? Math.min( + Math.max(Math.floor(Number(patch.autoDelete.afterDays) || 1), 1), + 3650, + ) + : current.autoDelete.afterDays, + }, + publicAccess: { + enabled: + patch?.publicAccess?.enabled !== undefined + ? Boolean(patch.publicAccess.enabled) + : current.publicAccess.enabled, + }, + }; + + const dir = path.dirname(SETTINGS_FILE); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(SETTINGS_FILE, JSON.stringify(next, null, 2), 'utf8'); + return next; +} + +function normalizeId(rawId) { + return String(rawId).replace(/[^0-9]/g, ''); +} + +function readTranscriptFile(rawId) { + const id = normalizeId(rawId); + if (!id) return null; + ensureTranscriptDir(); + const filePath = path.join(TRANSCRIPT_DIR, `${id}-transcript.html`); + if (!fs.existsSync(filePath)) return null; + const content = fs.readFileSync(filePath, 'utf8'); + const titleMatch = content.match(/\s*Transcript\s*-\s*([^<]+)\s*<\/title>/i); + return { + id, + filePath, + filename: `${id}-transcript.html`, + content, + ticketName: titleMatch ? titleMatch[1].trim() : null, + }; +} + +function listTranscripts() { + ensureTranscriptDir(); + if (!fs.existsSync(TRANSCRIPT_DIR)) { + return []; + } + return fs + .readdirSync(TRANSCRIPT_DIR) + .filter((f) => f.endsWith('-transcript.html')) + .map((f) => { + const id = f.replace('-transcript.html', ''); + const stats = fs.statSync(path.join(TRANSCRIPT_DIR, f)); + const parsed = readTranscriptFile(id); + return { + id, + filename: f, + createdAt: stats.mtime.toISOString(), + size: stats.size, + ticketName: parsed?.ticketName ?? null, + }; + }) + .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); +} + +function deleteTranscript(rawId) { + const id = normalizeId(rawId); + if (!id) return false; + const filePath = path.join(TRANSCRIPT_DIR, `${id}-transcript.html`); + if (!fs.existsSync(filePath)) return false; + fs.unlinkSync(filePath); + return true; +} + +function purgeExpiredTranscripts() { + const settings = getSettings(); + if (!settings.autoDelete.enabled) { + return { deleted: 0, skipped: true }; + } + + ensureTranscriptDir(); + const maxAgeMs = settings.autoDelete.afterDays * 24 * 60 * 60 * 1000; + const cutoff = Date.now() - maxAgeMs; + let deleted = 0; + + for (const f of fs.readdirSync(TRANSCRIPT_DIR)) { + if (!f.endsWith('-transcript.html')) continue; + const filePath = path.join(TRANSCRIPT_DIR, f); + const stats = fs.statSync(filePath); + if (stats.mtime.getTime() < cutoff) { + fs.unlinkSync(filePath); + deleted += 1; + } + } + + return { deleted, skipped: false }; +} + +function notFoundHtml(message = 'Transcript not found') { + return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Transcript + +

${message}

`; +} + +function servePublicTranscript(req, res) { + const settings = getSettings(); + if (!settings.publicAccess.enabled) { + return res.status(404).type('html').send(notFoundHtml('Transcript not found')); + } + + const transcript = readTranscriptFile(req.params.id); + if (!transcript) { + return res.status(404).type('html').send(notFoundHtml('Transcript not found')); + } + + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.setHeader('Cache-Control', 'public, max-age=120'); + return res.send(transcript.content); +} + +function startAutoDeleteScheduler(intervalMs = 60 * 60 * 1000) { + if (purgeTimer) return; + + const run = () => { + try { + const result = purgeExpiredTranscripts(); + if (result.deleted > 0) { + console.log( + `[Transcripts] Auto-deleted ${result.deleted} transcript(s) older than ${getSettings().autoDelete.afterDays} day(s)`, + ); + } + } catch (err) { + console.error('[Transcripts] Auto-delete failed:', err.message); + } + }; + + run(); + purgeTimer = setInterval(run, intervalMs); + if (typeof purgeTimer.unref === 'function') { + purgeTimer.unref(); + } +} + +function getPublicUrl(req, id) { + const settings = getSettings(); + if (!settings.publicAccess.enabled) return null; + const host = req.get('host'); + const proto = req.get('x-forwarded-proto') || req.protocol || 'http'; + return `${proto}://${host}/transcripts/${id}`; +} + +module.exports = { + TRANSCRIPT_DIR, + DEFAULT_SETTINGS, + getSettings, + saveSettings, + readTranscriptFile, + listTranscripts, + deleteTranscript, + purgeExpiredTranscripts, + servePublicTranscript, + startAutoDeleteScheduler, + getPublicUrl, +}; diff --git a/package-lock.json b/package-lock.json index 0c5ffa9..a3bd9c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supportbot", - "version": "26.2.0", + "version": "26.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "supportbot", - "version": "26.2.0", + "version": "26.3.0", "license": "MIT", "dependencies": { "@vitalets/google-translate-api": "^9.2.1", diff --git a/package.json b/package.json index b881256..62c0f8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supportbot", - "version": "26.2.0", + "version": "26.3.0", "description": "SupportBot is an open-sourced support system for discord. It's completely free to use with high amounts of customization to match your own brand.", "main": "index.js", "scripts": { diff --git a/public/dashboard/assets/index-DPWGusls.js b/public/dashboard/assets/index-DPWGusls.js deleted file mode 100644 index 08e7564..0000000 --- a/public/dashboard/assets/index-DPWGusls.js +++ /dev/null @@ -1,187 +0,0 @@ -function PE(t,r){for(var a=0;al[s]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))l(s);new MutationObserver(s=>{for(const c of s)if(c.type==="childList")for(const f of c.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&l(f)}).observe(document,{childList:!0,subtree:!0});function a(s){const c={};return s.integrity&&(c.integrity=s.integrity),s.referrerPolicy&&(c.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?c.credentials="include":s.crossOrigin==="anonymous"?c.credentials="omit":c.credentials="same-origin",c}function l(s){if(s.ep)return;s.ep=!0;const c=a(s);fetch(s.href,c)}})();function jb(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var Zf={exports:{}},cs={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var o1;function BE(){if(o1)return cs;o1=1;var t=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function a(l,s,c){var f=null;if(c!==void 0&&(f=""+c),s.key!==void 0&&(f=""+s.key),"key"in s){c={};for(var m in s)m!=="key"&&(c[m]=s[m])}else c=s;return s=c.ref,{$$typeof:t,type:l,key:f,ref:s!==void 0?s:null,props:c}}return cs.Fragment=r,cs.jsx=a,cs.jsxs=a,cs}var l1;function $E(){return l1||(l1=1,Zf.exports=BE()),Zf.exports}var u=$E(),Qf={exports:{}},je={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var s1;function UE(){if(s1)return je;s1=1;var t=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),a=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),s=Symbol.for("react.profiler"),c=Symbol.for("react.consumer"),f=Symbol.for("react.context"),m=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),h=Symbol.for("react.memo"),b=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),x=Symbol.iterator;function C(M){return M===null||typeof M!="object"?null:(M=x&&M[x]||M["@@iterator"],typeof M=="function"?M:null)}var E={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,j={};function N(M,D,H){this.props=M,this.context=D,this.refs=j,this.updater=H||E}N.prototype.isReactComponent={},N.prototype.setState=function(M,D){if(typeof M!="object"&&typeof M!="function"&&M!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,M,D,"setState")},N.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function _(){}_.prototype=N.prototype;function A(M,D,H){this.props=M,this.context=D,this.refs=j,this.updater=H||E}var T=A.prototype=new _;T.constructor=A,w(T,N.prototype),T.isPureReactComponent=!0;var z=Array.isArray;function P(){}var B={H:null,A:null,T:null,S:null},U=Object.prototype.hasOwnProperty;function Y(M,D,H){var X=H.ref;return{$$typeof:t,type:M,key:D,ref:X!==void 0?X:null,props:H}}function se(M,D){return Y(M.type,D,M.props)}function te(M){return typeof M=="object"&&M!==null&&M.$$typeof===t}function ce(M){var D={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(H){return D[H]})}var me=/\/+/g;function ae(M,D){return typeof M=="object"&&M!==null&&M.key!=null?ce(""+M.key):D.toString(36)}function de(M){switch(M.status){case"fulfilled":return M.value;case"rejected":throw M.reason;default:switch(typeof M.status=="string"?M.then(P,P):(M.status="pending",M.then(function(D){M.status==="pending"&&(M.status="fulfilled",M.value=D)},function(D){M.status==="pending"&&(M.status="rejected",M.reason=D)})),M.status){case"fulfilled":return M.value;case"rejected":throw M.reason}}throw M}function O(M,D,H,X,F){var oe=typeof M;(oe==="undefined"||oe==="boolean")&&(M=null);var ee=!1;if(M===null)ee=!0;else switch(oe){case"bigint":case"string":case"number":ee=!0;break;case"object":switch(M.$$typeof){case t:case r:ee=!0;break;case b:return ee=M._init,O(ee(M._payload),D,H,X,F)}}if(ee)return F=F(M),ee=X===""?"."+ae(M,0):X,z(F)?(H="",ee!=null&&(H=ee.replace(me,"$&/")+"/"),O(F,D,H,"",function(Ce){return Ce})):F!=null&&(te(F)&&(F=se(F,H+(F.key==null||M&&M.key===F.key?"":(""+F.key).replace(me,"$&/")+"/")+ee)),D.push(F)),1;ee=0;var fe=X===""?".":X+":";if(z(M))for(var ue=0;ue>>1,Z=O[re];if(0>>1;res(H,J))Xs(F,H)?(O[re]=F,O[X]=J,re=X):(O[re]=H,O[D]=J,re=D);else if(Xs(F,J))O[re]=F,O[X]=J,re=X;else break e}}return q}function s(O,q){var J=O.sortIndex-q.sortIndex;return J!==0?J:O.id-q.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var c=performance;t.unstable_now=function(){return c.now()}}else{var f=Date,m=f.now();t.unstable_now=function(){return f.now()-m}}var g=[],h=[],b=1,v=null,x=3,C=!1,E=!1,w=!1,j=!1,N=typeof setTimeout=="function"?setTimeout:null,_=typeof clearTimeout=="function"?clearTimeout:null,A=typeof setImmediate<"u"?setImmediate:null;function T(O){for(var q=a(h);q!==null;){if(q.callback===null)l(h);else if(q.startTime<=O)l(h),q.sortIndex=q.expirationTime,r(g,q);else break;q=a(h)}}function z(O){if(w=!1,T(O),!E)if(a(g)!==null)E=!0,P||(P=!0,ce());else{var q=a(h);q!==null&&de(z,q.startTime-O)}}var P=!1,B=-1,U=5,Y=-1;function se(){return j?!0:!(t.unstable_now()-YO&&se());){var re=v.callback;if(typeof re=="function"){v.callback=null,x=v.priorityLevel;var Z=re(v.expirationTime<=O);if(O=t.unstable_now(),typeof Z=="function"){v.callback=Z,T(O),q=!0;break t}v===a(g)&&l(g),T(O)}else l(g);v=a(g)}if(v!==null)q=!0;else{var M=a(h);M!==null&&de(z,M.startTime-O),q=!1}}break e}finally{v=null,x=J,C=!1}q=void 0}}finally{q?ce():P=!1}}}var ce;if(typeof A=="function")ce=function(){A(te)};else if(typeof MessageChannel<"u"){var me=new MessageChannel,ae=me.port2;me.port1.onmessage=te,ce=function(){ae.postMessage(null)}}else ce=function(){N(te,0)};function de(O,q){B=N(function(){O(t.unstable_now())},q)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(O){O.callback=null},t.unstable_forceFrameRate=function(O){0>O||125re?(O.sortIndex=J,r(h,O),a(g)===null&&O===a(h)&&(w?(_(B),B=-1):w=!0,de(z,J-re))):(O.sortIndex=Z,r(g,O),E||C||(E=!0,P||(P=!0,ce()))),O},t.unstable_shouldYield=se,t.unstable_wrapCallback=function(O){var q=x;return function(){var J=x;x=q;try{return O.apply(this,arguments)}finally{x=J}}}})(tm)),tm}var u1;function IE(){return u1||(u1=1,em.exports=HE()),em.exports}var nm={exports:{}},Pt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var d1;function VE(){if(d1)return Pt;d1=1;var t=Zc();function r(g){var h="https://react.dev/errors/"+g;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(r){console.error(r)}}return t(),nm.exports=VE(),nm.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var m1;function GE(){if(m1)return us;m1=1;var t=IE(),r=Zc(),a=Rb();function l(e){var n="https://react.dev/errors/"+e;if(1Z||(e.current=re[Z],re[Z]=null,Z--)}function H(e,n){Z++,re[Z]=e.current,e.current=n}var X=M(null),F=M(null),oe=M(null),ee=M(null);function fe(e,n){switch(H(oe,n),H(F,e),H(X,null),n.nodeType){case 9:case 11:e=(e=n.documentElement)&&(e=e.namespaceURI)?Ty(e):0;break;default:if(e=n.tagName,n=n.namespaceURI)n=Ty(n),e=Ay(n,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}D(X),H(X,e)}function ue(){D(X),D(F),D(oe)}function Ce(e){e.memoizedState!==null&&H(ee,e);var n=X.current,o=Ay(n,e.type);n!==o&&(H(F,e),H(X,o))}function Re(e){F.current===e&&(D(X),D(F)),ee.current===e&&(D(ee),os._currentValue=J)}var Ne,nt;function We(e){if(Ne===void 0)try{throw Error()}catch(o){var n=o.stack.trim().match(/\n( *(at )?)/);Ne=n&&n[1]||"",nt=-1)":-1p||k[i]!==V[p]){var K=` -`+k[i].replace(" at new "," at ");return e.displayName&&K.includes("")&&(K=K.replace("",e.displayName)),K}while(1<=i&&0<=p);break}}}finally{Ln=!1,Error.prepareStackTrace=o}return(o=e?e.displayName||e.name:"")?We(o):""}function rn(e,n){switch(e.tag){case 26:case 27:case 5:return We(e.type);case 16:return We("Lazy");case 13:return e.child!==n&&n!==null?We("Suspense Fallback"):We("Suspense");case 19:return We("SuspenseList");case 0:case 15:return yn(e.type,!1);case 11:return yn(e.type.render,!1);case 1:return yn(e.type,!0);case 31:return We("Activity");default:return""}}function va(e){try{var n="",o=null;do n+=rn(e,o),o=e,e=e.return;while(e);return n}catch(i){return` -Error generating stack: `+i.message+` -`+i.stack}}var bt=Object.prototype.hasOwnProperty,gl=t.unstable_scheduleCallback,vl=t.unstable_cancelCallback,$t=t.unstable_shouldYield,Or=t.unstable_requestPaint,Ut=t.unstable_now,Bu=t.unstable_getCurrentPriorityLevel,ya=t.unstable_ImmediatePriority,ni=t.unstable_UserBlockingPriority,ba=t.unstable_NormalPriority,yl=t.unstable_LowPriority,ir=t.unstable_IdlePriority,ri=t.log,Dr=t.unstable_setDisableYieldValue,xa=null,Ht=null;function zn(e){if(typeof ri=="function"&&Dr(e),Ht&&typeof Ht.setStrictMode=="function")try{Ht.setStrictMode(xa,e)}catch{}}var Lt=Math.clz32?Math.clz32:qn,$u=Math.log,bl=Math.LN2;function qn(e){return e>>>=0,e===0?32:31-($u(e)/bl|0)|0}var to=256,no=262144,Sa=4194304;function Xn(e){var n=e&42;if(n!==0)return n;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ee(e,n,o){var i=e.pendingLanes;if(i===0)return 0;var p=0,y=e.suspendedLanes,S=e.pingedLanes;e=e.warmLanes;var R=i&134217727;return R!==0?(i=R&~y,i!==0?p=Xn(i):(S&=R,S!==0?p=Xn(S):o||(o=R&~e,o!==0&&(p=Xn(o))))):(R=i&~y,R!==0?p=Xn(R):S!==0?p=Xn(S):o||(o=i&~e,o!==0&&(p=Xn(o)))),p===0?0:n!==0&&n!==p&&(n&y)===0&&(y=p&-p,o=n&-n,y>=o||y===32&&(o&4194048)!==0)?n:p}function Qe(e,n){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&n)===0}function xt(e,n){switch(e){case 1:case 2:case 4:case 8:case 64:return n+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return n+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function zt(){var e=Sa;return Sa<<=1,(Sa&62914560)===0&&(Sa=4194304),e}function Lr(e){for(var n=[],o=0;31>o;o++)n.push(e);return n}function tt(e,n){e.pendingLanes|=n,n!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Gt(e,n,o,i,p,y){var S=e.pendingLanes;e.pendingLanes=o,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=o,e.entangledLanes&=o,e.errorRecoveryDisabledLanes&=o,e.shellSuspendCounter=0;var R=e.entanglements,k=e.expirationTimes,V=e.hiddenUpdates;for(o=S&~o;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Mw=/[\n"\\]/g;function xn(e){return e.replace(Mw,function(n){return"\\"+n.charCodeAt(0).toString(16)+" "})}function Gu(e,n,o,i,p,y,S,R){e.name="",S!=null&&typeof S!="function"&&typeof S!="symbol"&&typeof S!="boolean"?e.type=S:e.removeAttribute("type"),n!=null?S==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+bn(n)):e.value!==""+bn(n)&&(e.value=""+bn(n)):S!=="submit"&&S!=="reset"||e.removeAttribute("value"),n!=null?Yu(e,S,bn(n)):o!=null?Yu(e,S,bn(o)):i!=null&&e.removeAttribute("value"),p==null&&y!=null&&(e.defaultChecked=!!y),p!=null&&(e.checked=p&&typeof p!="function"&&typeof p!="symbol"),R!=null&&typeof R!="function"&&typeof R!="symbol"&&typeof R!="boolean"?e.name=""+bn(R):e.removeAttribute("name")}function Ch(e,n,o,i,p,y,S,R){if(y!=null&&typeof y!="function"&&typeof y!="symbol"&&typeof y!="boolean"&&(e.type=y),n!=null||o!=null){if(!(y!=="submit"&&y!=="reset"||n!=null)){Vu(e);return}o=o!=null?""+bn(o):"",n=n!=null?""+bn(n):o,R||n===e.value||(e.value=n),e.defaultValue=n}i=i??p,i=typeof i!="function"&&typeof i!="symbol"&&!!i,e.checked=R?e.checked:!!i,e.defaultChecked=!!i,S!=null&&typeof S!="function"&&typeof S!="symbol"&&typeof S!="boolean"&&(e.name=S),Vu(e)}function Yu(e,n,o){n==="number"&&li(e.ownerDocument)===e||e.defaultValue===""+o||(e.defaultValue=""+o)}function co(e,n,o,i){if(e=e.options,n){n={};for(var p=0;p"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ku=!1;if(dr)try{var wl={};Object.defineProperty(wl,"passive",{get:function(){Ku=!0}}),window.addEventListener("test",wl,wl),window.removeEventListener("test",wl,wl)}catch{Ku=!1}var Pr=null,Zu=null,ii=null;function Ah(){if(ii)return ii;var e,n=Zu,o=n.length,i,p="value"in Pr?Pr.value:Pr.textContent,y=p.length;for(e=0;e=jl),Lh=" ",zh=!1;function Ph(e,n){switch(e){case"keyup":return o5.indexOf(n.keyCode)!==-1;case"keydown":return n.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Bh(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var po=!1;function s5(e,n){switch(e){case"compositionend":return Bh(n);case"keypress":return n.which!==32?null:(zh=!0,Lh);case"textInput":return e=n.data,e===Lh&&zh?null:e;default:return null}}function i5(e,n){if(po)return e==="compositionend"||!nd&&Ph(e,n)?(e=Ah(),ii=Zu=Pr=null,po=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(n.ctrlKey||n.altKey||n.metaKey)||n.ctrlKey&&n.altKey){if(n.char&&1=n)return{node:o,offset:n-e};e=i}e:{for(;o;){if(o.nextSibling){o=o.nextSibling;break e}o=o.parentNode}o=void 0}o=Fh(o)}}function qh(e,n){return e&&n?e===n?!0:e&&e.nodeType===3?!1:n&&n.nodeType===3?qh(e,n.parentNode):"contains"in e?e.contains(n):e.compareDocumentPosition?!!(e.compareDocumentPosition(n)&16):!1:!1}function Xh(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var n=li(e.document);n instanceof e.HTMLIFrameElement;){try{var o=typeof n.contentWindow.location.href=="string"}catch{o=!1}if(o)e=n.contentWindow;else break;n=li(e.document)}return n}function od(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return n&&(n==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||n==="textarea"||e.contentEditable==="true")}var g5=dr&&"documentMode"in document&&11>=document.documentMode,ho=null,ld=null,_l=null,sd=!1;function Kh(e,n,o){var i=o.window===o?o.document:o.nodeType===9?o:o.ownerDocument;sd||ho==null||ho!==li(i)||(i=ho,"selectionStart"in i&&od(i)?i={start:i.selectionStart,end:i.selectionEnd}:(i=(i.ownerDocument&&i.ownerDocument.defaultView||window).getSelection(),i={anchorNode:i.anchorNode,anchorOffset:i.anchorOffset,focusNode:i.focusNode,focusOffset:i.focusOffset}),_l&&Al(_l,i)||(_l=i,i=tc(ld,"onSelect"),0>=S,p-=S,Kn=1<<32-Lt(n)+p|o<Ae?(Le=ye,ye=null):Le=ye.sibling;var $e=G($,ye,I[Ae],Q);if($e===null){ye===null&&(ye=Le);break}e&&ye&&$e.alternate===null&&n($,ye),L=y($e,L,Ae),Be===null?be=$e:Be.sibling=$e,Be=$e,ye=Le}if(Ae===I.length)return o($,ye),ze&&mr($,Ae),be;if(ye===null){for(;AeAe?(Le=ye,ye=null):Le=ye.sibling;var oa=G($,ye,$e.value,Q);if(oa===null){ye===null&&(ye=Le);break}e&&ye&&oa.alternate===null&&n($,ye),L=y(oa,L,Ae),Be===null?be=oa:Be.sibling=oa,Be=oa,ye=Le}if($e.done)return o($,ye),ze&&mr($,Ae),be;if(ye===null){for(;!$e.done;Ae++,$e=I.next())$e=ne($,$e.value,Q),$e!==null&&(L=y($e,L,Ae),Be===null?be=$e:Be.sibling=$e,Be=$e);return ze&&mr($,Ae),be}for(ye=i(ye);!$e.done;Ae++,$e=I.next())$e=W(ye,$,Ae,$e.value,Q),$e!==null&&(e&&$e.alternate!==null&&ye.delete($e.key===null?Ae:$e.key),L=y($e,L,Ae),Be===null?be=$e:Be.sibling=$e,Be=$e);return e&&ye.forEach(function(zE){return n($,zE)}),ze&&mr($,Ae),be}function Fe($,L,I,Q){if(typeof I=="object"&&I!==null&&I.type===w&&I.key===null&&(I=I.props.children),typeof I=="object"&&I!==null){switch(I.$$typeof){case C:e:{for(var be=I.key;L!==null;){if(L.key===be){if(be=I.type,be===w){if(L.tag===7){o($,L.sibling),Q=p(L,I.props.children),Q.return=$,$=Q;break e}}else if(L.elementType===be||typeof be=="object"&&be!==null&&be.$$typeof===U&&Oa(be)===L.type){o($,L.sibling),Q=p(L,I.props),zl(Q,I),Q.return=$,$=Q;break e}o($,L);break}else n($,L);L=L.sibling}I.type===w?(Q=Ta(I.props.children,$.mode,Q,I.key),Q.return=$,$=Q):(Q=yi(I.type,I.key,I.props,null,$.mode,Q),zl(Q,I),Q.return=$,$=Q)}return S($);case E:e:{for(be=I.key;L!==null;){if(L.key===be)if(L.tag===4&&L.stateNode.containerInfo===I.containerInfo&&L.stateNode.implementation===I.implementation){o($,L.sibling),Q=p(L,I.children||[]),Q.return=$,$=Q;break e}else{o($,L);break}else n($,L);L=L.sibling}Q=pd(I,$.mode,Q),Q.return=$,$=Q}return S($);case U:return I=Oa(I),Fe($,L,I,Q)}if(de(I))return ve($,L,I,Q);if(ce(I)){if(be=ce(I),typeof be!="function")throw Error(l(150));return I=be.call(I),xe($,L,I,Q)}if(typeof I.then=="function")return Fe($,L,Ni(I),Q);if(I.$$typeof===A)return Fe($,L,Si($,I),Q);ji($,I)}return typeof I=="string"&&I!==""||typeof I=="number"||typeof I=="bigint"?(I=""+I,L!==null&&L.tag===6?(o($,L.sibling),Q=p(L,I),Q.return=$,$=Q):(o($,L),Q=md(I,$.mode,Q),Q.return=$,$=Q),S($)):o($,L)}return function($,L,I,Q){try{Ll=0;var be=Fe($,L,I,Q);return jo=null,be}catch(ye){if(ye===No||ye===wi)throw ye;var Be=on(29,ye,null,$.mode);return Be.lanes=Q,Be.return=$,Be}finally{}}}var La=bg(!0),xg=bg(!1),Ir=!1;function jd(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Rd(e,n){e=e.updateQueue,n.updateQueue===e&&(n.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Vr(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Gr(e,n,o){var i=e.updateQueue;if(i===null)return null;if(i=i.shared,(He&2)!==0){var p=i.pending;return p===null?n.next=n:(n.next=p.next,p.next=n),i.pending=n,n=vi(e),rg(e,null,o),n}return gi(e,i,n,o),vi(e)}function Pl(e,n,o){if(n=n.updateQueue,n!==null&&(n=n.shared,(o&4194048)!==0)){var i=n.lanes;i&=e.pendingLanes,o|=i,n.lanes=o,Yt(e,o)}}function Td(e,n){var o=e.updateQueue,i=e.alternate;if(i!==null&&(i=i.updateQueue,o===i)){var p=null,y=null;if(o=o.firstBaseUpdate,o!==null){do{var S={lane:o.lane,tag:o.tag,payload:o.payload,callback:null,next:null};y===null?p=y=S:y=y.next=S,o=o.next}while(o!==null);y===null?p=y=n:y=y.next=n}else p=y=n;o={baseState:i.baseState,firstBaseUpdate:p,lastBaseUpdate:y,shared:i.shared,callbacks:i.callbacks},e.updateQueue=o;return}e=o.lastBaseUpdate,e===null?o.firstBaseUpdate=n:e.next=n,o.lastBaseUpdate=n}var Ad=!1;function Bl(){if(Ad){var e=Eo;if(e!==null)throw e}}function $l(e,n,o,i){Ad=!1;var p=e.updateQueue;Ir=!1;var y=p.firstBaseUpdate,S=p.lastBaseUpdate,R=p.shared.pending;if(R!==null){p.shared.pending=null;var k=R,V=k.next;k.next=null,S===null?y=V:S.next=V,S=k;var K=e.alternate;K!==null&&(K=K.updateQueue,R=K.lastBaseUpdate,R!==S&&(R===null?K.firstBaseUpdate=V:R.next=V,K.lastBaseUpdate=k))}if(y!==null){var ne=p.baseState;S=0,K=V=k=null,R=y;do{var G=R.lane&-536870913,W=G!==R.lane;if(W?(De&G)===G:(i&G)===G){G!==0&&G===wo&&(Ad=!0),K!==null&&(K=K.next={lane:0,tag:R.tag,payload:R.payload,callback:null,next:null});e:{var ve=e,xe=R;G=n;var Fe=o;switch(xe.tag){case 1:if(ve=xe.payload,typeof ve=="function"){ne=ve.call(Fe,ne,G);break e}ne=ve;break e;case 3:ve.flags=ve.flags&-65537|128;case 0:if(ve=xe.payload,G=typeof ve=="function"?ve.call(Fe,ne,G):ve,G==null)break e;ne=v({},ne,G);break e;case 2:Ir=!0}}G=R.callback,G!==null&&(e.flags|=64,W&&(e.flags|=8192),W=p.callbacks,W===null?p.callbacks=[G]:W.push(G))}else W={lane:G,tag:R.tag,payload:R.payload,callback:R.callback,next:null},K===null?(V=K=W,k=ne):K=K.next=W,S|=G;if(R=R.next,R===null){if(R=p.shared.pending,R===null)break;W=R,R=W.next,W.next=null,p.lastBaseUpdate=W,p.shared.pending=null}}while(!0);K===null&&(k=ne),p.baseState=k,p.firstBaseUpdate=V,p.lastBaseUpdate=K,y===null&&(p.shared.lanes=0),Xr|=S,e.lanes=S,e.memoizedState=ne}}function Sg(e,n){if(typeof e!="function")throw Error(l(191,e));e.call(n)}function Cg(e,n){var o=e.callbacks;if(o!==null)for(e.callbacks=null,e=0;ey?y:8;var S=O.T,R={};O.T=R,qd(e,!1,n,o);try{var k=p(),V=O.S;if(V!==null&&V(R,k),k!==null&&typeof k=="object"&&typeof k.then=="function"){var K=N5(k,i);Il(e,n,K,dn(e))}else Il(e,n,i,dn(e))}catch(ne){Il(e,n,{then:function(){},status:"rejected",reason:ne},dn())}finally{q.p=y,S!==null&&R.types!==null&&(S.types=R.types),O.T=S}}function M5(){}function Fd(e,n,o,i){if(e.tag!==5)throw Error(l(476));var p=ev(e).queue;Jg(e,p,n,J,o===null?M5:function(){return tv(e),o(i)})}function ev(e){var n=e.memoizedState;if(n!==null)return n;n={memoizedState:J,baseState:J,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:vr,lastRenderedState:J},next:null};var o={};return n.next={memoizedState:o,baseState:o,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:vr,lastRenderedState:o},next:null},e.memoizedState=n,e=e.alternate,e!==null&&(e.memoizedState=n),n}function tv(e){var n=ev(e);n.next===null&&(n=e.alternate.memoizedState),Il(e,n.next.queue,{},dn())}function Wd(){return Tt(os)}function nv(){return it().memoizedState}function rv(){return it().memoizedState}function k5(e){for(var n=e.return;n!==null;){switch(n.tag){case 24:case 3:var o=dn();e=Vr(o);var i=Gr(n,e,o);i!==null&&(Jt(i,n,o),Pl(i,n,o)),n={cache:Cd()},e.payload=n;return}n=n.return}}function O5(e,n,o){var i=dn();o={lane:i,revertLane:0,gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null},zi(e)?ov(n,o):(o=dd(e,n,o,i),o!==null&&(Jt(o,e,i),lv(o,n,i)))}function av(e,n,o){var i=dn();Il(e,n,o,i)}function Il(e,n,o,i){var p={lane:i,revertLane:0,gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null};if(zi(e))ov(n,p);else{var y=e.alternate;if(e.lanes===0&&(y===null||y.lanes===0)&&(y=n.lastRenderedReducer,y!==null))try{var S=n.lastRenderedState,R=y(S,o);if(p.hasEagerState=!0,p.eagerState=R,an(R,S))return gi(e,n,p,0),qe===null&&hi(),!1}catch{}finally{}if(o=dd(e,n,p,i),o!==null)return Jt(o,e,i),lv(o,n,i),!0}return!1}function qd(e,n,o,i){if(i={lane:2,revertLane:Rf(),gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},zi(e)){if(n)throw Error(l(479))}else n=dd(e,o,i,2),n!==null&&Jt(n,e,2)}function zi(e){var n=e.alternate;return e===Te||n!==null&&n===Te}function ov(e,n){To=Ai=!0;var o=e.pending;o===null?n.next=n:(n.next=o.next,o.next=n),e.pending=n}function lv(e,n,o){if((o&4194048)!==0){var i=n.lanes;i&=e.pendingLanes,o|=i,n.lanes=o,Yt(e,o)}}var Vl={readContext:Tt,use:ki,useCallback:rt,useContext:rt,useEffect:rt,useImperativeHandle:rt,useLayoutEffect:rt,useInsertionEffect:rt,useMemo:rt,useReducer:rt,useRef:rt,useState:rt,useDebugValue:rt,useDeferredValue:rt,useTransition:rt,useSyncExternalStore:rt,useId:rt,useHostTransitionStatus:rt,useFormState:rt,useActionState:rt,useOptimistic:rt,useMemoCache:rt,useCacheRefresh:rt};Vl.useEffectEvent=rt;var sv={readContext:Tt,use:ki,useCallback:function(e,n){return It().memoizedState=[e,n===void 0?null:n],e},useContext:Tt,useEffect:Gg,useImperativeHandle:function(e,n,o){o=o!=null?o.concat([e]):null,Di(4194308,4,qg.bind(null,n,e),o)},useLayoutEffect:function(e,n){return Di(4194308,4,e,n)},useInsertionEffect:function(e,n){Di(4,2,e,n)},useMemo:function(e,n){var o=It();n=n===void 0?null:n;var i=e();if(za){zn(!0);try{e()}finally{zn(!1)}}return o.memoizedState=[i,n],i},useReducer:function(e,n,o){var i=It();if(o!==void 0){var p=o(n);if(za){zn(!0);try{o(n)}finally{zn(!1)}}}else p=n;return i.memoizedState=i.baseState=p,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:p},i.queue=e,e=e.dispatch=O5.bind(null,Te,e),[i.memoizedState,e]},useRef:function(e){var n=It();return e={current:e},n.memoizedState=e},useState:function(e){e=Hd(e);var n=e.queue,o=av.bind(null,Te,n);return n.dispatch=o,[e.memoizedState,o]},useDebugValue:Gd,useDeferredValue:function(e,n){var o=It();return Yd(o,e,n)},useTransition:function(){var e=Hd(!1);return e=Jg.bind(null,Te,e.queue,!0,!1),It().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,n,o){var i=Te,p=It();if(ze){if(o===void 0)throw Error(l(407));o=o()}else{if(o=n(),qe===null)throw Error(l(349));(De&127)!==0||Tg(i,n,o)}p.memoizedState=o;var y={value:o,getSnapshot:n};return p.queue=y,Gg(_g.bind(null,i,y,e),[e]),i.flags|=2048,_o(9,{destroy:void 0},Ag.bind(null,i,y,o,n),null),o},useId:function(){var e=It(),n=qe.identifierPrefix;if(ze){var o=Zn,i=Kn;o=(i&~(1<<32-Lt(i)-1)).toString(32)+o,n="_"+n+"R_"+o,o=_i++,0<\/script>",y=y.removeChild(y.firstChild);break;case"select":y=typeof i.is=="string"?S.createElement("select",{is:i.is}):S.createElement("select"),i.multiple?y.multiple=!0:i.size&&(y.size=i.size);break;default:y=typeof i.is=="string"?S.createElement(p,{is:i.is}):S.createElement(p)}}y[jt]=n,y[Wt]=i;e:for(S=n.child;S!==null;){if(S.tag===5||S.tag===6)y.appendChild(S.stateNode);else if(S.tag!==4&&S.tag!==27&&S.child!==null){S.child.return=S,S=S.child;continue}if(S===n)break e;for(;S.sibling===null;){if(S.return===null||S.return===n)break e;S=S.return}S.sibling.return=S.return,S=S.sibling}n.stateNode=y;e:switch(_t(y,p,i),p){case"button":case"input":case"select":case"textarea":i=!!i.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&br(n)}}return Ze(n),cf(n,n.type,e===null?null:e.memoizedProps,n.pendingProps,o),null;case 6:if(e&&n.stateNode!=null)e.memoizedProps!==i&&br(n);else{if(typeof i!="string"&&n.stateNode===null)throw Error(l(166));if(e=oe.current,So(n)){if(e=n.stateNode,o=n.memoizedProps,i=null,p=Rt,p!==null)switch(p.tag){case 27:case 5:i=p.memoizedProps}e[jt]=n,e=!!(e.nodeValue===o||i!==null&&i.suppressHydrationWarning===!0||jy(e.nodeValue,o)),e||Ur(n,!0)}else e=nc(e).createTextNode(i),e[jt]=n,n.stateNode=e}return Ze(n),null;case 31:if(o=n.memoizedState,e===null||e.memoizedState!==null){if(i=So(n),o!==null){if(e===null){if(!i)throw Error(l(318));if(e=n.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(l(557));e[jt]=n}else Aa(),(n.flags&128)===0&&(n.memoizedState=null),n.flags|=4;Ze(n),e=!1}else o=yd(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=o),e=!0;if(!e)return n.flags&256?(sn(n),n):(sn(n),null);if((n.flags&128)!==0)throw Error(l(558))}return Ze(n),null;case 13:if(i=n.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(p=So(n),i!==null&&i.dehydrated!==null){if(e===null){if(!p)throw Error(l(318));if(p=n.memoizedState,p=p!==null?p.dehydrated:null,!p)throw Error(l(317));p[jt]=n}else Aa(),(n.flags&128)===0&&(n.memoizedState=null),n.flags|=4;Ze(n),p=!1}else p=yd(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=p),p=!0;if(!p)return n.flags&256?(sn(n),n):(sn(n),null)}return sn(n),(n.flags&128)!==0?(n.lanes=o,n):(o=i!==null,e=e!==null&&e.memoizedState!==null,o&&(i=n.child,p=null,i.alternate!==null&&i.alternate.memoizedState!==null&&i.alternate.memoizedState.cachePool!==null&&(p=i.alternate.memoizedState.cachePool.pool),y=null,i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(y=i.memoizedState.cachePool.pool),y!==p&&(i.flags|=2048)),o!==e&&o&&(n.child.flags|=8192),Hi(n,n.updateQueue),Ze(n),null);case 4:return ue(),e===null&&Mf(n.stateNode.containerInfo),Ze(n),null;case 10:return hr(n.type),Ze(n),null;case 19:if(D(st),i=n.memoizedState,i===null)return Ze(n),null;if(p=(n.flags&128)!==0,y=i.rendering,y===null)if(p)Yl(i,!1);else{if(at!==0||e!==null&&(e.flags&128)!==0)for(e=n.child;e!==null;){if(y=Ti(e),y!==null){for(n.flags|=128,Yl(i,!1),e=y.updateQueue,n.updateQueue=e,Hi(n,e),n.subtreeFlags=0,e=o,o=n.child;o!==null;)ag(o,e),o=o.sibling;return H(st,st.current&1|2),ze&&mr(n,i.treeForkCount),n.child}e=e.sibling}i.tail!==null&&Ut()>Fi&&(n.flags|=128,p=!0,Yl(i,!1),n.lanes=4194304)}else{if(!p)if(e=Ti(y),e!==null){if(n.flags|=128,p=!0,e=e.updateQueue,n.updateQueue=e,Hi(n,e),Yl(i,!0),i.tail===null&&i.tailMode==="hidden"&&!y.alternate&&!ze)return Ze(n),null}else 2*Ut()-i.renderingStartTime>Fi&&o!==536870912&&(n.flags|=128,p=!0,Yl(i,!1),n.lanes=4194304);i.isBackwards?(y.sibling=n.child,n.child=y):(e=i.last,e!==null?e.sibling=y:n.child=y,i.last=y)}return i.tail!==null?(e=i.tail,i.rendering=e,i.tail=e.sibling,i.renderingStartTime=Ut(),e.sibling=null,o=st.current,H(st,p?o&1|2:o&1),ze&&mr(n,i.treeForkCount),e):(Ze(n),null);case 22:case 23:return sn(n),Md(),i=n.memoizedState!==null,e!==null?e.memoizedState!==null!==i&&(n.flags|=8192):i&&(n.flags|=8192),i?(o&536870912)!==0&&(n.flags&128)===0&&(Ze(n),n.subtreeFlags&6&&(n.flags|=8192)):Ze(n),o=n.updateQueue,o!==null&&Hi(n,o.retryQueue),o=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(o=e.memoizedState.cachePool.pool),i=null,n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(i=n.memoizedState.cachePool.pool),i!==o&&(n.flags|=2048),e!==null&&D(ka),null;case 24:return o=null,e!==null&&(o=e.memoizedState.cache),n.memoizedState.cache!==o&&(n.flags|=2048),hr(ft),Ze(n),null;case 25:return null;case 30:return null}throw Error(l(156,n.tag))}function B5(e,n){switch(gd(n),n.tag){case 1:return e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 3:return hr(ft),ue(),e=n.flags,(e&65536)!==0&&(e&128)===0?(n.flags=e&-65537|128,n):null;case 26:case 27:case 5:return Re(n),null;case 31:if(n.memoizedState!==null){if(sn(n),n.alternate===null)throw Error(l(340));Aa()}return e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 13:if(sn(n),e=n.memoizedState,e!==null&&e.dehydrated!==null){if(n.alternate===null)throw Error(l(340));Aa()}return e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 19:return D(st),null;case 4:return ue(),null;case 10:return hr(n.type),null;case 22:case 23:return sn(n),Md(),e!==null&&D(ka),e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 24:return hr(ft),null;case 25:return null;default:return null}}function Mv(e,n){switch(gd(n),n.tag){case 3:hr(ft),ue();break;case 26:case 27:case 5:Re(n);break;case 4:ue();break;case 31:n.memoizedState!==null&&sn(n);break;case 13:sn(n);break;case 19:D(st);break;case 10:hr(n.type);break;case 22:case 23:sn(n),Md(),e!==null&&D(ka);break;case 24:hr(ft)}}function Fl(e,n){try{var o=n.updateQueue,i=o!==null?o.lastEffect:null;if(i!==null){var p=i.next;o=p;do{if((o.tag&e)===e){i=void 0;var y=o.create,S=o.inst;i=y(),S.destroy=i}o=o.next}while(o!==p)}}catch(R){Ve(n,n.return,R)}}function Wr(e,n,o){try{var i=n.updateQueue,p=i!==null?i.lastEffect:null;if(p!==null){var y=p.next;i=y;do{if((i.tag&e)===e){var S=i.inst,R=S.destroy;if(R!==void 0){S.destroy=void 0,p=n;var k=o,V=R;try{V()}catch(K){Ve(p,k,K)}}}i=i.next}while(i!==y)}}catch(K){Ve(n,n.return,K)}}function kv(e){var n=e.updateQueue;if(n!==null){var o=e.stateNode;try{Cg(n,o)}catch(i){Ve(e,e.return,i)}}}function Ov(e,n,o){o.props=Pa(e.type,e.memoizedProps),o.state=e.memoizedState;try{o.componentWillUnmount()}catch(i){Ve(e,n,i)}}function Wl(e,n){try{var o=e.ref;if(o!==null){switch(e.tag){case 26:case 27:case 5:var i=e.stateNode;break;case 30:i=e.stateNode;break;default:i=e.stateNode}typeof o=="function"?e.refCleanup=o(i):o.current=i}}catch(p){Ve(e,n,p)}}function Qn(e,n){var o=e.ref,i=e.refCleanup;if(o!==null)if(typeof i=="function")try{i()}catch(p){Ve(e,n,p)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof o=="function")try{o(null)}catch(p){Ve(e,n,p)}else o.current=null}function Dv(e){var n=e.type,o=e.memoizedProps,i=e.stateNode;try{e:switch(n){case"button":case"input":case"select":case"textarea":o.autoFocus&&i.focus();break e;case"img":o.src?i.src=o.src:o.srcSet&&(i.srcset=o.srcSet)}}catch(p){Ve(e,e.return,p)}}function uf(e,n,o){try{var i=e.stateNode;lE(i,e.type,o,n),i[Wt]=n}catch(p){Ve(e,e.return,p)}}function Lv(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&ea(e.type)||e.tag===4}function df(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Lv(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&ea(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ff(e,n,o){var i=e.tag;if(i===5||i===6)e=e.stateNode,n?(o.nodeType===9?o.body:o.nodeName==="HTML"?o.ownerDocument.body:o).insertBefore(e,n):(n=o.nodeType===9?o.body:o.nodeName==="HTML"?o.ownerDocument.body:o,n.appendChild(e),o=o._reactRootContainer,o!=null||n.onclick!==null||(n.onclick=ur));else if(i!==4&&(i===27&&ea(e.type)&&(o=e.stateNode,n=null),e=e.child,e!==null))for(ff(e,n,o),e=e.sibling;e!==null;)ff(e,n,o),e=e.sibling}function Ii(e,n,o){var i=e.tag;if(i===5||i===6)e=e.stateNode,n?o.insertBefore(e,n):o.appendChild(e);else if(i!==4&&(i===27&&ea(e.type)&&(o=e.stateNode),e=e.child,e!==null))for(Ii(e,n,o),e=e.sibling;e!==null;)Ii(e,n,o),e=e.sibling}function zv(e){var n=e.stateNode,o=e.memoizedProps;try{for(var i=e.type,p=n.attributes;p.length;)n.removeAttributeNode(p[0]);_t(n,i,o),n[jt]=e,n[Wt]=o}catch(y){Ve(e,e.return,y)}}var xr=!1,ht=!1,mf=!1,Pv=typeof WeakSet=="function"?WeakSet:Set,Ct=null;function $5(e,n){if(e=e.containerInfo,Df=cc,e=Xh(e),od(e)){if("selectionStart"in e)var o={start:e.selectionStart,end:e.selectionEnd};else e:{o=(o=e.ownerDocument)&&o.defaultView||window;var i=o.getSelection&&o.getSelection();if(i&&i.rangeCount!==0){o=i.anchorNode;var p=i.anchorOffset,y=i.focusNode;i=i.focusOffset;try{o.nodeType,y.nodeType}catch{o=null;break e}var S=0,R=-1,k=-1,V=0,K=0,ne=e,G=null;t:for(;;){for(var W;ne!==o||p!==0&&ne.nodeType!==3||(R=S+p),ne!==y||i!==0&&ne.nodeType!==3||(k=S+i),ne.nodeType===3&&(S+=ne.nodeValue.length),(W=ne.firstChild)!==null;)G=ne,ne=W;for(;;){if(ne===e)break t;if(G===o&&++V===p&&(R=S),G===y&&++K===i&&(k=S),(W=ne.nextSibling)!==null)break;ne=G,G=ne.parentNode}ne=W}o=R===-1||k===-1?null:{start:R,end:k}}else o=null}o=o||{start:0,end:0}}else o=null;for(Lf={focusedElem:e,selectionRange:o},cc=!1,Ct=n;Ct!==null;)if(n=Ct,e=n.child,(n.subtreeFlags&1028)!==0&&e!==null)e.return=n,Ct=e;else for(;Ct!==null;){switch(n=Ct,y=n.alternate,e=n.flags,n.tag){case 0:if((e&4)!==0&&(e=n.updateQueue,e=e!==null?e.events:null,e!==null))for(o=0;o title"))),_t(y,i,o),y[jt]=e,St(y),i=y;break e;case"link":var S=Vy("link","href",p).get(i+(o.href||""));if(S){for(var R=0;RFe&&(S=Fe,Fe=xe,xe=S);var $=Wh(R,xe),L=Wh(R,Fe);if($&&L&&(W.rangeCount!==1||W.anchorNode!==$.node||W.anchorOffset!==$.offset||W.focusNode!==L.node||W.focusOffset!==L.offset)){var I=ne.createRange();I.setStart($.node,$.offset),W.removeAllRanges(),xe>Fe?(W.addRange(I),W.extend(L.node,L.offset)):(I.setEnd(L.node,L.offset),W.addRange(I))}}}}for(ne=[],W=R;W=W.parentNode;)W.nodeType===1&&ne.push({element:W,left:W.scrollLeft,top:W.scrollTop});for(typeof R.focus=="function"&&R.focus(),R=0;Ro?32:o,O.T=null,o=xf,xf=null;var y=Zr,S=Nr;if(vt=0,Lo=Zr=null,Nr=0,(He&6)!==0)throw Error(l(331));var R=He;if(He|=4,qv(y.current),Yv(y,y.current,S,o),He=R,Jl(0,!1),Ht&&typeof Ht.onPostCommitFiberRoot=="function")try{Ht.onPostCommitFiberRoot(xa,y)}catch{}return!0}finally{q.p=p,O.T=i,fy(e,n)}}function py(e,n,o){n=Cn(o,n),n=Qd(e.stateNode,n,2),e=Gr(e,n,2),e!==null&&(tt(e,2),Jn(e))}function Ve(e,n,o){if(e.tag===3)py(e,e,o);else for(;n!==null;){if(n.tag===3){py(n,e,o);break}else if(n.tag===1){var i=n.stateNode;if(typeof n.type.getDerivedStateFromError=="function"||typeof i.componentDidCatch=="function"&&(Kr===null||!Kr.has(i))){e=Cn(o,e),o=hv(2),i=Gr(n,o,2),i!==null&&(gv(o,i,n,e),tt(i,2),Jn(i));break}}n=n.return}}function Ef(e,n,o){var i=e.pingCache;if(i===null){i=e.pingCache=new I5;var p=new Set;i.set(n,p)}else p=i.get(n),p===void 0&&(p=new Set,i.set(n,p));p.has(o)||(gf=!0,p.add(o),e=W5.bind(null,e,n,o),n.then(e,e))}function W5(e,n,o){var i=e.pingCache;i!==null&&i.delete(n),e.pingedLanes|=e.suspendedLanes&o,e.warmLanes&=~o,qe===e&&(De&o)===o&&(at===4||at===3&&(De&62914560)===De&&300>Ut()-Yi?(He&2)===0&&zo(e,0):vf|=o,Do===De&&(Do=0)),Jn(e)}function hy(e,n){n===0&&(n=zt()),e=Ra(e,n),e!==null&&(tt(e,n),Jn(e))}function q5(e){var n=e.memoizedState,o=0;n!==null&&(o=n.retryLane),hy(e,o)}function X5(e,n){var o=0;switch(e.tag){case 31:case 13:var i=e.stateNode,p=e.memoizedState;p!==null&&(o=p.retryLane);break;case 19:i=e.stateNode;break;case 22:i=e.stateNode._retryCache;break;default:throw Error(l(314))}i!==null&&i.delete(n),hy(e,o)}function K5(e,n){return gl(e,n)}var Qi=null,Bo=null,Nf=!1,Ji=!1,jf=!1,Jr=0;function Jn(e){e!==Bo&&e.next===null&&(Bo===null?Qi=Bo=e:Bo=Bo.next=e),Ji=!0,Nf||(Nf=!0,Q5())}function Jl(e,n){if(!jf&&Ji){jf=!0;do for(var o=!1,i=Qi;i!==null;){if(e!==0){var p=i.pendingLanes;if(p===0)var y=0;else{var S=i.suspendedLanes,R=i.pingedLanes;y=(1<<31-Lt(42|e)+1)-1,y&=p&~(S&~R),y=y&201326741?y&201326741|1:y?y|2:0}y!==0&&(o=!0,by(i,y))}else y=De,y=Ee(i,i===qe?y:0,i.cancelPendingCommit!==null||i.timeoutHandle!==-1),(y&3)===0||Qe(i,y)||(o=!0,by(i,y));i=i.next}while(o);jf=!1}}function Z5(){gy()}function gy(){Ji=Nf=!1;var e=0;Jr!==0&&iE()&&(e=Jr);for(var n=Ut(),o=null,i=Qi;i!==null;){var p=i.next,y=vy(i,n);y===0?(i.next=null,o===null?Qi=p:o.next=p,p===null&&(Bo=o)):(o=i,(e!==0||(y&3)!==0)&&(Ji=!0)),i=p}vt!==0&&vt!==5||Jl(e),Jr!==0&&(Jr=0)}function vy(e,n){for(var o=e.suspendedLanes,i=e.pingedLanes,p=e.expirationTimes,y=e.pendingLanes&-62914561;0R)break;var K=k.transferSize,ne=k.initiatorType;K&&Ry(ne)&&(k=k.responseEnd,S+=K*(k"u"?null:document;function $y(e,n,o){var i=$o;if(i&&typeof n=="string"&&n){var p=xn(n);p='link[rel="'+e+'"][href="'+p+'"]',typeof o=="string"&&(p+='[crossorigin="'+o+'"]'),By.has(p)||(By.add(p),e={rel:e,crossOrigin:o,href:n},i.querySelector(p)===null&&(n=i.createElement("link"),_t(n,"link",e),St(n),i.head.appendChild(n)))}}function vE(e){jr.D(e),$y("dns-prefetch",e,null)}function yE(e,n){jr.C(e,n),$y("preconnect",e,n)}function bE(e,n,o){jr.L(e,n,o);var i=$o;if(i&&e&&n){var p='link[rel="preload"][as="'+xn(n)+'"]';n==="image"&&o&&o.imageSrcSet?(p+='[imagesrcset="'+xn(o.imageSrcSet)+'"]',typeof o.imageSizes=="string"&&(p+='[imagesizes="'+xn(o.imageSizes)+'"]')):p+='[href="'+xn(e)+'"]';var y=p;switch(n){case"style":y=Uo(e);break;case"script":y=Ho(e)}Tn.has(y)||(e=v({rel:"preload",href:n==="image"&&o&&o.imageSrcSet?void 0:e,as:n},o),Tn.set(y,e),i.querySelector(p)!==null||n==="style"&&i.querySelector(rs(y))||n==="script"&&i.querySelector(as(y))||(n=i.createElement("link"),_t(n,"link",e),St(n),i.head.appendChild(n)))}}function xE(e,n){jr.m(e,n);var o=$o;if(o&&e){var i=n&&typeof n.as=="string"?n.as:"script",p='link[rel="modulepreload"][as="'+xn(i)+'"][href="'+xn(e)+'"]',y=p;switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":y=Ho(e)}if(!Tn.has(y)&&(e=v({rel:"modulepreload",href:e},n),Tn.set(y,e),o.querySelector(p)===null)){switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(o.querySelector(as(y)))return}i=o.createElement("link"),_t(i,"link",e),St(i),o.head.appendChild(i)}}}function SE(e,n,o){jr.S(e,n,o);var i=$o;if(i&&e){var p=so(i).hoistableStyles,y=Uo(e);n=n||"default";var S=p.get(y);if(!S){var R={loading:0,preload:null};if(S=i.querySelector(rs(y)))R.loading=5;else{e=v({rel:"stylesheet",href:e,"data-precedence":n},o),(o=Tn.get(y))&&If(e,o);var k=S=i.createElement("link");St(k),_t(k,"link",e),k._p=new Promise(function(V,K){k.onload=V,k.onerror=K}),k.addEventListener("load",function(){R.loading|=1}),k.addEventListener("error",function(){R.loading|=2}),R.loading|=4,ac(S,n,i)}S={type:"stylesheet",instance:S,count:1,state:R},p.set(y,S)}}}function CE(e,n){jr.X(e,n);var o=$o;if(o&&e){var i=so(o).hoistableScripts,p=Ho(e),y=i.get(p);y||(y=o.querySelector(as(p)),y||(e=v({src:e,async:!0},n),(n=Tn.get(p))&&Vf(e,n),y=o.createElement("script"),St(y),_t(y,"link",e),o.head.appendChild(y)),y={type:"script",instance:y,count:1,state:null},i.set(p,y))}}function wE(e,n){jr.M(e,n);var o=$o;if(o&&e){var i=so(o).hoistableScripts,p=Ho(e),y=i.get(p);y||(y=o.querySelector(as(p)),y||(e=v({src:e,async:!0,type:"module"},n),(n=Tn.get(p))&&Vf(e,n),y=o.createElement("script"),St(y),_t(y,"link",e),o.head.appendChild(y)),y={type:"script",instance:y,count:1,state:null},i.set(p,y))}}function Uy(e,n,o,i){var p=(p=oe.current)?rc(p):null;if(!p)throw Error(l(446));switch(e){case"meta":case"title":return null;case"style":return typeof o.precedence=="string"&&typeof o.href=="string"?(n=Uo(o.href),o=so(p).hoistableStyles,i=o.get(n),i||(i={type:"style",instance:null,count:0,state:null},o.set(n,i)),i):{type:"void",instance:null,count:0,state:null};case"link":if(o.rel==="stylesheet"&&typeof o.href=="string"&&typeof o.precedence=="string"){e=Uo(o.href);var y=so(p).hoistableStyles,S=y.get(e);if(S||(p=p.ownerDocument||p,S={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},y.set(e,S),(y=p.querySelector(rs(e)))&&!y._p&&(S.instance=y,S.state.loading=5),Tn.has(e)||(o={rel:"preload",as:"style",href:o.href,crossOrigin:o.crossOrigin,integrity:o.integrity,media:o.media,hrefLang:o.hrefLang,referrerPolicy:o.referrerPolicy},Tn.set(e,o),y||EE(p,e,o,S.state))),n&&i===null)throw Error(l(528,""));return S}if(n&&i!==null)throw Error(l(529,""));return null;case"script":return n=o.async,o=o.src,typeof o=="string"&&n&&typeof n!="function"&&typeof n!="symbol"?(n=Ho(o),o=so(p).hoistableScripts,i=o.get(n),i||(i={type:"script",instance:null,count:0,state:null},o.set(n,i)),i):{type:"void",instance:null,count:0,state:null};default:throw Error(l(444,e))}}function Uo(e){return'href="'+xn(e)+'"'}function rs(e){return'link[rel="stylesheet"]['+e+"]"}function Hy(e){return v({},e,{"data-precedence":e.precedence,precedence:null})}function EE(e,n,o,i){e.querySelector('link[rel="preload"][as="style"]['+n+"]")?i.loading=1:(n=e.createElement("link"),i.preload=n,n.addEventListener("load",function(){return i.loading|=1}),n.addEventListener("error",function(){return i.loading|=2}),_t(n,"link",o),St(n),e.head.appendChild(n))}function Ho(e){return'[src="'+xn(e)+'"]'}function as(e){return"script[async]"+e}function Iy(e,n,o){if(n.count++,n.instance===null)switch(n.type){case"style":var i=e.querySelector('style[data-href~="'+xn(o.href)+'"]');if(i)return n.instance=i,St(i),i;var p=v({},o,{"data-href":o.href,"data-precedence":o.precedence,href:null,precedence:null});return i=(e.ownerDocument||e).createElement("style"),St(i),_t(i,"style",p),ac(i,o.precedence,e),n.instance=i;case"stylesheet":p=Uo(o.href);var y=e.querySelector(rs(p));if(y)return n.state.loading|=4,n.instance=y,St(y),y;i=Hy(o),(p=Tn.get(p))&&If(i,p),y=(e.ownerDocument||e).createElement("link"),St(y);var S=y;return S._p=new Promise(function(R,k){S.onload=R,S.onerror=k}),_t(y,"link",i),n.state.loading|=4,ac(y,o.precedence,e),n.instance=y;case"script":return y=Ho(o.src),(p=e.querySelector(as(y)))?(n.instance=p,St(p),p):(i=o,(p=Tn.get(y))&&(i=v({},o),Vf(i,p)),e=e.ownerDocument||e,p=e.createElement("script"),St(p),_t(p,"link",i),e.head.appendChild(p),n.instance=p);case"void":return null;default:throw Error(l(443,n.type))}else n.type==="stylesheet"&&(n.state.loading&4)===0&&(i=n.instance,n.state.loading|=4,ac(i,o.precedence,e));return n.instance}function ac(e,n,o){for(var i=o.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),p=i.length?i[i.length-1]:null,y=p,S=0;S title"):null)}function NE(e,n,o){if(o===1||n.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof n.precedence!="string"||typeof n.href!="string"||n.href==="")break;return!0;case"link":if(typeof n.rel!="string"||typeof n.href!="string"||n.href===""||n.onLoad||n.onError)break;switch(n.rel){case"stylesheet":return e=n.disabled,typeof n.precedence=="string"&&e==null;default:return!0}case"script":if(n.async&&typeof n.async!="function"&&typeof n.async!="symbol"&&!n.onLoad&&!n.onError&&n.src&&typeof n.src=="string")return!0}return!1}function Yy(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function jE(e,n,o,i){if(o.type==="stylesheet"&&(typeof i.media!="string"||matchMedia(i.media).matches!==!1)&&(o.state.loading&4)===0){if(o.instance===null){var p=Uo(i.href),y=n.querySelector(rs(p));if(y){n=y._p,n!==null&&typeof n=="object"&&typeof n.then=="function"&&(e.count++,e=lc.bind(e),n.then(e,e)),o.state.loading|=4,o.instance=y,St(y);return}y=n.ownerDocument||n,i=Hy(i),(p=Tn.get(p))&&If(i,p),y=y.createElement("link"),St(y);var S=y;S._p=new Promise(function(R,k){S.onload=R,S.onerror=k}),_t(y,"link",i),o.instance=y}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(o,n),(n=o.state.preload)&&(o.state.loading&3)===0&&(e.count++,o=lc.bind(e),n.addEventListener("load",o),n.addEventListener("error",o))}}var Gf=0;function RE(e,n){return e.stylesheets&&e.count===0&&ic(e,e.stylesheets),0Gf?50:800)+n);return e.unsuspend=o,function(){e.unsuspend=null,clearTimeout(i),clearTimeout(p)}}:null}function lc(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)ic(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var sc=null;function ic(e,n){e.stylesheets=null,e.unsuspend!==null&&(e.count++,sc=new Map,n.forEach(TE,e),sc=null,lc.call(e))}function TE(e,n){if(!(n.state.loading&4)){var o=sc.get(e);if(o)var i=o.get(null);else{o=new Map,sc.set(e,o);for(var p=e.querySelectorAll("link[data-precedence],style[data-precedence]"),y=0;y"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(r){console.error(r)}}return t(),Jf.exports=GE(),Jf.exports}var FE=YE();/** - * react-router v7.15.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */var h1="popstate";function g1(t){return typeof t=="object"&&t!=null&&"pathname"in t&&"search"in t&&"hash"in t&&"state"in t&&"key"in t}function WE(t={}){function r(l,s){var h;let c=(h=s.state)==null?void 0:h.masked,{pathname:f,search:m,hash:g}=c||l.location;return jm("",{pathname:f,search:m,hash:g},s.state&&s.state.usr||null,s.state&&s.state.key||"default",c?{pathname:l.location.pathname,search:l.location.search,hash:l.location.hash}:void 0)}function a(l,s){return typeof s=="string"?s:Cs(s)}return XE(r,a,null,t)}function et(t,r){if(t===!1||t===null||typeof t>"u")throw new Error(r)}function kn(t,r){if(!t){typeof console<"u"&&console.warn(r);try{throw new Error(r)}catch{}}}function qE(){return Math.random().toString(36).substring(2,10)}function v1(t,r){return{usr:t.state,key:t.key,idx:r,masked:t.mask?{pathname:t.pathname,search:t.search,hash:t.hash}:void 0}}function jm(t,r,a=null,l,s){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof r=="string"?ol(r):r,state:a,key:r&&r.key||l||qE(),mask:s}}function Cs({pathname:t="/",search:r="",hash:a=""}){return r&&r!=="?"&&(t+=r.charAt(0)==="?"?r:"?"+r),a&&a!=="#"&&(t+=a.charAt(0)==="#"?a:"#"+a),t}function ol(t){let r={};if(t){let a=t.indexOf("#");a>=0&&(r.hash=t.substring(a),t=t.substring(0,a));let l=t.indexOf("?");l>=0&&(r.search=t.substring(l),t=t.substring(0,l)),t&&(r.pathname=t)}return r}function XE(t,r,a,l={}){let{window:s=document.defaultView,v5Compat:c=!1}=l,f=s.history,m="POP",g=null,h=b();h==null&&(h=0,f.replaceState({...f.state,idx:h},""));function b(){return(f.state||{idx:null}).idx}function v(){m="POP";let j=b(),N=j==null?null:j-h;h=j,g&&g({action:m,location:w.location,delta:N})}function x(j,N){m="PUSH";let _=g1(j)?j:jm(w.location,j,N);h=b()+1;let A=v1(_,h),T=w.createHref(_.mask||_);try{f.pushState(A,"",T)}catch(z){if(z instanceof DOMException&&z.name==="DataCloneError")throw z;s.location.assign(T)}c&&g&&g({action:m,location:w.location,delta:1})}function C(j,N){m="REPLACE";let _=g1(j)?j:jm(w.location,j,N);h=b();let A=v1(_,h),T=w.createHref(_.mask||_);f.replaceState(A,"",T),c&&g&&g({action:m,location:w.location,delta:0})}function E(j){return KE(j)}let w={get action(){return m},get location(){return t(s,f)},listen(j){if(g)throw new Error("A history only accepts one active listener");return s.addEventListener(h1,v),g=j,()=>{s.removeEventListener(h1,v),g=null}},createHref(j){return r(s,j)},createURL:E,encodeLocation(j){let N=E(j);return{pathname:N.pathname,search:N.search,hash:N.hash}},push:x,replace:C,go(j){return f.go(j)}};return w}function KE(t,r=!1){let a="http://localhost";typeof window<"u"&&(a=window.location.origin!=="null"?window.location.origin:window.location.href),et(a,"No window.location.(origin|href) available to create URL");let l=typeof t=="string"?t:Cs(t);return l=l.replace(/ $/,"%20"),!r&&l.startsWith("//")&&(l=a+l),new URL(l,a)}function Tb(t,r,a="/"){return ZE(t,r,a,!1)}function ZE(t,r,a,l,s){let c=typeof r=="string"?ol(r):r,f=Tr(c.pathname||"/",a);if(f==null)return null;let m=QE(t),g=null,h=u3(f);for(let b=0;g==null&&b{let b={relativePath:h===void 0?f.path||"":h,caseSensitive:f.caseSensitive===!0,childrenIndex:m,route:f};if(b.relativePath.startsWith("/")){if(!b.relativePath.startsWith(l)&&g)return;et(b.relativePath.startsWith(l),`Absolute route path "${b.relativePath}" nested under path "${l}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),b.relativePath=b.relativePath.slice(l.length)}let v=Vn([l,b.relativePath]),x=a.concat(b);f.children&&f.children.length>0&&(et(f.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${v}".`),Ab(f.children,r,x,v,g)),!(f.path==null&&!f.index)&&r.push({path:v,score:l3(v,f.index),routesMeta:x})};return t.forEach((f,m)=>{var g;if(f.path===""||!((g=f.path)!=null&&g.includes("?")))c(f,m);else for(let h of _b(f.path))c(f,m,!0,h)}),r}function _b(t){let r=t.split("/");if(r.length===0)return[];let[a,...l]=r,s=a.endsWith("?"),c=a.replace(/\?$/,"");if(l.length===0)return s?[c,""]:[c];let f=_b(l.join("/")),m=[];return m.push(...f.map(g=>g===""?c:[c,g].join("/"))),s&&m.push(...f),m.map(g=>t.startsWith("/")&&g===""?"/":g)}function JE(t){t.sort((r,a)=>r.score!==a.score?a.score-r.score:s3(r.routesMeta.map(l=>l.childrenIndex),a.routesMeta.map(l=>l.childrenIndex)))}var e3=/^:[\w-]+$/,t3=3,n3=2,r3=1,a3=10,o3=-2,y1=t=>t==="*";function l3(t,r){let a=t.split("/"),l=a.length;return a.some(y1)&&(l+=o3),r&&(l+=n3),a.filter(s=>!y1(s)).reduce((s,c)=>s+(e3.test(c)?t3:c===""?r3:a3),l)}function s3(t,r){return t.length===r.length&&t.slice(0,-1).every((l,s)=>l===r[s])?t[t.length-1]-r[r.length-1]:0}function i3(t,r,a=!1){let{routesMeta:l}=t,s={},c="/",f=[];for(let m=0;m{if(b==="*"){let E=m[x]||"";f=c.slice(0,c.length-E.length).replace(/(.)\/+$/,"$1")}const C=m[x];return v&&!C?h[b]=void 0:h[b]=(C||"").replace(/%2F/g,"/"),h},{}),pathname:c,pathnameBase:f,pattern:t}}function c3(t,r=!1,a=!0){kn(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let l=[],s="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(f,m,g,h,b)=>{if(l.push({paramName:m,isOptional:g!=null}),g){let v=b.charAt(h+f.length);return v&&v!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(l.push({paramName:"*"}),s+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):a?s+="\\/*$":t!==""&&t!=="/"&&(s+="(?:(?=\\/|$))"),[new RegExp(s,r?void 0:"i"),l]}function u3(t){try{return t.split("/").map(r=>decodeURIComponent(r).replace(/\//g,"%2F")).join("/")}catch(r){return kn(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${r}).`),t}}function Tr(t,r){if(r==="/")return t;if(!t.toLowerCase().startsWith(r.toLowerCase()))return null;let a=r.endsWith("/")?r.length-1:r.length,l=t.charAt(a);return l&&l!=="/"?null:t.slice(a)||"/"}var d3=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function f3(t,r="/"){let{pathname:a,search:l="",hash:s=""}=typeof t=="string"?ol(t):t,c;return a?(a=Mb(a),a.startsWith("/")?c=b1(a.substring(1),"/"):c=b1(a,r)):c=r,{pathname:c,search:h3(l),hash:g3(s)}}function b1(t,r){let a=Lc(r).split("/");return t.split("/").forEach(s=>{s===".."?a.length>1&&a.pop():s!=="."&&a.push(s)}),a.length>1?a.join("/"):"/"}function rm(t,r,a,l){return`Cannot include a '${t}' character in a manually specified \`to.${r}\` field [${JSON.stringify(l)}]. Please separate it out to the \`to.${a}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function m3(t){return t.filter((r,a)=>a===0||r.route.path&&r.route.path.length>0)}function sp(t){let r=m3(t);return r.map((a,l)=>l===r.length-1?a.pathname:a.pathnameBase)}function Qc(t,r,a,l=!1){let s;typeof t=="string"?s=ol(t):(s={...t},et(!s.pathname||!s.pathname.includes("?"),rm("?","pathname","search",s)),et(!s.pathname||!s.pathname.includes("#"),rm("#","pathname","hash",s)),et(!s.search||!s.search.includes("#"),rm("#","search","hash",s)));let c=t===""||s.pathname==="",f=c?"/":s.pathname,m;if(f==null)m=a;else{let v=r.length-1;if(!l&&f.startsWith("..")){let x=f.split("/");for(;x[0]==="..";)x.shift(),v-=1;s.pathname=x.join("/")}m=v>=0?r[v]:"/"}let g=f3(s,m),h=f&&f!=="/"&&f.endsWith("/"),b=(c||f===".")&&a.endsWith("/");return!g.pathname.endsWith("/")&&(h||b)&&(g.pathname+="/"),g}var Mb=t=>t.replace(/\/\/+/g,"/"),Vn=t=>Mb(t.join("/")),Lc=t=>t.replace(/\/+$/,""),p3=t=>Lc(t).replace(/^\/*/,"/"),h3=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,g3=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t,v3=class{constructor(t,r,a,l=!1){this.status=t,this.statusText=r||"",this.internal=l,a instanceof Error?(this.data=a.toString(),this.error=a):this.data=a}};function y3(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}function b3(t){let r=t.map(a=>a.route.path).filter(Boolean);return Vn(r)||"/"}var kb=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Ob(t,r){let a=t;if(typeof a!="string"||!d3.test(a))return{absoluteURL:void 0,isExternal:!1,to:a};let l=a,s=!1;if(kb)try{let c=new URL(window.location.href),f=a.startsWith("//")?new URL(c.protocol+a):new URL(a),m=Tr(f.pathname,r);f.origin===c.origin&&m!=null?a=m+f.search+f.hash:s=!0}catch{kn(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:l,isExternal:s,to:a}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Db=["POST","PUT","PATCH","DELETE"];new Set(Db);var x3=["GET",...Db];new Set(x3);var ll=d.createContext(null);ll.displayName="DataRouter";var Jc=d.createContext(null);Jc.displayName="DataRouterState";var Lb=d.createContext(!1);function S3(){return d.useContext(Lb)}var zb=d.createContext({isTransitioning:!1});zb.displayName="ViewTransition";var C3=d.createContext(new Map);C3.displayName="Fetchers";var w3=d.createContext(null);w3.displayName="Await";var hn=d.createContext(null);hn.displayName="Navigation";var zs=d.createContext(null);zs.displayName="Location";var Fn=d.createContext({outlet:null,matches:[],isDataRoute:!1});Fn.displayName="Route";var ip=d.createContext(null);ip.displayName="RouteError";var Pb="REACT_ROUTER_ERROR",E3="REDIRECT",N3="ROUTE_ERROR_RESPONSE";function j3(t){if(t.startsWith(`${Pb}:${E3}:{`))try{let r=JSON.parse(t.slice(28));if(typeof r=="object"&&r&&typeof r.status=="number"&&typeof r.statusText=="string"&&typeof r.location=="string"&&typeof r.reloadDocument=="boolean"&&typeof r.replace=="boolean")return r}catch{}}function R3(t){if(t.startsWith(`${Pb}:${N3}:{`))try{let r=JSON.parse(t.slice(40));if(typeof r=="object"&&r&&typeof r.status=="number"&&typeof r.statusText=="string")return new v3(r.status,r.statusText,r.data)}catch{}}function T3(t,{relative:r}={}){et(sl(),"useHref() may be used only in the context of a component.");let{basename:a,navigator:l}=d.useContext(hn),{hash:s,pathname:c,search:f}=Ps(t,{relative:r}),m=c;return a!=="/"&&(m=c==="/"?a:Vn([a,c])),l.createHref({pathname:m,search:f,hash:s})}function sl(){return d.useContext(zs)!=null}function On(){return et(sl(),"useLocation() may be used only in the context of a component."),d.useContext(zs).location}var Bb="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function $b(t){d.useContext(hn).static||d.useLayoutEffect(t)}function il(){let{isDataRoute:t}=d.useContext(Fn);return t?V3():A3()}function A3(){et(sl(),"useNavigate() may be used only in the context of a component.");let t=d.useContext(ll),{basename:r,navigator:a}=d.useContext(hn),{matches:l}=d.useContext(Fn),{pathname:s}=On(),c=JSON.stringify(sp(l)),f=d.useRef(!1);return $b(()=>{f.current=!0}),d.useCallback((g,h={})=>{if(kn(f.current,Bb),!f.current)return;if(typeof g=="number"){a.go(g);return}let b=Qc(g,JSON.parse(c),s,h.relative==="path");t==null&&r!=="/"&&(b.pathname=b.pathname==="/"?r:Vn([r,b.pathname])),(h.replace?a.replace:a.push)(b,h.state,h)},[r,a,c,s,t])}var _3=d.createContext(null);function M3(t){let r=d.useContext(Fn).outlet;return d.useMemo(()=>r&&d.createElement(_3.Provider,{value:t},r),[r,t])}function Ps(t,{relative:r}={}){let{matches:a}=d.useContext(Fn),{pathname:l}=On(),s=JSON.stringify(sp(a));return d.useMemo(()=>Qc(t,JSON.parse(s),l,r==="path"),[t,s,l,r])}function k3(t,r){return Ub(t,r)}function Ub(t,r,a){var j;et(sl(),"useRoutes() may be used only in the context of a component.");let{navigator:l}=d.useContext(hn),{matches:s}=d.useContext(Fn),c=s[s.length-1],f=c?c.params:{},m=c?c.pathname:"/",g=c?c.pathnameBase:"/",h=c&&c.route;{let N=h&&h.path||"";Ib(m,!h||N.endsWith("*")||N.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${m}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let b=On(),v;if(r){let N=typeof r=="string"?ol(r):r;et(g==="/"||((j=N.pathname)==null?void 0:j.startsWith(g)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${g}" but pathname "${N.pathname}" was given in the \`location\` prop.`),v=N}else v=b;let x=v.pathname||"/",C=x;if(g!=="/"){let N=g.replace(/^\//,"").split("/");C="/"+x.replace(/^\//,"").split("/").slice(N.length).join("/")}let E=a&&a.state.matches.length?a.state.matches.map(N=>Object.assign(N,{route:a.manifest[N.route.id]||N.route})):Tb(t,{pathname:C});kn(h||E!=null,`No routes matched location "${v.pathname}${v.search}${v.hash}" `),kn(E==null||E[E.length-1].route.element!==void 0||E[E.length-1].route.Component!==void 0||E[E.length-1].route.lazy!==void 0,`Matched leaf route at location "${v.pathname}${v.search}${v.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let w=P3(E&&E.map(N=>Object.assign({},N,{params:Object.assign({},f,N.params),pathname:Vn([g,l.encodeLocation?l.encodeLocation(N.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:N.pathname]),pathnameBase:N.pathnameBase==="/"?g:Vn([g,l.encodeLocation?l.encodeLocation(N.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:N.pathnameBase])})),s,a);return r&&w?d.createElement(zs.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...v},navigationType:"POP"}},w):w}function O3(){let t=I3(),r=y3(t)?`${t.status} ${t.statusText}`:t instanceof Error?t.message:JSON.stringify(t),a=t instanceof Error?t.stack:null,l="rgba(200,200,200, 0.5)",s={padding:"0.5rem",backgroundColor:l},c={padding:"2px 4px",backgroundColor:l},f=null;return console.error("Error handled by React Router default ErrorBoundary:",t),f=d.createElement(d.Fragment,null,d.createElement("p",null,"💿 Hey developer 👋"),d.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",d.createElement("code",{style:c},"ErrorBoundary")," or"," ",d.createElement("code",{style:c},"errorElement")," prop on your route.")),d.createElement(d.Fragment,null,d.createElement("h2",null,"Unexpected Application Error!"),d.createElement("h3",{style:{fontStyle:"italic"}},r),a?d.createElement("pre",{style:s},a):null,f)}var D3=d.createElement(O3,null),Hb=class extends d.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,r){return r.location!==t.location||r.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:r.error,location:r.location,revalidation:t.revalidation||r.revalidation}}componentDidCatch(t,r){this.props.onError?this.props.onError(t,r):console.error("React Router caught the following error during render",t)}render(){let t=this.state.error;if(this.context&&typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){const a=R3(t.digest);a&&(t=a)}let r=t!==void 0?d.createElement(Fn.Provider,{value:this.props.routeContext},d.createElement(ip.Provider,{value:t,children:this.props.component})):this.props.children;return this.context?d.createElement(L3,{error:t},r):r}};Hb.contextType=Lb;var am=new WeakMap;function L3({children:t,error:r}){let{basename:a}=d.useContext(hn);if(typeof r=="object"&&r&&"digest"in r&&typeof r.digest=="string"){let l=j3(r.digest);if(l){let s=am.get(r);if(s)throw s;let c=Ob(l.location,a);if(kb&&!am.get(r))if(c.isExternal||l.reloadDocument)window.location.href=c.absoluteURL||c.to;else{const f=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(c.to,{replace:l.replace}));throw am.set(r,f),f}return d.createElement("meta",{httpEquiv:"refresh",content:`0;url=${c.absoluteURL||c.to}`})}}return t}function z3({routeContext:t,match:r,children:a}){let l=d.useContext(ll);return l&&l.static&&l.staticContext&&(r.route.errorElement||r.route.ErrorBoundary)&&(l.staticContext._deepestRenderedBoundaryId=r.route.id),d.createElement(Fn.Provider,{value:t},a)}function P3(t,r=[],a){let l=a==null?void 0:a.state;if(t==null){if(!l)return null;if(l.errors)t=l.matches;else if(r.length===0&&!l.initialized&&l.matches.length>0)t=l.matches;else return null}let s=t,c=l==null?void 0:l.errors;if(c!=null){let b=s.findIndex(v=>v.route.id&&(c==null?void 0:c[v.route.id])!==void 0);et(b>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(c).join(",")}`),s=s.slice(0,Math.min(s.length,b+1))}let f=!1,m=-1;if(a&&l){f=l.renderFallback;for(let b=0;b=0?s=s.slice(0,m+1):s=[s[0]];break}}}}let g=a==null?void 0:a.onError,h=l&&g?(b,v)=>{var x,C;g(b,{location:l.location,params:((C=(x=l.matches)==null?void 0:x[0])==null?void 0:C.params)??{},pattern:b3(l.matches),errorInfo:v})}:void 0;return s.reduceRight((b,v,x)=>{let C,E=!1,w=null,j=null;l&&(C=c&&v.route.id?c[v.route.id]:void 0,w=v.route.errorElement||D3,f&&(m<0&&x===0?(Ib("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),E=!0,j=null):m===x&&(E=!0,j=v.route.hydrateFallbackElement||null)));let N=r.concat(s.slice(0,x+1)),_=()=>{let A;return C?A=w:E?A=j:v.route.Component?A=d.createElement(v.route.Component,null):v.route.element?A=v.route.element:A=b,d.createElement(z3,{match:v,routeContext:{outlet:b,matches:N,isDataRoute:l!=null},children:A})};return l&&(v.route.ErrorBoundary||v.route.errorElement||x===0)?d.createElement(Hb,{location:l.location,revalidation:l.revalidation,component:w,error:C,children:_(),routeContext:{outlet:null,matches:N,isDataRoute:!0},onError:h}):_()},null)}function cp(t){return`${t} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function B3(t){let r=d.useContext(ll);return et(r,cp(t)),r}function $3(t){let r=d.useContext(Jc);return et(r,cp(t)),r}function U3(t){let r=d.useContext(Fn);return et(r,cp(t)),r}function up(t){let r=U3(t),a=r.matches[r.matches.length-1];return et(a.route.id,`${t} can only be used on routes that contain a unique "id"`),a.route.id}function H3(){return up("useRouteId")}function I3(){var l;let t=d.useContext(ip),r=$3("useRouteError"),a=up("useRouteError");return t!==void 0?t:(l=r.errors)==null?void 0:l[a]}function V3(){let{router:t}=B3("useNavigate"),r=up("useNavigate"),a=d.useRef(!1);return $b(()=>{a.current=!0}),d.useCallback(async(s,c={})=>{kn(a.current,Bb),a.current&&(typeof s=="number"?await t.navigate(s):await t.navigate(s,{fromRouteId:r,...c}))},[t,r])}var x1={};function Ib(t,r,a){!r&&!x1[t]&&(x1[t]=!0,kn(!1,a))}d.memo(G3);function G3({routes:t,manifest:r,future:a,state:l,isStatic:s,onError:c}){return Ub(t,void 0,{manifest:r,state:l,isStatic:s,onError:c})}function Ia({to:t,replace:r,state:a,relative:l}){et(sl()," may be used only in the context of a component.");let{static:s}=d.useContext(hn);kn(!s," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:c}=d.useContext(Fn),{pathname:f}=On(),m=il(),g=Qc(t,sp(c),f,l==="path"),h=JSON.stringify(g);return d.useEffect(()=>{m(JSON.parse(h),{replace:r,state:a,relative:l})},[m,h,l,r,a]),null}function Y3(t){return M3(t.context)}function Hn(t){et(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function F3({basename:t="/",children:r=null,location:a,navigationType:l="POP",navigator:s,static:c=!1,useTransitions:f}){et(!sl(),"You cannot render a inside another . You should never have more than one in your app.");let m=t.replace(/^\/*/,"/"),g=d.useMemo(()=>({basename:m,navigator:s,static:c,useTransitions:f,future:{}}),[m,s,c,f]);typeof a=="string"&&(a=ol(a));let{pathname:h="/",search:b="",hash:v="",state:x=null,key:C="default",mask:E}=a,w=d.useMemo(()=>{let j=Tr(h,m);return j==null?null:{location:{pathname:j,search:b,hash:v,state:x,key:C,mask:E},navigationType:l}},[m,h,b,v,x,C,l,E]);return kn(w!=null,` is not able to match the URL "${h}${b}${v}" because it does not start with the basename, so the won't render anything.`),w==null?null:d.createElement(hn.Provider,{value:g},d.createElement(zs.Provider,{children:r,value:w}))}function W3({children:t,location:r}){return k3(Rm(t),r)}function Rm(t,r=[]){let a=[];return d.Children.forEach(t,(l,s)=>{if(!d.isValidElement(l))return;let c=[...r,s];if(l.type===d.Fragment){a.push.apply(a,Rm(l.props.children,c));return}et(l.type===Hn,`[${typeof l.type=="string"?l.type:l.type.name}] is not a component. All component children of must be a or `),et(!l.props.index||!l.props.children,"An index route cannot have child routes.");let f={id:l.props.id||c.join("-"),caseSensitive:l.props.caseSensitive,element:l.props.element,Component:l.props.Component,index:l.props.index,path:l.props.path,middleware:l.props.middleware,loader:l.props.loader,action:l.props.action,hydrateFallbackElement:l.props.hydrateFallbackElement,HydrateFallback:l.props.HydrateFallback,errorElement:l.props.errorElement,ErrorBoundary:l.props.ErrorBoundary,hasErrorBoundary:l.props.hasErrorBoundary===!0||l.props.ErrorBoundary!=null||l.props.errorElement!=null,shouldRevalidate:l.props.shouldRevalidate,handle:l.props.handle,lazy:l.props.lazy};l.props.children&&(f.children=Rm(l.props.children,c)),a.push(f)}),a}var Rc="get",Tc="application/x-www-form-urlencoded";function eu(t){return typeof HTMLElement<"u"&&t instanceof HTMLElement}function q3(t){return eu(t)&&t.tagName.toLowerCase()==="button"}function X3(t){return eu(t)&&t.tagName.toLowerCase()==="form"}function K3(t){return eu(t)&&t.tagName.toLowerCase()==="input"}function Z3(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function Q3(t,r){return t.button===0&&(!r||r==="_self")&&!Z3(t)}function Tm(t=""){return new URLSearchParams(typeof t=="string"||Array.isArray(t)||t instanceof URLSearchParams?t:Object.keys(t).reduce((r,a)=>{let l=t[a];return r.concat(Array.isArray(l)?l.map(s=>[a,s]):[[a,l]])},[]))}function J3(t,r){let a=Tm(t);return r&&r.forEach((l,s)=>{a.has(s)||r.getAll(s).forEach(c=>{a.append(s,c)})}),a}var gc=null;function e4(){if(gc===null)try{new FormData(document.createElement("form"),0),gc=!1}catch{gc=!0}return gc}var t4=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function om(t){return t!=null&&!t4.has(t)?(kn(!1,`"${t}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Tc}"`),null):t}function n4(t,r){let a,l,s,c,f;if(X3(t)){let m=t.getAttribute("action");l=m?Tr(m,r):null,a=t.getAttribute("method")||Rc,s=om(t.getAttribute("enctype"))||Tc,c=new FormData(t)}else if(q3(t)||K3(t)&&(t.type==="submit"||t.type==="image")){let m=t.form;if(m==null)throw new Error('Cannot submit a